Merge commit 'mingw/master' into work/merge-mingw

Conflicts:

	Makefile
	help.c
	  moved msysgit specific code to show_html_page
	  and call show_html_page per default.
This commit is contained in:
Steffen Prohaska
2007-12-13 22:38:05 +01:00
86 changed files with 2498 additions and 753 deletions

1
.gitignore vendored
View File

@@ -12,6 +12,7 @@ git-archive
git-bisect
git-blame
git-branch
git-browse-help
git-bundle
git-cat-file
git-check-attr

View File

@@ -5,7 +5,11 @@ MAN1_TXT= \
MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt
MAN7_TXT=git.txt
DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT))
MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
MAN_HTML=$(patsubst %.txt,%.html,$(MAN_TXT))
DOC_HTML=$(MAN_HTML)
ARTICLES = tutorial
ARTICLES += tutorial-2
@@ -29,6 +33,7 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
prefix?=$(HOME)
bindir?=$(prefix)/bin
htmldir?=$(prefix)/share/doc/git-doc
mandir?=$(prefix)/share/man
man1dir=$(mandir)/man1
man5dir=$(mandir)/man5
@@ -79,7 +84,7 @@ man1: $(DOC_MAN1)
man5: $(DOC_MAN5)
man7: $(DOC_MAN7)
info: git.info
info: git.info gitman.info
install: man
$(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
@@ -91,13 +96,17 @@ install: man
install-info: info
$(INSTALL) -d -m 755 $(DESTDIR)$(infodir)
$(INSTALL) -m 644 git.info $(DESTDIR)$(infodir)
$(INSTALL) -m 644 git.info gitman.info $(DESTDIR)$(infodir)
if test -r $(DESTDIR)$(infodir)/dir; then \
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) gitman.info ;\
else \
echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
fi
install-html: html
sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
$(MAKE) -C ../ GIT-VERSION-FILE
@@ -161,12 +170,27 @@ XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
git.info: user-manual.xml
$(RM) $@ $*.texi $*.texi+
$(DOCBOOK2X_TEXI) user-manual.xml --to-stdout >$*.texi+
$(PERL_PATH) fix-texi.perl <$*.texi+ >$*.texi
git.info: user-manual.texi
$(MAKEINFO) --no-split -o $@ user-manual.texi
user-manual.texi: user-manual.xml
$(RM) $@+ $@
$(DOCBOOK2X_TEXI) user-manual.xml --to-stdout | $(PERL_PATH) fix-texi.perl >$@+
mv $@+ $@
gitman.texi: $(MAN_XML) cat-texi.perl
$(RM) $@+ $@
($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --to-stdout $(xml);)) | \
$(PERL_PATH) cat-texi.perl $@ >$@+
mv $@+ $@
gitman.info: gitman.texi
$(MAKEINFO) --no-split $*.texi
$(RM) $*.texi $*.texi+
$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
$(RM) $@+ $@
$(DOCBOOK2X_TEXI) --to-stdout $*.xml >$@+
mv $@+ $@
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
$(RM) $@+ $@

View File

@@ -132,6 +132,10 @@ Updates since v1.5.3
variable used to mean "do not require -f option to lose untracked
files", but we now use the safer default).
* The kinds of whitespace errors "git diff" and "git apply" notice (and
fix) can be controlled via 'core.whitespace' configuration variable
and 'whitespace' attribute in .gitattributes file.
* "git push" learned --dry-run option to show what would happen if a
push is run.
@@ -200,6 +204,12 @@ Updates since v1.5.3
* "git bisect" learned "skip" action to mark untestable commits.
* "git bisect visualize" learned a shorter synonym "git bisect view".
* "git bisect visualize" runs "git log" in a non-windowed
environments. It also can be told what command to run (e.g. "git
bisect visualize tig").
* "git format-patch" learned "format.numbered" configuration variable
to automatically turn --numbered option on when more than one commits
are formatted.
@@ -220,6 +230,9 @@ Updates since v1.5.3
* "git checkout" from a subdirectory learned to use "../path" to allow
checking out a path outside the current directory without cd'ing up.
* "git checkout" from and to detached HEAD leaves a bit more
information in the reflog.
* "git send-email --dry-run" shows full headers for easier diagnosis.
* "git merge-ours" is now built-in.
@@ -233,11 +246,26 @@ Updates since v1.5.3
descriptive name from From: and Signed-off-by: lines in the commit
message.
* "git status" from a subdirectory now shows relative paths which makes
copy-and-pasting for git-checkout/git-add/git-rm easier.
* "git svn" wasted way too much disk to record revision mappings
between svn and git; a new representation that is much more compact
for this information has been introduced to correct this.
* "git checkout" from and to detached HEAD leaves a bit more
information in the reflog.
* "git status" from a subdirectory now shows relative paths, which
makes copy-and-pasting for git-checkout/git-add/git-rm easier. The
traditional behaviour to show the full path relative to the top of
the work tree can be had by setting status.relativepaths
configuration variable to true.
* "git blame" kept text for each annotated revision in core needlessly;
this has been corrected.
* "git shortlog" learned to default to HEAD when the standard input is
a terminal and the user did not give any revision parameter.
* "git shortlog" learned "-e" option to show e-mail addresses as well as
authors' names.
* "git help" learned "-w" option to show documentation in browsers.
* In addition there are quite a few internal clean-ups. Notably
@@ -266,6 +294,6 @@ series.
--
exec >/var/tmp/1
O=v1.5.3.7-1111-gd9f4059
O=v1.5.4-rc0
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

38
Documentation/cat-texi.perl Executable file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/perl -w
my @menu = ();
my $output = $ARGV[0];
open TMP, '>', "$output.tmp";
while (<STDIN>) {
next if (/^\\input texinfo/../\@node Top/);
next if (/^\@bye/ || /^\.ft/);
if (s/^\@top (.*)/\@node $1,,,Top/) {
push @menu, $1;
}
s/\(\@pxref{\[URLS\]}\)//;
print TMP;
}
close TMP;
printf '\input texinfo
@setfilename gitman.info
@documentencoding us-ascii
@node Top,,%s
@top Git Manual Pages
@documentlanguage en
@menu
', $menu[0];
for (@menu) {
print "* ${_}::\n";
}
print "\@end menu\n";
open TMP, '<', "$output.tmp";
while (<TMP>) {
print;
}
close TMP;
print "\@bye\n";
unlink "$output.tmp";

View File

@@ -295,6 +295,20 @@ core.pager::
The command that git will use to paginate output. Can be overridden
with the `GIT_PAGER` environment variable.
core.whitespace::
A comma separated list of common whitespace problems to
notice. `git diff` will use `color.diff.whitespace` to
highlight them, and `git apply --whitespace=error` will
consider them as errors:
+
* `trailing-space` treats trailing whitespaces at the end of the line
as an error (enabled by default).
* `space-before-tab` treats a space character that appears immediately
before a tab character in the initial indent part of the line as an
error (enabled by default).
* `indent-with-non-tab` treats a line that is indented with 8 or more
space characters that can be replaced with tab characters.
alias.*::
Command aliases for the gitlink:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation
@@ -387,8 +401,8 @@ color.diff.<slot>::
which part of the patch to use the specified color, and is one
of `plain` (context text), `meta` (metainformation), `frag`
(hunk header), `old` (removed lines), `new` (added lines),
`commit` (commit headers), or `whitespace` (highlighting dubious
whitespace). The values of these variables may be specified as
`commit` (commit headers), or `whitespace` (highlighting
whitespace errors). The values of these variables may be specified as
in color.branch.<slot>.
color.interactive::
@@ -762,6 +776,12 @@ showbranch.default::
The default set of branches for gitlink:git-show-branch[1].
See gitlink:git-show-branch[1].
status.relativePaths::
By default, gitlink:git-status[1] shows paths relative to the
current directory. Setting this variable to `false` shows paths
relative to the repository root (this was the default for git
prior to v1.5.4).
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the

View File

@@ -84,3 +84,64 @@ all parents.
include::diff-generate-patch.txt[]
other diff formats
------------------
The `--summary` option describes newly added, deleted, renamed and
copied files. The `--stat` option adds diffstat(1) graph to the
output. These options can be combined with other options, such as
`-p`, and are meant for human consumption.
When showing a change that involves a rename or a copy, `--stat` output
formats the pathnames compactly by combining common prefix and suffix of
the pathnames. For example, a change that moves `arch/i386/Makefile` to
`arch/x86/Makefile` while modifying 4 lines will be shown like this:
------------------------------------
arch/{i386 => x86}/Makefile | 4 +--
------------------------------------
The `--numstat` option gives the diffstat(1) information but is designed
for easier machine consumption. An entry in `--numstat` output looks
like this:
----------------------------------------
1 2 README
3 1 arch/{i386 => x86}/Makefile
----------------------------------------
That is, from left to right:
. the number of added lines;
. a tab;
. the number of deleted lines;
. a tab;
. pathname (possibly with rename/copy information);
. a newline.
When `-z` output option is in effect, the output is formatted this way:
----------------------------------------
1 2 README NUL
3 1 NUL arch/i386/Makefile NUL arch/x86/Makefile NUL
----------------------------------------
That is:
. the number of added lines;
. a tab;
. the number of deleted lines;
. a tab;
. a NUL (only exists if renamed/copied);
. pathname in preimage;
. a NUL (only exists if renamed/copied);
. pathname in postimage (only exists if renamed/copied);
. a NUL.
The extra `NUL` before the preimage path in renamed case is to allow
scripts that read the output to tell if the current record being read is
a single-path record or a rename/copy record without reading ahead.
After reading added and deleted lines, reading up to `NUL` would yield
the pathname, but if that is `NUL`, the record will show two paths.

View File

@@ -175,19 +175,19 @@ endif::git-format-patch[]
Shorthand for "--text".
--ignore-space-at-eol::
Ignore changes in white spaces at EOL.
Ignore changes in whitespace at EOL.
--ignore-space-change::
Ignore changes in amount of white space. This ignores white
space at line end, and consider all other sequences of one or
more white space characters to be equivalent.
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".
--ignore-all-space::
Ignore white space when comparing lines. This ignores
difference even if one line has white space where the other
Ignore whitespace when comparing lines. This ignores
differences even if one line has whitespace where the other
line has none.
-w::

View File

@@ -65,7 +65,7 @@ OPTIONS
operation to a subset of the working tree. See ``Interactive
mode'' for details.
-p, \--patch:
-p, \--patch::
Similar to Interactive mode but the initial command loop is
bypassed and the 'patch' subcommand is invoked using each of
the specified filepatterns before exiting.

View File

@@ -13,7 +13,7 @@ SYNOPSIS
[--apply] [--no-add] [--build-fake-ancestor <file>] [-R | --reverse]
[--allow-binary-replacement | --binary] [--reject] [-z]
[-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
[--whitespace=<nowarn|warn|error|error-all|strip>]
[--whitespace=<nowarn|warn|fix|error|error-all>]
[--exclude=PATH] [--verbose] [<patch>...]
DESCRIPTION
@@ -119,7 +119,7 @@ discouraged.
--no-add::
When applying a patch, ignore additions made by the
patch. This can be used to extract common part between
patch. This can be used to extract the common part between
two files by first running `diff` on them and applying
the result with this option, which would apply the
deletion part but not addition part.
@@ -135,25 +135,32 @@ discouraged.
be useful when importing patchsets, where you want to exclude certain
files or directories.
--whitespace=<option>::
When applying a patch, detect a new or modified line
that ends with trailing whitespaces (this includes a
line that solely consists of whitespaces). By default,
the command outputs warning messages and applies the
patch.
When gitlink:git-apply[1] is used for statistics and not applying a
patch, it defaults to `nowarn`.
You can use different `<option>` to control this
behavior:
--whitespace=<action>::
When applying a patch, detect a new or modified line that has
whitespace errors. What are considered whitespace errors is
controlled by `core.whitespace` configuration. By default,
trailing whitespaces (including lines that solely consist of
whitespaces) and a space character that is immediately followed
by a tab character inside the initial indent of the line are
considered whitespace errors.
+
By default, the command outputs warning messages but applies the patch.
When gitlink:git-apply[1] is used for statistics and not applying a
patch, it defaults to `nowarn`.
+
You can use different `<action>` to control this
behavior:
+
* `nowarn` turns off the trailing whitespace warning.
* `warn` outputs warnings for a few such errors, but applies the
patch (default).
patch as-is (default).
* `fix` outputs warnings for a few such errors, and applies the
patch after fixing them (`strip` is a synonym --- the tool
used to consider only trailing whitespaces as errors, and the
fix involved 'stripping' them, but modern gits do more).
* `error` outputs warnings for a few such errors, and refuses
to apply the patch.
* `error-all` is similar to `error` but shows all errors.
* `strip` outputs warnings for a few such errors, strips out the
trailing whitespaces and applies the patch.
--inaccurate-eof::
Under certain circumstances, some versions of diff do not correctly

View File

@@ -92,7 +92,16 @@ During the bisection process, you can say
$ git bisect visualize
------------
to see the currently remaining suspects in `gitk`.
to see the currently remaining suspects in `gitk`. `visualize` is a bit
too long to type and `view` is provided as a synonym.
If `DISPLAY` environment variable is not set, `git log` is used
instead. You can even give command line options such as `-p` and
`--stat`.
------------
$ git bisect view --stat
------------
Bisect log and bisect replay
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -86,7 +86,7 @@ OPTIONS
Add Signed-off-by line at the end of the commit message.
--no-verify::
This option bypasses the pre-commit hook.
This option bypasses the pre-commit and commit-msg hooks.
See also link:hooks.html[hooks].
--allow-empty::

View File

@@ -7,7 +7,7 @@ git-help - display help information about git
SYNOPSIS
--------
'git help' [-a|--all] [COMMAND]
'git help' [-a|--all|-i|--info|-w|--web] [COMMAND]
DESCRIPTION
-----------
@@ -20,7 +20,8 @@ If the option '--all' or '-a' is given, then all available commands are
printed on the standard output.
If a git command is named, a manual page for that command is brought
up. The 'man' program is used by default for this purpose.
up. The 'man' program is used by default for this purpose, but this
can be overriden by other options.
Note that 'git --help ...' is identical as 'git help ...' because the
former is internally converted into the latter.
@@ -28,10 +29,31 @@ former is internally converted into the latter.
OPTIONS
-------
-a|--all::
Prints all the available commands on the standard output. This
option superseeds any other option.
-i|--info::
Use the 'info' program to display the manual page, instead of
the 'man' program that is used by default.
-w|--web::
Use a web browser to display the HTML manual page, instead of
the 'man' program that is used by default.
+
The web browser can be specified using the configuration variable
'help.browser', or 'web.browser' if the former is not set. If none of
these config variables is set, the 'git-browse-help' script (called by
'git-help') will pick a suitable default.
+
You can explicitly provide a full path to your prefered browser by
setting the configuration variable 'browser.<tool>.path'. For example,
you can configure the absolute path to firefox by setting
'browser.firefox.path'. Otherwise, 'git-browse-help' assumes the tool
is available in PATH.
+
Note that the script tries, as much as possible, to display the HTML
page in a new tab on an already opened browser.
Author
------
Written by Junio C Hamano <gitster@pobox.com> and the git-list

View File

@@ -71,6 +71,9 @@ You may specify configuration in your .git/config
-----------------------------------------------------------------------
If the configuration variable 'instaweb.browser' is not set,
'web.browser' will be used instead if it is defined.
Author
------
Written by Eric Wong <normalperson@yhbt.net>

View File

@@ -8,8 +8,8 @@ git-shortlog - Summarize 'git log' output
SYNOPSIS
--------
[verse]
git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
git-shortlog [-n|--numbered] [-s|--summary] [<committish>...]
git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] [-e]
git-shortlog [-n|--numbered] [-s|--summary] [-e|--email] [<committish>...]
DESCRIPTION
-----------
@@ -32,6 +32,9 @@ OPTIONS
-s, \--summary::
Suppress commit description and provide a commit count summary only.
-e, \--email::
Show the email address of each author.
FILES
-----

View File

@@ -12,37 +12,32 @@ SYNOPSIS
DESCRIPTION
-----------
Examines paths in the working tree that has changes unrecorded
to the index file, and changes between the index file and the
current HEAD commit. The former paths are what you _could_
commit by running 'git add' before running 'git
commit', and the latter paths are what you _would_ commit by
running 'git commit'.
If there is no path that is different between the index file and
the current HEAD commit, the command exits with non-zero
status.
Displays paths that have differences between the index file and the
current HEAD commit, paths that have differences between the working
tree and the index file, and paths in the working tree that are not
tracked by git (and are not ignored by gitlink:gitignore[5]). The first
are what you _would_ commit by running `git commit`; the second and
third are what you _could_ commit by running `git add` before running
`git commit`.
The command takes the same set of options as `git-commit`; it
shows what would be committed if the same options are given to
`git-commit`.
If any paths have been touched in the working tree (that is,
their modification times have changed) but their contents and
permissions are identical to those in the index file, the command
updates the index file. Running `git-status` can thus speed up
subsequent operations such as `git-diff` if the working tree
contains many paths that have been touched but not modified.
If there is no path that is different between the index file and
the current HEAD commit (i.e., there is nothing to commit by running
`git-commit`), the command exits with non-zero status.
OUTPUT
------
The output from this command is designed to be used as a commit
template comments, and all the output lines are prefixed with '#'.
template comment, and all the output lines are prefixed with '#'.
The paths mentioned in the output, unlike many other git commands, are
made relative to the current directory, if you are working in a
subdirectory (this is on purpose, to help cutting and pasting).
made relative to the current directory if you are working in a
subdirectory (this is on purpose, to help cutting and pasting). See
the status.relativePaths config option below.
CONFIGURATION
@@ -53,6 +48,10 @@ mean the same thing and the latter is kept for backward
compatibility) and `color.status.<slot>` configuration variables
to colorize its output.
If the config variable `status.relativePaths` is set to false, then all
paths shown are relative to the repository root, not to the current
directory.
See Also
--------
gitlink:gitignore[5]

View File

@@ -104,6 +104,11 @@ OPTIONS
commands. If the option '--all' or '-a' is given then all
available commands are printed. If a git command is named this
option will bring up the manual page for that command.
+
Other options are available to control how the manual page is
displayed. See gitlink:git-help[1] for more information,
because 'git --help ...' is converted internally into 'git
help ...'.
--exec-path::
Path to wherever your core git programs are installed.

View File

@@ -361,6 +361,37 @@ When left unspecified, the driver itself is used for both
internal merge and the final merge.
Checking whitespace errors
~~~~~~~~~~~~~~~~~~~~~~~~~~
`whitespace`
^^^^^^^^^^^^
The `core.whitespace` configuration variable allows you to define what
`diff` and `apply` should consider whitespace errors for all paths in
the project (See gitlink:git-config[1]). This attribute gives you finer
control per path.
Set::
Notice all types of potential whitespace errors known to git.
Unset::
Do not notice anything as error.
Unspecified::
Use the value of `core.whitespace` configuration variable to
decide what to notice as error.
String::
Specify a comma separate list of common whitespace problems to
notice in the same format as `core.whitespace` configuration
variable.
EXAMPLE
-------

View File

@@ -244,8 +244,7 @@ This commit is referred to as a "merge commit", or sometimes just a
The unique identifier of an <<def_object,object>>. The <<def_hash,hash>>
of the object's contents using the Secure Hash Algorithm
1 and usually represented by the 40 character hexadecimal encoding of
the <<def_hash,hash>> of the object (possibly followed by
a white space).
the <<def_hash,hash>> of the object.
[[def_object_type]]object type::
One of the identifiers

View File

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

View File

@@ -157,9 +157,12 @@ STRIP ?= strip
prefix =
bindir = $(prefix)/bin
mandir = $(prefix)/share/man
infodir = $(prefix)/share/info
gitexecdir = $(bindir)
sharedir = $(prefix)/share
template_dir = $(sharedir)/git-core/templates
htmldir=$(sharedir)/doc/git-doc
ifeq ($(prefix),/usr)
sysconfdir = /etc
else
@@ -186,7 +189,7 @@ GITWEB_FAVICON = git-favicon.png
GITWEB_SITE_HEADER =
GITWEB_SITE_FOOTER =
export prefix bindir gitexecdir sharedir template_dir sysconfdir
export prefix bindir gitexecdir sharedir template_dir htmldir sysconfdir
CC = gcc
AR = ar
@@ -226,7 +229,8 @@ SCRIPT_SH = \
git-merge-resolve.sh \
git-lost-found.sh git-quiltimport.sh git-submodule.sh \
git-filter-branch.sh \
git-stash.sh
git-stash.sh \
git-browse-help.sh
SCRIPT_PERL = \
git-add--interactive.perl \
@@ -314,7 +318,7 @@ LIB_OBJS = \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
transport.o bundle.o walker.o parse-options.o
transport.o bundle.o walker.o parse-options.o ws.o
BUILTIN_OBJS = \
builtin-add.o \
@@ -787,8 +791,11 @@ ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
bindir_SQ = $(subst ','\'',$(bindir))
mandir_SQ = $(subst ','\'',$(mandir))
infodir_SQ = $(subst ','\'',$(infodir))
gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
template_dir_SQ = $(subst ','\'',$(template_dir))
htmldir_SQ = $(subst ','\'',$(htmldir))
prefix_SQ = $(subst ','\'',$(prefix))
sysconfdir_SQ = $(subst ','\'',$(sysconfdir))
@@ -834,7 +841,10 @@ git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
help.o: common-cmds.h
help.o: help.c common-cmds.h GIT-CFLAGS
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
'-DGIT_MAN_PATH="$(mandir_SQ)"' \
'-DGIT_INFO_PATH="$(infodir_SQ)"' $<
git-merge-subtree$X: git-merge-recursive$X
$(QUIET_BUILT_IN)$(RM) $@ && ln git-merge-recursive$X $@
@@ -853,6 +863,7 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
-e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e 's|@@HTMLDIR@@|$(htmldir_SQ)|g' \
-e 's/@@NO_HARDLINKS@@/$(NO_HARDLINKS)/g' \
$@.sh >$@+ && \
chmod +x $@+ && \
@@ -1068,7 +1079,7 @@ install: all
GIT_CONFIG='$(DESTDIR_SQ)$(sysconfdir_SQ)/gitconfig' \
$(DESTDIR_SQ)$(bindir_SQ)/git$X config core.symlinks false
$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
$(MAKE) -C perl prefix='$(prefix_SQ)' install
$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
ifndef NO_TCLTK
$(MAKE) -C gitk-git install
$(MAKE) -C git-gui install

View File

@@ -45,14 +45,14 @@ 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|error|error-all|strip>] <patch>...";
"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 enum whitespace_eol {
nowarn_whitespace,
warn_on_whitespace,
error_on_whitespace,
strip_whitespace,
} new_whitespace = warn_on_whitespace;
static enum ws_error_action {
nowarn_ws_error,
warn_on_ws_error,
die_on_ws_error,
correct_ws_error,
} ws_error_action = warn_on_ws_error;
static int whitespace_error;
static int squelch_whitespace_errors = 5;
static int applied_after_fixing_ws;
@@ -61,28 +61,28 @@ static const char *patch_input_file;
static void parse_whitespace_option(const char *option)
{
if (!option) {
new_whitespace = warn_on_whitespace;
ws_error_action = warn_on_ws_error;
return;
}
if (!strcmp(option, "warn")) {
new_whitespace = warn_on_whitespace;
ws_error_action = warn_on_ws_error;
return;
}
if (!strcmp(option, "nowarn")) {
new_whitespace = nowarn_whitespace;
ws_error_action = nowarn_ws_error;
return;
}
if (!strcmp(option, "error")) {
new_whitespace = error_on_whitespace;
ws_error_action = die_on_ws_error;
return;
}
if (!strcmp(option, "error-all")) {
new_whitespace = error_on_whitespace;
ws_error_action = die_on_ws_error;
squelch_whitespace_errors = 0;
return;
}
if (!strcmp(option, "strip")) {
new_whitespace = strip_whitespace;
if (!strcmp(option, "strip") || !strcmp(option, "fix")) {
ws_error_action = correct_ws_error;
return;
}
die("unrecognized whitespace option '%s'", option);
@@ -90,11 +90,8 @@ static void parse_whitespace_option(const char *option)
static void set_default_whitespace_mode(const char *whitespace_option)
{
if (!whitespace_option && !apply_default_whitespace) {
new_whitespace = (apply
? warn_on_whitespace
: nowarn_whitespace);
}
if (!whitespace_option && !apply_default_whitespace)
ws_error_action = (apply ? warn_on_ws_error : nowarn_ws_error);
}
/*
@@ -137,11 +134,17 @@ struct fragment {
#define BINARY_DELTA_DEFLATED 1
#define BINARY_LITERAL_DEFLATED 2
/*
* This represents a "patch" to a file, both metainfo changes
* such as creation/deletion, filemode and content changes represented
* as a series of fragments.
*/
struct patch {
char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode;
int is_new, is_delete; /* -1 = unknown, 0 = false, 1 = true */
int rejected;
unsigned ws_rule;
unsigned long deflate_origlen;
int lines_added, lines_deleted;
int score;
@@ -158,7 +161,8 @@ struct patch {
struct patch *next;
};
static void say_patch_name(FILE *output, const char *pre, struct patch *patch, const char *post)
static void say_patch_name(FILE *output, const char *pre,
struct patch *patch, const char *post)
{
fputs(pre, output);
if (patch->old_name && patch->new_name &&
@@ -229,7 +233,8 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
if (*line == '"') {
struct strbuf name;
/* Proposed "new-style" GNU patch/diff format; see
/*
* Proposed "new-style" GNU patch/diff format; see
* http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
*/
strbuf_init(&name, 0);
@@ -499,7 +504,8 @@ static int gitdiff_dissimilarity(const char *line, struct patch *patch)
static int gitdiff_index(const char *line, struct patch *patch)
{
/* index line is N hexadecimal, "..", N hexadecimal,
/*
* index line is N hexadecimal, "..", N hexadecimal,
* and optional space with octal mode.
*/
const char *ptr, *eol;
@@ -550,7 +556,8 @@ static const char *stop_at_slash(const char *line, int llen)
return NULL;
}
/* This is to extract the same name that appears on "diff --git"
/*
* This is to extract the same name that appears on "diff --git"
* line. We do not find and return anything if it is a rename
* patch, and it is OK because we will find the name elsewhere.
* We need to reliably find name only when it is mode-change only,
@@ -584,7 +591,8 @@ static char *git_header_name(char *line, int llen)
goto free_and_fail1;
strbuf_remove(&first, 0, cp + 1 - first.buf);
/* second points at one past closing dq of name.
/*
* second points at one past closing dq of name.
* find the second name.
*/
while ((second < line + llen) && isspace(*second))
@@ -627,7 +635,8 @@ static char *git_header_name(char *line, int llen)
return NULL;
name++;
/* since the first name is unquoted, a dq if exists must be
/*
* since the first name is unquoted, a dq if exists must be
* the beginning of the second name.
*/
for (second = name; second < line + llen; second++) {
@@ -758,7 +767,7 @@ static int parse_num(const char *line, unsigned long *p)
}
static int parse_range(const char *line, int len, int offset, const char *expect,
unsigned long *p1, unsigned long *p2)
unsigned long *p1, unsigned long *p2)
{
int digits, ex;
@@ -867,14 +876,14 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
return offset;
}
/** --- followed by +++ ? */
/* --- followed by +++ ? */
if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4))
continue;
/*
* We only accept unified patches, so we want it to
* at least have "@@ -a,b +c,d @@\n", which is 14 chars
* minimum
* minimum ("@@ -0,0 +1 @@\n" is the shortest).
*/
nextlen = linelen(line + len, size - len);
if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
@@ -889,7 +898,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
return -1;
}
static void check_whitespace(const char *line, int len)
static void check_whitespace(const char *line, int len, unsigned ws_rule)
{
const char *err = "Adds trailing whitespace";
int seen_space = 0;
@@ -901,23 +910,35 @@ static void check_whitespace(const char *line, int len)
* this function. That is, an addition of an empty line would
* check the '+' here. Sneaky...
*/
if (isspace(line[len-2]))
if ((ws_rule & WS_TRAILING_SPACE) && isspace(line[len-2]))
goto error;
/*
* Make sure that there is no space followed by a tab in
* indentation.
*/
err = "Space in indent is followed by a tab";
for (i = 1; i < len; i++) {
if (line[i] == '\t') {
if (seen_space)
goto error;
if (ws_rule & WS_SPACE_BEFORE_TAB) {
err = "Space in indent is followed by a tab";
for (i = 1; i < len; i++) {
if (line[i] == '\t') {
if (seen_space)
goto error;
}
else if (line[i] == ' ')
seen_space = 1;
else
break;
}
else if (line[i] == ' ')
seen_space = 1;
else
break;
}
/*
* Make sure that the indentation does not contain more than
* 8 spaces.
*/
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
(8 < len) && !strncmp("+ ", line, 9)) {
err = "Indent more than 8 places with spaces";
goto error;
}
return;
@@ -931,14 +952,14 @@ static void check_whitespace(const char *line, int len)
err, patch_input_file, linenr, len-2, line+1);
}
/*
* Parse a unified diff. Note that this really needs to parse each
* fragment separately, since the only way to know the difference
* between a "---" that is part of a patch, and a "---" that starts
* the next patch is to look at the line counts..
*/
static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
static int parse_fragment(char *line, unsigned long size,
struct patch *patch, struct fragment *fragment)
{
int added, deleted;
int len = linelen(line, size), offset;
@@ -979,22 +1000,23 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
break;
case '-':
if (apply_in_reverse &&
new_whitespace != nowarn_whitespace)
check_whitespace(line, len);
ws_error_action != nowarn_ws_error)
check_whitespace(line, len, patch->ws_rule);
deleted++;
oldlines--;
trailing = 0;
break;
case '+':
if (!apply_in_reverse &&
new_whitespace != nowarn_whitespace)
check_whitespace(line, len);
ws_error_action != nowarn_ws_error)
check_whitespace(line, len, patch->ws_rule);
added++;
newlines--;
trailing = 0;
break;
/* We allow "\ No newline at end of file". Depending
/*
* We allow "\ No newline at end of file". Depending
* on locale settings when the patch was produced we
* don't know what this line looks like. The only
* thing we do know is that it begins with "\ ".
@@ -1012,7 +1034,8 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
fragment->leading = leading;
fragment->trailing = trailing;
/* If a fragment ends with an incomplete line, we failed to include
/*
* If a fragment ends with an incomplete line, we failed to include
* it in the above loop because we hit oldlines == newlines == 0
* before seeing it.
*/
@@ -1140,7 +1163,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,
int *status_p,
int *used_p)
{
/* Expect a line that begins with binary patch method ("literal"
/*
* Expect a line that begins with binary patch method ("literal"
* or "delta"), followed by the length of data before deflating.
* a sequence of 'length-byte' followed by base-85 encoded data
* should follow, terminated by a newline.
@@ -1189,7 +1213,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,
size--;
break;
}
/* Minimum line is "A00000\n" which is 7-byte long,
/*
* Minimum line is "A00000\n" which is 7-byte long,
* and the line length must be multiple of 5 plus 2.
*/
if ((llen < 7) || (llen-2) % 5)
@@ -1240,7 +1265,8 @@ static struct fragment *parse_binary_hunk(char **buf_p,
static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
{
/* We have read "GIT binary patch\n"; what follows is a line
/*
* We have read "GIT binary patch\n"; what follows is a line
* that says the patch method (currently, either "literal" or
* "delta") and the length of data before deflating; a
* sequence of 'length-byte' followed by base-85 encoded data
@@ -1270,7 +1296,8 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
if (reverse)
used += used_1;
else if (status) {
/* not having reverse hunk is not an error, but having
/*
* Not having reverse hunk is not an error, but having
* a corrupt reverse hunk is.
*/
free((void*) forward->patch);
@@ -1291,7 +1318,12 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
if (offset < 0)
return offset;
patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
patch->ws_rule = whitespace_rule(patch->new_name
? patch->new_name
: patch->old_name);
patchsize = parse_single_patch(buffer + offset + hdrsize,
size - offset - hdrsize, patch);
if (!patchsize) {
static const char *binhdr[] = {
@@ -1367,8 +1399,10 @@ static void reverse_patches(struct patch *p)
}
}
static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]= "----------------------------------------------------------------------";
static const char pluses[] =
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
static const char minuses[]=
"----------------------------------------------------------------------";
static void show_stats(struct patch *patch)
{
@@ -1437,7 +1471,9 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
}
}
static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)
static int find_offset(const char *buf, unsigned long size,
const char *fragment, unsigned long fragsize,
int line, int *lines)
{
int i;
unsigned long start, backwards, forwards;
@@ -1536,9 +1572,11 @@ static void remove_last_line(const char **rbuf, int *rsize)
*rsize = offset + 1;
}
static int apply_line(char *output, const char *patch, int plen)
static int apply_line(char *output, const char *patch, int plen,
unsigned ws_rule)
{
/* plen is number of bytes to be copied from patch,
/*
* plen is number of bytes to be copied from patch,
* starting at patch+1 (patch[0] is '+'). Typically
* patch[plen] is '\n', unless this is the incomplete
* last line.
@@ -1551,13 +1589,17 @@ static int apply_line(char *output, const char *patch, int plen)
int need_fix_leading_space = 0;
char *buf;
if ((new_whitespace != strip_whitespace) || !whitespace_error ||
if ((ws_error_action != correct_ws_error) || !whitespace_error ||
*patch != '+') {
memcpy(output, patch + 1, plen);
return plen;
}
if (1 < plen && isspace(patch[plen-1])) {
/*
* Strip trailing whitespace
*/
if ((ws_rule & WS_TRAILING_SPACE) &&
(1 < plen && isspace(patch[plen-1]))) {
if (patch[plen] == '\n')
add_nl_to_tail = 1;
plen--;
@@ -1566,15 +1608,23 @@ static int apply_line(char *output, const char *patch, int plen)
fixed = 1;
}
/*
* Check leading whitespaces (indent)
*/
for (i = 1; i < plen; i++) {
char ch = patch[i];
if (ch == '\t') {
last_tab_in_indent = i;
if (0 <= last_space_in_indent)
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
0 <= last_space_in_indent)
need_fix_leading_space = 1;
} else if (ch == ' ') {
last_space_in_indent = i;
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
last_tab_in_indent < 0 &&
8 <= i)
need_fix_leading_space = 1;
}
else if (ch == ' ')
last_space_in_indent = i;
else
break;
}
@@ -1582,10 +1632,21 @@ static int apply_line(char *output, const char *patch, int plen)
buf = output;
if (need_fix_leading_space) {
int consecutive_spaces = 0;
/* between patch[1..last_tab_in_indent] strip the
* funny spaces, updating them to tab as needed.
int last = last_tab_in_indent + 1;
if (ws_rule & WS_INDENT_WITH_NON_TAB) {
/* have "last" point at one past the indent */
if (last_tab_in_indent < last_space_in_indent)
last = last_space_in_indent + 1;
else
last = last_tab_in_indent + 1;
}
/*
* between patch[1..last], strip the funny spaces,
* updating them to tab as needed.
*/
for (i = 1; i < last_tab_in_indent; i++, plen--) {
for (i = 1; i < last; i++, plen--) {
char ch = patch[i];
if (ch != ' ') {
consecutive_spaces = 0;
@@ -1598,8 +1659,10 @@ static int apply_line(char *output, const char *patch, int plen)
}
}
}
while (0 < consecutive_spaces--)
*output++ = ' ';
fixed = 1;
i = last_tab_in_indent;
i = last;
}
else
i = 1;
@@ -1612,7 +1675,8 @@ static int apply_line(char *output, const char *patch, int plen)
return output + plen - buf;
}
static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int inaccurate_eof)
static int apply_one_fragment(struct strbuf *buf, struct fragment *frag,
int inaccurate_eof, unsigned ws_rule)
{
int match_beginning, match_end;
const char *patch = frag->patch;
@@ -1671,7 +1735,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
case '+':
if (first != '+' || !no_add) {
int added = apply_line(new + newsize, patch,
plen);
plen, ws_rule);
newsize += added;
if (first == '+' &&
added == 1 && new[newsize-1] == '\n')
@@ -1694,8 +1758,9 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
size -= len;
}
if (inaccurate_eof && oldsize > 0 && old[oldsize - 1] == '\n' &&
newsize > 0 && new[newsize - 1] == '\n') {
if (inaccurate_eof &&
oldsize > 0 && old[oldsize - 1] == '\n' &&
newsize > 0 && new[newsize - 1] == '\n') {
oldsize--;
newsize--;
}
@@ -1732,7 +1797,7 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
if (match_beginning && offset)
offset = -1;
if (offset >= 0) {
if (new_whitespace == strip_whitespace &&
if (ws_error_action == correct_ws_error &&
(buf->len - oldsize - offset == 0)) /* end of file? */
newsize -= new_blank_lines_at_end;
@@ -1757,9 +1822,10 @@ static int apply_one_fragment(struct strbuf *buf, struct fragment *frag, int ina
match_beginning = match_end = 0;
continue;
}
/* Reduce the number of context lines
* Reduce both leading and trailing if they are equal
* otherwise just reduce the larger context.
/*
* Reduce the number of context lines; reduce both
* leading and trailing if they are equal otherwise
* just reduce the larger context.
*/
if (leading >= trailing) {
remove_first_line(&oldlines, &oldsize);
@@ -1819,7 +1885,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
const char *name = patch->old_name ? patch->old_name : patch->new_name;
unsigned char sha1[20];
/* For safety, we require patch index line to contain
/*
* For safety, we require patch index line to contain
* full 40-byte textual SHA1 for old and new, at least for now.
*/
if (strlen(patch->old_sha1_prefix) != 40 ||
@@ -1830,7 +1897,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
"without full index line", name);
if (patch->old_name) {
/* See if the old one matches what the patch
/*
* See if the old one matches what the patch
* applies to.
*/
hash_sha1_file(buf->buf, buf->len, blob_type, sha1);
@@ -1867,7 +1935,8 @@ static int apply_binary(struct strbuf *buf, struct patch *patch)
/* XXX read_sha1_file NUL-terminates */
strbuf_attach(buf, result, size, size + 1);
} else {
/* We have verified buf matches the preimage;
/*
* We have verified buf matches the preimage;
* apply the patch data to it, which is stored
* in the patch->fragments->{patch,size}.
*/
@@ -1889,12 +1958,14 @@ static int apply_fragments(struct strbuf *buf, struct patch *patch)
{
struct fragment *frag = patch->fragments;
const char *name = patch->old_name ? patch->old_name : patch->new_name;
unsigned ws_rule = patch->ws_rule;
unsigned inaccurate_eof = patch->inaccurate_eof;
if (patch->is_binary)
return apply_binary(buf, patch);
while (frag) {
if (apply_one_fragment(buf, frag, patch->inaccurate_eof)) {
if (apply_one_fragment(buf, frag, inaccurate_eof, ws_rule)) {
error("patch failed: %s:%ld", name, frag->oldpos);
if (!apply_with_reject)
return -1;
@@ -2066,7 +2137,8 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
if (new_name && prev_patch && 0 < prev_patch->is_delete &&
!strcmp(prev_patch->old_name, new_name))
/* A type-change diff is always split into a patch to
/*
* A type-change diff is always split into a patch to
* delete old, immediately followed by a patch to
* create new (see diff.c::run_diff()); in such a case
* it is Ok that the entry to be deleted by the
@@ -2670,7 +2742,7 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
offset += nr;
}
if (whitespace_error && (new_whitespace == error_on_whitespace))
if (whitespace_error && (ws_error_action == die_on_ws_error))
apply = 0;
update_index = check_index && apply;
@@ -2865,7 +2937,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
squelched,
squelched == 1 ? "" : "s");
}
if (new_whitespace == error_on_whitespace)
if (ws_error_action == die_on_ws_error)
die("%d line%s add%s whitespace errors.",
whitespace_error,
whitespace_error == 1 ? "" : "s",

View File

@@ -130,6 +130,14 @@ static void origin_decref(struct origin *o)
}
}
static void drop_origin_blob(struct origin *o)
{
if (o->file.ptr) {
free(o->file.ptr);
o->file.ptr = NULL;
}
}
/*
* Each group of lines is described by a blame_entry; it can be split
* as we pass blame to the parents. They form a linked list in the
@@ -380,6 +388,7 @@ static struct origin *find_origin(struct scoreboard *sb,
}
}
diff_flush(&diff_opts);
diff_tree_release_paths(&diff_opts);
if (porigin) {
/*
* Create a freestanding copy that is not part of
@@ -436,6 +445,7 @@ static struct origin *find_rename(struct scoreboard *sb,
}
}
diff_flush(&diff_opts);
diff_tree_release_paths(&diff_opts);
return porigin;
}
@@ -1157,7 +1167,7 @@ static int find_copy_in_parent(struct scoreboard *sb,
}
}
diff_flush(&diff_opts);
diff_tree_release_paths(&diff_opts);
return retval;
}
@@ -1274,8 +1284,13 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
}
finish:
for (i = 0; i < MAXPARENT; i++)
origin_decref(parent_origin[i]);
for (i = 0; i < MAXPARENT; i++) {
if (parent_origin[i]) {
drop_origin_blob(parent_origin[i]);
origin_decref(parent_origin[i]);
}
}
drop_origin_blob(origin);
}
/*

View File

@@ -98,8 +98,8 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
strbuf_addf(&buffer, "parent %s\n", sha1_to_hex(parent_sha1[i]));
/* Person/date information */
strbuf_addf(&buffer, "author %s\n", git_author_info(1));
strbuf_addf(&buffer, "committer %s\n", git_committer_info(1));
strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME));
strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
if (!encoding_is_utf8)
strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
strbuf_addch(&buffer, '\n');

View File

@@ -285,7 +285,8 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix)
struct wt_status s;
wt_status_prepare(&s);
s.prefix = prefix;
if (wt_status_relative_paths)
s.prefix = prefix;
if (amend) {
s.amend = 1;
@@ -496,7 +497,7 @@ static void determine_author_info(struct strbuf *sb)
email = xstrndup(lb + 2, rb - (lb + 2));
}
strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, 1));
strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME));
}
static int parse_and_validate_options(int argc, const char *argv[],
@@ -659,12 +660,16 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
rev.verbose_header = 1;
rev.show_root_diff = 1;
rev.commit_format = get_commit_format("format:%h: %s");
rev.always_show_header = 1;
rev.always_show_header = 0;
printf("Created %scommit ", initial_commit ? "initial " : "");
log_tree_commit(&rev, commit);
printf("\n");
if (!log_tree_commit(&rev, commit)) {
struct strbuf buf = STRBUF_INIT;
format_commit_message(commit, "%h: %s", &buf);
printf("%s\n", buf.buf);
strbuf_release(&buf);
}
}
int git_commit_config(const char *k, const char *v)
@@ -775,7 +780,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
}
determine_author_info(&sb);
strbuf_addf(&sb, "committer %s\n", git_committer_info(1));
strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
if (!is_encoding_utf8(git_commit_encoding))
strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
strbuf_addch(&sb, '\n');
@@ -786,15 +791,17 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
char index[PATH_MAX];
const char *env[2] = { index, NULL };
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
launch_editor(git_path(commit_editmsg), &sb, env);
} else if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
rollback_index_files();
die("could not read commit message");
launch_editor(git_path(commit_editmsg), NULL, env);
}
if (run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
if (!no_verify &&
run_hook(index_file, "commit-msg", git_path(commit_editmsg))) {
rollback_index_files();
exit(1);
}
if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
rollback_index_files();
die("could not read commit message");
}
/* Truncate the message just before the diff, if any. */
p = strstr(sb.buf, "\ndiff --git a/");

View File

@@ -176,18 +176,6 @@ static int builtin_diff_combined(struct rev_info *revs,
return 0;
}
void add_head(struct rev_info *revs)
{
unsigned char sha1[20];
struct object *obj;
if (get_sha1("HEAD", sha1))
return;
obj = parse_object(sha1);
if (!obj)
return;
add_pending_object(revs, obj, "HEAD");
}
static void refresh_index_quietly(void)
{
struct lock_file *lock_file;
@@ -272,7 +260,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!strcmp(arg, "--"))
break;
else if (!strcmp(arg, "--cached")) {
add_head(&rev);
add_head_to_pending(&rev);
if (!rev.pending.nr)
die("No HEAD commit to compare with (yet)");
break;

View File

@@ -103,7 +103,7 @@ static void handle_object(const unsigned char *sha1)
mark_object(object);
printf("blob\nmark :%d\ndata %lu\n", last_idnum, size);
if (fwrite(buf, size, 1, stdout) != 1)
if (size && fwrite(buf, size, 1, stdout) != 1)
die ("Could not write blob %s", sha1_to_hex(sha1));
printf("\n");

View File

@@ -18,9 +18,6 @@
static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);
static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
{
int plen = strlen(prefix);
@@ -557,7 +554,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const cha
static void gen_message_id(char *dest, unsigned int length, char *base)
{
const char *committer = git_committer_info(-1);
const char *committer = git_committer_info(IDENT_WARN_ON_NO_NAME);
const char *email_start = strrchr(committer, '<');
const char *email_end = strrchr(committer, '>');
if(!email_start || !email_end || email_start > email_end - 1)
@@ -665,7 +662,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
!strcmp(argv[i], "-s")) {
const char *committer;
const char *endpos;
committer = git_committer_info(1);
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
endpos = strchr(committer, '>');
if (!endpos)
die("bogos committer info %s\n", committer);
@@ -746,7 +743,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
* does not have.
*/
rev.pending.objects[0].item->flags |= UNINTERESTING;
add_head(&rev);
add_head_to_pending(&rev);
}
/*
* Otherwise, it is "format-patch -22 HEAD", and/or

View File

@@ -6,6 +6,27 @@
static const char ls_remote_usage[] =
"git-ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
/*
* Is there one among the list of patterns that match the tail part
* of the path?
*/
static int tail_match(const char **pattern, const char *path)
{
const char *p;
char pathbuf[PATH_MAX];
if (!pattern)
return 1; /* no restriction */
if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
return error("insanely long ref %.*s...", 20, path);
while ((p = *(pattern++)) != NULL) {
if (!fnmatch(p, pathbuf, 0))
return 1;
}
return 0;
}
int cmd_ls_remote(int argc, const char **argv, const char *prefix)
{
int i;
@@ -13,6 +34,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
int nongit = 0;
unsigned flags = 0;
const char *uploadpack = NULL;
const char **pattern = NULL;
struct remote *remote;
struct transport *transport;
@@ -47,12 +69,23 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
usage(ls_remote_usage);
}
dest = arg;
i++;
break;
}
if (!dest || i != argc - 1)
if (!dest)
usage(ls_remote_usage);
if (argv[i]) {
int j;
pattern = xcalloc(sizeof(const char *), argc - i + 1);
for (j = i; j < argc; j++) {
int len = strlen(argv[j]);
char *p = xmalloc(len + 3);
sprintf(p, "*/%s", argv[j]);
pattern[j - i] = p;
}
}
remote = nongit ? NULL : remote_get(dest);
if (remote && !remote->url_nr)
die("remote %s has no configured URL", dest);
@@ -65,10 +98,12 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
if (!ref)
return 1;
while (ref) {
if (check_ref_type(ref, flags))
printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name);
ref = ref->next;
for ( ; ref; ref = ref->next) {
if (!check_ref_type(ref, flags))
continue;
if (!tail_match(pattern, ref->name))
continue;
printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name);
}
return 0;
}

View File

@@ -1245,28 +1245,37 @@ static void get_object_details(void)
free(sorted_by_offset);
}
/*
* We search for deltas in a list sorted by type, by filename hash, and then
* by size, so that we see progressively smaller and smaller files.
* That's because we prefer deltas to be from the bigger file
* to the smaller -- deletes are potentially cheaper, but perhaps
* more importantly, the bigger file is likely the more recent
* one. The deepest deltas are therefore the oldest objects which are
* less susceptible to be accessed often.
*/
static int type_size_sort(const void *_a, const void *_b)
{
const struct object_entry *a = *(struct object_entry **)_a;
const struct object_entry *b = *(struct object_entry **)_b;
if (a->type < b->type)
return -1;
if (a->type > b->type)
return 1;
if (a->hash < b->hash)
return -1;
if (a->type < b->type)
return 1;
if (a->hash > b->hash)
return 1;
if (a->preferred_base < b->preferred_base)
return -1;
if (a->hash < b->hash)
return 1;
if (a->preferred_base > b->preferred_base)
return 1;
if (a->size < b->size)
return -1;
if (a->size > b->size)
if (a->preferred_base < b->preferred_base)
return 1;
return a > b ? -1 : (a < b); /* newest last */
if (a->size > b->size)
return -1;
if (a->size < b->size)
return 1;
return a < b ? -1 : (a > b); /* newest first */
}
struct unpacked {
@@ -1317,14 +1326,6 @@ static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
/*
* We search for deltas _backwards_ in a list sorted by type and
* by size, so that we see progressively smaller and smaller files.
* That's because we prefer deltas to be from the bigger file
* to the smaller - deletes are potentially cheaper, but perhaps
* more importantly, the bigger file is likely the more recent
* one.
*/
static int try_delta(struct unpacked *trg, struct unpacked *src,
unsigned max_depth, unsigned long *mem_usage)
{
@@ -1422,10 +1423,6 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
}
}
trg_entry->delta = src_entry;
trg_entry->delta_size = delta_size;
trg->depth = src->depth + 1;
/*
* Handle memory allocation outside of the cache
* accounting lock. Compiler will optimize the strangeness
@@ -1439,7 +1436,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
trg_entry->delta_data = NULL;
}
if (delta_cacheable(src_size, trg_size, delta_size)) {
delta_cache_size += trg_entry->delta_size;
delta_cache_size += delta_size;
cache_unlock();
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
} else {
@@ -1447,6 +1444,10 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
free(delta_buf);
}
trg_entry->delta = src_entry;
trg_entry->delta_size = delta_size;
trg->depth = src->depth + 1;
return 1;
}
@@ -1478,10 +1479,10 @@ static unsigned long free_unpacked(struct unpacked *n)
return freed_mem;
}
static void find_deltas(struct object_entry **list, unsigned list_size,
static void find_deltas(struct object_entry **list, unsigned *list_size,
int window, int depth, unsigned *processed)
{
uint32_t i = list_size, idx = 0, count = 0;
uint32_t i, idx = 0, count = 0;
unsigned int array_size = window * sizeof(struct unpacked);
struct unpacked *array;
unsigned long mem_usage = 0;
@@ -1489,11 +1490,23 @@ static void find_deltas(struct object_entry **list, unsigned list_size,
array = xmalloc(array_size);
memset(array, 0, array_size);
do {
struct object_entry *entry = list[--i];
for (;;) {
struct object_entry *entry = *list++;
struct unpacked *n = array + idx;
int j, max_depth, best_base = -1;
progress_lock();
if (!*list_size) {
progress_unlock();
break;
}
(*list_size)--;
if (!entry->preferred_base) {
(*processed)++;
display_progress(progress_state, *processed);
}
progress_unlock();
mem_usage -= free_unpacked(n);
n->entry = entry;
@@ -1511,11 +1524,6 @@ static void find_deltas(struct object_entry **list, unsigned list_size,
if (entry->preferred_base)
goto next;
progress_lock();
(*processed)++;
display_progress(progress_state, *processed);
progress_unlock();
/*
* If the current object is at pack edge, take the depth the
* objects that depend on the current object into account
@@ -1575,7 +1583,7 @@ static void find_deltas(struct object_entry **list, unsigned list_size,
count++;
if (idx >= window)
idx = 0;
} while (i > 0);
}
for (i = 0; i < window; ++i) {
free_delta_index(array[i].index);
@@ -1590,6 +1598,7 @@ struct thread_params {
pthread_t thread;
struct object_entry **list;
unsigned list_size;
unsigned remaining;
int window;
int depth;
unsigned *processed;
@@ -1611,10 +1620,10 @@ static void *threaded_find_deltas(void *arg)
pthread_mutex_lock(&data_ready);
pthread_mutex_unlock(&data_request);
if (!me->list_size)
if (!me->remaining)
return NULL;
find_deltas(me->list, me->list_size,
find_deltas(me->list, &me->remaining,
me->window, me->depth, me->processed);
}
}
@@ -1623,57 +1632,112 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
int window, int depth, unsigned *processed)
{
struct thread_params *target, p[delta_search_threads];
int i, ret;
unsigned chunk_size;
int i, ret, active_threads = 0;
if (delta_search_threads <= 1) {
find_deltas(list, list_size, window, depth, processed);
find_deltas(list, &list_size, window, depth, processed);
return;
}
pthread_mutex_lock(&data_provider);
pthread_mutex_lock(&data_ready);
/* Start work threads. */
for (i = 0; i < delta_search_threads; i++) {
p[i].window = window;
p[i].depth = depth;
p[i].processed = processed;
p[i].remaining = 0;
ret = pthread_create(&p[i].thread, NULL,
threaded_find_deltas, &p[i]);
if (ret)
die("unable to create thread: %s", strerror(ret));
active_threads++;
}
/* this should be auto-tuned somehow */
chunk_size = window * 1000;
do {
unsigned sublist_size = chunk_size;
if (sublist_size > list_size)
sublist_size = list_size;
/* try to split chunks on "path" boundaries */
while (sublist_size < list_size && list[sublist_size]->hash &&
list[sublist_size]->hash == list[sublist_size-1]->hash)
sublist_size++;
/* Then partition the work amongst them. */
for (i = 0; i < delta_search_threads; i++) {
unsigned sub_size = list_size / (delta_search_threads - i);
pthread_mutex_lock(&data_provider);
target = data_requester;
if (!sub_size) {
pthread_mutex_unlock(&data_ready);
pthread_join(target->thread, NULL);
active_threads--;
continue;
}
/* try to split chunks on "path" boundaries */
while (sub_size < list_size && list[sub_size]->hash &&
list[sub_size]->hash == list[sub_size-1]->hash)
sub_size++;
target->list = list;
target->list_size = sublist_size;
target->list_size = sub_size;
target->remaining = sub_size;
pthread_mutex_unlock(&data_ready);
list += sublist_size;
list_size -= sublist_size;
if (!sublist_size) {
pthread_join(target->thread, NULL);
i--;
list += sub_size;
list_size -= sub_size;
}
/*
* Now let's wait for work completion. Each time a thread is done
* with its work, we steal half of the remaining work from the
* thread with the largest number of unprocessed objects and give
* it to that newly idle thread. This ensure good load balancing
* until the remaining object list segments are simply too short
* to be worth splitting anymore.
*/
do {
struct thread_params *victim = NULL;
unsigned sub_size = 0;
pthread_mutex_lock(&data_provider);
target = data_requester;
progress_lock();
for (i = 0; i < delta_search_threads; i++)
if (p[i].remaining > 2*window &&
(!victim || victim->remaining < p[i].remaining))
victim = &p[i];
if (victim) {
sub_size = victim->remaining / 2;
list = victim->list + victim->list_size - sub_size;
while (sub_size && list[0]->hash &&
list[0]->hash == list[-1]->hash) {
list++;
sub_size--;
}
if (!sub_size) {
/*
* It is possible for some "paths" to have
* so many objects that no hash boundary
* might be found. Let's just steal the
* exact half in that case.
*/
sub_size = victim->remaining / 2;
list -= sub_size;
}
target->list = list;
victim->list_size -= sub_size;
victim->remaining -= sub_size;
}
} while (i);
progress_unlock();
target->list_size = sub_size;
target->remaining = sub_size;
pthread_mutex_unlock(&data_ready);
if (!sub_size) {
pthread_join(target->thread, NULL);
active_threads--;
}
} while (active_threads);
}
#else
#define ll_find_deltas find_deltas
#define ll_find_deltas(l, s, w, d, p) find_deltas(l, &s, w, d, p)
#endif
static void prepare_pack(int window, int depth)

View File

@@ -158,6 +158,7 @@ static int read_from_tree(const char *prefix, const char **argv,
return 1;
diffcore_std(&opt);
diff_flush(&opt);
diff_tree_release_paths(&opt);
if (!index_was_discarded)
/* The index is still clobbered from do_diff_cache() */

View File

@@ -11,6 +11,7 @@ static const char shortlog_usage[] =
"git-shortlog [-n] [-s] [<commit-id>... ]";
static char *common_repo_prefix;
static int email;
static int compare_by_number(const void *a1, const void *a2)
{
@@ -27,45 +28,68 @@ static int compare_by_number(const void *a1, const void *a2)
static struct path_list mailmap = {NULL, 0, 0, 0};
static void insert_author_oneline(struct path_list *list,
const char *author, int authorlen,
const char *oneline, int onelinelen)
static void insert_one_record(struct path_list *list,
const char *author,
const char *oneline)
{
const char *dot3 = common_repo_prefix;
char *buffer, *p;
struct path_list_item *item;
struct path_list *onelines;
char namebuf[1024];
size_t len;
const char *eol;
const char *boemail, *eoemail;
while (authorlen > 0 && isspace(author[authorlen - 1]))
authorlen--;
boemail = strchr(author, '<');
if (!boemail)
return;
eoemail = strchr(boemail, '>');
if (!eoemail)
return;
if (!map_email(&mailmap, boemail+1, namebuf, sizeof(namebuf))) {
while (author < boemail && isspace(*author))
author++;
for (len = 0;
len < sizeof(namebuf) - 1 && author + len < boemail;
len++)
namebuf[len] = author[len];
while (0 < len && isspace(namebuf[len-1]))
len--;
namebuf[len] = '\0';
}
else
len = strlen(namebuf);
buffer = xmemdupz(author, authorlen);
if (email) {
size_t room = sizeof(namebuf) - len - 1;
int maillen = eoemail - boemail + 1;
snprintf(namebuf + len, room, " %.*s", maillen, boemail);
}
buffer = xstrdup(namebuf);
item = path_list_insert(buffer, list);
if (item->util == NULL)
item->util = xcalloc(1, sizeof(struct path_list));
else
free(buffer);
eol = strchr(oneline, '\n');
if (!eol)
eol = oneline + strlen(oneline);
while (*oneline && isspace(*oneline) && *oneline != '\n')
oneline++;
if (!prefixcmp(oneline, "[PATCH")) {
char *eob = strchr(oneline, ']');
if (eob) {
while (isspace(eob[1]) && eob[1] != '\n')
eob++;
if (eob - oneline < onelinelen) {
onelinelen -= eob - oneline;
oneline = eob;
}
}
if (eob && (!eol || eob < eol))
oneline = eob + 1;
}
while (onelinelen > 0 && isspace(oneline[0])) {
while (*oneline && isspace(*oneline) && *oneline != '\n')
oneline++;
onelinelen--;
}
while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
onelinelen--;
buffer = xmemdupz(oneline, onelinelen);
len = eol - oneline;
while (len && isspace(oneline[len-1]))
len--;
buffer = xmemdupz(oneline, len);
if (dot3) {
int dot3len = strlen(dot3);
@@ -92,55 +116,32 @@ static void insert_author_oneline(struct path_list *list,
static void read_from_stdin(struct path_list *list)
{
char buffer[1024];
char author[1024], oneline[1024];
while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
char *bob;
if ((buffer[0] == 'A' || buffer[0] == 'a') &&
!prefixcmp(buffer + 1, "uthor: ") &&
(bob = strchr(buffer + 7, '<')) != NULL) {
char buffer2[1024], offset = 0;
if (map_email(&mailmap, bob + 1, buffer, sizeof(buffer)))
bob = buffer + strlen(buffer);
else {
offset = 8;
while (buffer + offset < bob &&
isspace(bob[-1]))
bob--;
}
while (fgets(buffer2, sizeof(buffer2), stdin) &&
buffer2[0] != '\n')
; /* chomp input */
if (fgets(buffer2, sizeof(buffer2), stdin)) {
int l2 = strlen(buffer2);
int i;
for (i = 0; i < l2; i++)
if (!isspace(buffer2[i]))
break;
insert_author_oneline(list,
buffer + offset,
bob - buffer - offset,
buffer2 + i, l2 - i);
}
}
while (fgets(author, sizeof(author), stdin) != NULL) {
if (!(author[0] == 'A' || author[0] == 'a') ||
prefixcmp(author + 1, "uthor: "))
continue;
while (fgets(oneline, sizeof(oneline), stdin) &&
oneline[0] != '\n')
; /* discard headers */
while (fgets(oneline, sizeof(oneline), stdin) &&
oneline[0] == '\n')
; /* discard blanks */
insert_one_record(list, author + 8, oneline);
}
}
static void get_from_rev(struct rev_info *rev, struct path_list *list)
{
char scratch[1024];
struct commit *commit;
prepare_revision_walk(rev);
while ((commit = get_revision(rev)) != NULL) {
const char *author = NULL, *oneline, *buffer;
int authorlen = authorlen, onelinelen;
const char *author = NULL, *buffer;
/* get author and oneline */
for (buffer = commit->buffer; buffer && *buffer != '\0' &&
*buffer != '\n'; ) {
buffer = commit->buffer;
while (*buffer && *buffer != '\n') {
const char *eol = strchr(buffer, '\n');
if (eol == NULL)
@@ -148,50 +149,17 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list)
else
eol++;
if (!prefixcmp(buffer, "author ")) {
char *bracket = strchr(buffer, '<');
if (bracket == NULL || bracket > eol)
die("Invalid commit buffer: %s",
sha1_to_hex(commit->object.sha1));
if (map_email(&mailmap, bracket + 1, scratch,
sizeof(scratch))) {
author = scratch;
authorlen = strlen(scratch);
} else {
if (bracket[-1] == ' ')
bracket--;
author = buffer + 7;
authorlen = bracket - buffer - 7;
}
}
if (!prefixcmp(buffer, "author "))
author = buffer + 7;
buffer = eol;
}
if (author == NULL)
die ("Missing author: %s",
sha1_to_hex(commit->object.sha1));
if (buffer == NULL || *buffer == '\0') {
oneline = "<none>";
onelinelen = sizeof(oneline) + 1;
} else {
char *eol;
oneline = buffer + 1;
eol = strchr(oneline, '\n');
if (eol == NULL)
onelinelen = strlen(oneline);
else
onelinelen = eol - oneline;
}
insert_author_oneline(list,
author, authorlen, oneline, onelinelen);
if (!author)
die("Missing author: %s",
sha1_to_hex(commit->object.sha1));
if (*buffer)
buffer++;
insert_one_record(list, author, !*buffer ? "<none>" : buffer);
}
}
static int parse_uint(char const **arg, int comma)
@@ -260,6 +228,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
else if (!strcmp(argv[1], "-s") ||
!strcmp(argv[1], "--summary"))
summary = 1;
else if (!strcmp(argv[1], "-e") ||
!strcmp(argv[1], "--email"))
email = 1;
else if (!prefixcmp(argv[1], "-w")) {
wrap_lines = 1;
parse_wrap_args(argv[1], &in1, &in2, &wrap);
@@ -278,9 +249,10 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
/* assume HEAD if from a tty */
if (!rev.pending.nr && isatty(0))
add_head_to_pending(&rev);
if (rev.pending.nr == 0) {
if (isatty(0))
fprintf(stderr, "(reading log to summarize from standard input)\n");
read_from_stdin(&list);
}
else
@@ -294,7 +266,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
struct path_list *onelines = list.items[i].util;
if (summary) {
printf("%s: %d\n", list.items[i].path, onelines->nr);
printf("%6d\t%s\n", onelines->nr, list.items[i].path);
} else {
printf("%s (%d):\n", list.items[i].path, onelines->nr);
for (j = onelines->nr - 1; j >= 0; j--) {

View File

@@ -53,6 +53,8 @@ void launch_editor(const char *path, struct strbuf *buffer, const char *const *e
die("There was a problem with the editor %s.", editor);
}
if (!buffer)
return;
if (strbuf_read_file(buffer, path, 0) < 0)
die("could not read message file '%s': %s",
path, strerror(errno));
@@ -186,7 +188,7 @@ static int do_sign(struct strbuf *buffer)
int len;
if (!*signingkey) {
if (strlcpy(signingkey, git_committer_info(1),
if (strlcpy(signingkey, git_committer_info(IDENT_ERROR_ON_NO_NAME),
sizeof(signingkey)) > sizeof(signingkey) - 1)
return error("committer info too long.");
bracket = strchr(signingkey, '>');
@@ -248,14 +250,18 @@ static const char tag_template[] =
"# Write a tag message\n"
"#\n";
static void set_signingkey(const char *value)
{
if (strlcpy(signingkey, value, sizeof(signingkey)) >= sizeof(signingkey))
die("signing key value too long (%.10s...)", value);
}
static int git_tag_config(const char *var, const char *value)
{
if (!strcmp(var, "user.signingkey")) {
if (!value)
die("user.signingkey without value");
if (strlcpy(signingkey, value, sizeof(signingkey))
>= sizeof(signingkey))
die("user.signingkey value too long");
set_signingkey(value);
return 0;
}
@@ -310,7 +316,7 @@ static void create_tag(const unsigned char *object, const char *tag,
sha1_to_hex(object),
typename(type),
tag,
git_committer_info(1));
git_committer_info(IDENT_ERROR_ON_NO_NAME));
if (header_len > sizeof(header_buf) - 1)
die("tag header too big.");
@@ -408,6 +414,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, options, git_tag_usage, 0);
if (keyid) {
sign = 1;
set_signingkey(keyid);
}
if (sign)
annotate = 1;

17
cache.h
View File

@@ -468,6 +468,9 @@ void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
enum date_mode parse_date_format(const char *format);
#define IDENT_WARN_ON_NO_NAME 1
#define IDENT_ERROR_ON_NO_NAME 2
#define IDENT_NO_DATE 4
extern const char *git_author_info(int);
extern const char *git_committer_info(int);
extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
@@ -620,7 +623,7 @@ extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char
/* pager.c */
extern void setup_pager(void);
extern char *pager_program;
extern int pager_in_use;
extern int pager_in_use(void);
extern int pager_use_color;
extern char *editor_program;
@@ -656,6 +659,18 @@ extern int diff_auto_refresh_index;
/* match-trees.c */
void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
/*
* whitespace rules.
* used by both diff and apply
*/
#define WS_TRAILING_SPACE 01
#define WS_SPACE_BEFORE_TAB 02
#define WS_INDENT_WITH_NON_TAB 04
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *);
extern unsigned parse_whitespace_rule(const char *);
/* 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);

View File

@@ -135,7 +135,7 @@ int git_config_colorbool(const char *var, const char *value, int stdout_is_tty)
auto_color:
if (stdout_is_tty < 0)
stdout_is_tty = isatty(1);
if (stdout_is_tty || (pager_in_use && pager_use_color)) {
if (stdout_is_tty || (pager_in_use() && pager_use_color)) {
char *term = getenv("TERM");
if (term && strcmp(term, "dumb"))
return 1;

View File

@@ -439,6 +439,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.whitespace")) {
whitespace_rule_cfg = parse_whitespace_rule(value);
return 0;
}
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}
@@ -646,13 +651,19 @@ static int store_write_pair(int fd, const char* key, const char* value)
int length = strlen(key+store.baselen+1);
int quote = 0;
/* Check to see if the value needs to be quoted. */
/*
* Check to see if the value needs to be surrounded with a dq pair.
* Note that problematic characters are always backslash-quoted; this
* check is about not losing leading or trailing SP and strings that
* follow beginning-of-comment characters (i.e. ';' and '#') by the
* configuration parser.
*/
if (value[0] == ' ')
quote = 1;
for (i = 0; value[i]; i++)
if (value[i] == ';' || value[i] == '#')
quote = 1;
if (value[i-1] == ' ')
if (i && value[i-1] == ' ')
quote = 1;
if (write_in_full(fd, "\t", 1) != 1 ||

View File

@@ -23,6 +23,7 @@ VPATH = @srcdir@
export exec_prefix mandir
export srcdir VPATH
ASCIIDOC8=@ASCIIDOC8@
NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
NO_OPENSSL=@NO_OPENSSL@
NO_CURL=@NO_CURL@

View File

@@ -122,6 +122,27 @@ if test -z "$NO_TCLTK"; then
AC_SUBST(TCLTK_PATH)
fi
fi
AC_CHECK_PROGS(ASCIIDOC, [asciidoc])
if test -n "$ASCIIDOC"; then
AC_MSG_CHECKING([for asciidoc version])
asciidoc_version=`$ASCIIDOC --version 2>&1`
case "${asciidoc_version}" in
asciidoc' '8*)
ASCIIDOC8=YesPlease
AC_MSG_RESULT([${asciidoc_version} > 7])
;;
asciidoc' '7*)
ASCIIDOC8=
AC_MSG_RESULT([${asciidoc_version}])
;;
*)
ASCIIDOC8=
AC_MSG_RESULT([${asciidoc_version} (unknown)])
;;
esac
fi
AC_SUBST(ASCIIDOC8)
## Checks for libraries.
AC_MSG_NOTICE([CHECKS for libraries])

View File

@@ -523,8 +523,13 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
end = host;
path = strchr(end, c);
#ifdef __MINGW32__
/* host must have at least 2 chars to catch DOS C:/path */
if (path && path - end > 1) {
if (path && path - end > 1)
#else
if (path)
#endif
{
if (c == ':') {
protocol = PROTO_SSH;
*path++ = '\0';

View File

@@ -49,6 +49,7 @@
(eval-when-compile (require 'cl))
(require 'ewoc)
(require 'log-edit)
(require 'easymenu)
;;;; Customizations
@@ -1297,7 +1298,47 @@ Return the list of files that haven't been handled."
(define-key toggle-map "i" 'git-toggle-show-ignored)
(define-key toggle-map "k" 'git-toggle-show-unknown)
(define-key toggle-map "m" 'git-toggle-all-marks)
(setq git-status-mode-map map)))
(setq git-status-mode-map map))
(easy-menu-define git-menu git-status-mode-map
"Git Menu"
`("Git"
["Refresh" git-refresh-status t]
["Commit" git-commit-file t]
("Merge"
["Next Unmerged File" git-next-unmerged-file t]
["Prev Unmerged File" git-prev-unmerged-file t]
["Mark as Resolved" git-resolve-file t]
["Interactive Merge File" git-find-file-imerge t]
["Diff Against Common Base File" git-diff-file-base t]
["Diff Combined" git-diff-file-combined t]
["Diff Against Merge Head" git-diff-file-merge-head t]
["Diff Against Mine" git-diff-file-mine t]
["Diff Against Other" git-diff-file-other t])
"--------"
["Add File" git-add-file t]
["Revert File" git-revert-file t]
["Ignore File" git-ignore-file t]
["Remove File" git-remove-file t]
"--------"
["Find File" git-find-file t]
["View File" git-view-file t]
["Diff File" git-diff-file t]
["Interactive Diff File" git-diff-file-idiff t]
["Log" git-log-file t]
"--------"
["Mark" git-mark-file t]
["Mark All" git-mark-all t]
["Unmark" git-unmark-file t]
["Unmark All" git-unmark-all t]
["Toggle All Marks" git-toggle-all-marks t]
["Hide Handled Files" git-remove-handled t]
"--------"
["Show Uptodate Files" git-toggle-show-uptodate :style toggle :selected git-show-uptodate]
["Show Ignored Files" git-toggle-show-ignored :style toggle :selected git-show-ignored]
["Show Unknown Files" git-toggle-show-unknown :style toggle :selected git-show-unknown]
"--------"
["Quit" git-status-quit t])))
;; git mode should only run in the *git status* buffer
(put 'git-status-mode 'mode-class 'special)

157
diff.c
View File

@@ -454,6 +454,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
struct emit_callback {
struct xdiff_emit_state xm;
int nparents, color_diff;
unsigned ws_rule;
const char **label_path;
struct diff_words_data *diff_words;
int *found_changesp;
@@ -493,8 +494,8 @@ static void emit_line(const char *set, const char *reset, const char *line, int
}
static void emit_line_with_ws(int nparents,
const char *set, const char *reset, const char *ws,
const char *line, int len)
const char *set, const char *reset, const char *ws,
const char *line, int len, unsigned ws_rule)
{
int col0 = nparents;
int last_tab_in_indent = -1;
@@ -502,13 +503,17 @@ static void emit_line_with_ws(int nparents,
int i;
int tail = len;
int need_highlight_leading_space = 0;
/* The line is a newly added line. Does it have funny leading
* whitespaces? In indent, SP should never precede a TAB.
/*
* The line is a newly added line. Does it have funny leading
* whitespaces? In indent, SP should never precede a TAB. In
* addition, under "indent with non tab" rule, there should not
* be more than 8 consecutive spaces.
*/
for (i = col0; i < len; i++) {
if (line[i] == '\t') {
last_tab_in_indent = i;
if (0 <= last_space_in_indent)
if ((ws_rule & WS_SPACE_BEFORE_TAB) &&
0 <= last_space_in_indent)
need_highlight_leading_space = 1;
}
else if (line[i] == ' ')
@@ -516,6 +521,13 @@ static void emit_line_with_ws(int nparents,
else
break;
}
if ((ws_rule & WS_INDENT_WITH_NON_TAB) &&
0 <= last_space_in_indent &&
last_tab_in_indent < 0 &&
8 <= (i - col0)) {
last_tab_in_indent = i;
need_highlight_leading_space = 1;
}
fputs(set, stdout);
fwrite(line, col0, 1, stdout);
fputs(reset, stdout);
@@ -540,10 +552,12 @@ static void emit_line_with_ws(int nparents,
tail = len - 1;
if (line[tail] == '\n' && i < tail)
tail--;
while (i < tail) {
if (!isspace(line[tail]))
break;
tail--;
if (ws_rule & WS_TRAILING_SPACE) {
while (i < tail) {
if (!isspace(line[tail]))
break;
tail--;
}
}
if ((i < tail && line[tail + 1] != '\n')) {
/* This has whitespace between tail+1..len */
@@ -565,7 +579,7 @@ static void emit_add_line(const char *reset, struct emit_callback *ecbdata, cons
emit_line(set, reset, line, len);
else
emit_line_with_ws(ecbdata->nparents, set, reset, ws,
line, len);
line, len, ecbdata->ws_rule);
}
static void fn_out_consume(void *priv, char *line, unsigned long len)
@@ -720,7 +734,9 @@ struct diffstat_t {
int nr;
int alloc;
struct diffstat_file {
char *from_name;
char *name;
char *print_name;
unsigned is_unmerged:1;
unsigned is_binary:1;
unsigned is_renamed:1;
@@ -741,11 +757,14 @@ static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
}
diffstat->files[diffstat->nr++] = x;
if (name_b) {
x->name = pprint_rename(name_a, name_b);
x->from_name = xstrdup(name_a);
x->name = xstrdup(name_b);
x->is_renamed = 1;
}
else
else {
x->from_name = NULL;
x->name = xstrdup(name_a);
}
return x;
}
@@ -789,6 +808,28 @@ static void show_graph(char ch, int cnt, const char *set, const char *reset)
printf("%s", reset);
}
static void fill_print_name(struct diffstat_file *file)
{
char *pname;
if (file->print_name)
return;
if (!file->is_renamed) {
struct strbuf buf;
strbuf_init(&buf, 0);
if (quote_c_style(file->name, &buf, NULL, 0)) {
pname = strbuf_detach(&buf, NULL);
} else {
pname = file->name;
strbuf_release(&buf);
}
} else {
pname = pprint_rename(file->from_name, file->name);
}
file->print_name = pname;
}
static void show_stats(struct diffstat_t* data, struct diff_options *options)
{
int i, len, add, del, total, adds = 0, dels = 0;
@@ -822,19 +863,8 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
for (i = 0; i < data->nr; i++) {
struct diffstat_file *file = data->files[i];
int change = file->added + file->deleted;
if (!file->is_renamed) { /* renames are already quoted by pprint_rename */
struct strbuf buf;
strbuf_init(&buf, 0);
if (quote_c_style(file->name, &buf, NULL, 0)) {
free(file->name);
file->name = strbuf_detach(&buf, NULL);
} else {
strbuf_release(&buf);
}
}
len = strlen(file->name);
fill_print_name(file);
len = strlen(file->print_name);
if (max_len < len)
max_len = len;
@@ -859,7 +889,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
for (i = 0; i < data->nr; i++) {
const char *prefix = "";
char *name = data->files[i]->name;
char *name = data->files[i]->print_name;
int added = data->files[i]->added;
int deleted = data->files[i]->deleted;
int name_len;
@@ -887,17 +917,17 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
printf("%s%d%s", add_c, added, reset);
printf(" bytes");
printf("\n");
goto free_diffstat_file;
continue;
}
else if (data->files[i]->is_unmerged) {
show_name(prefix, name, len, reset, set);
printf(" Unmerged\n");
goto free_diffstat_file;
continue;
}
else if (!data->files[i]->is_renamed &&
(added + deleted == 0)) {
total_files--;
goto free_diffstat_file;
continue;
}
/*
@@ -919,11 +949,7 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
show_graph('+', add, add_c, reset);
show_graph('-', del, del_c, reset);
putchar('\n');
free_diffstat_file:
free(data->files[i]->name);
free(data->files[i]);
}
free(data->files);
printf("%s %d files changed, %d insertions(+), %d deletions(-)%s\n",
set, total_files, adds, dels, reset);
}
@@ -948,11 +974,7 @@ static void show_shortstats(struct diffstat_t* data)
dels += deleted;
}
}
free(data->files[i]->name);
free(data->files[i]);
}
free(data->files);
printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
total_files, adds, dels);
}
@@ -961,6 +983,9 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
{
int i;
if (data->nr == 0)
return;
for (i = 0; i < data->nr; i++) {
struct diffstat_file *file = data->files[i];
@@ -968,19 +993,44 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
printf("-\t-\t");
else
printf("%d\t%d\t", file->added, file->deleted);
if (!file->is_renamed) {
write_name_quoted(file->name, stdout, options->line_termination);
if (options->line_termination) {
fill_print_name(file);
if (!file->is_renamed)
write_name_quoted(file->name, stdout,
options->line_termination);
else {
fputs(file->print_name, stdout);
putchar(options->line_termination);
}
} else {
fputs(file->name, stdout);
putchar(options->line_termination);
if (file->is_renamed) {
putchar('\0');
write_name_quoted(file->from_name, stdout, '\0');
}
write_name_quoted(file->name, stdout, '\0');
}
}
}
static void free_diffstat_info(struct diffstat_t *diffstat)
{
int i;
for (i = 0; i < diffstat->nr; i++) {
struct diffstat_file *f = diffstat->files[i];
if (f->name != f->print_name)
free(f->print_name);
free(f->name);
free(f->from_name);
free(f);
}
free(diffstat->files);
}
struct checkdiff_t {
struct xdiff_emit_state xm;
const char *filename;
int lineno, color_diff;
unsigned ws_rule;
};
static void checkdiff_consume(void *priv, char *line, unsigned long len)
@@ -994,13 +1044,20 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
int i, spaces = 0, space_before_tab = 0, white_space_at_end = 0;
/* check space before tab */
for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
for (i = 1; i < len; i++) {
if (line[i] == ' ')
spaces++;
if (line[i - 1] == '\t' && spaces)
space_before_tab = 1;
else if (line[i] == '\t') {
if (spaces) {
space_before_tab = 1;
break;
}
}
else
break;
}
/* check white space at line end */
/* check whitespace at line end */
if (line[len - 1] == '\n')
len--;
if (isspace(line[len - 1]))
@@ -1014,9 +1071,10 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
putchar(',');
}
if (white_space_at_end)
printf("white space at end");
printf("whitespace at end");
printf(":%s ", reset);
emit_line_with_ws(1, set, reset, ws, line, len);
emit_line_with_ws(1, set, reset, ws, line, len,
data->ws_rule);
}
data->lineno++;
@@ -1317,6 +1375,7 @@ static void builtin_diff(const char *name_a,
ecbdata.label_path = lbl;
ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1410,6 +1469,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
data.filename = name_b ? name_b : name_a;
data.lineno = 0;
data.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
data.ws_rule = whitespace_rule(data.filename);
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
@@ -2925,8 +2985,9 @@ void diff_flush(struct diff_options *options)
show_numstat(&diffstat, options);
if (output_format & DIFF_FORMAT_DIFFSTAT)
show_stats(&diffstat, options);
else if (output_format & DIFF_FORMAT_SHORTSTAT)
if (output_format & DIFF_FORMAT_SHORTSTAT)
show_shortstats(&diffstat);
free_diffstat_info(&diffstat);
separator++;
}

View File

@@ -31,11 +31,11 @@ size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
size_t delta_base_cache_limit = 16 * 1024 * 1024;
char *pager_program;
int pager_in_use;
int pager_use_color = 1;
char *editor_program;
char *excludes_file;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
/* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg;

View File

@@ -525,42 +525,23 @@ sub add_untracked_cmd {
sub parse_diff {
my ($path) = @_;
my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
my (@hunk) = { TEXT => [] };
my @colored = ();
if ($diff_use_color) {
@colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
}
my (@hunk) = { TEXT => [], DISPLAY => [] };
for (@diff) {
if (/^@@ /) {
push @hunk, { TEXT => [] };
for (my $i = 0; $i < @diff; $i++) {
if ($diff[$i] =~ /^@@ /) {
push @hunk, { TEXT => [], DISPLAY => [] };
}
push @{$hunk[-1]{TEXT}}, $_;
push @{$hunk[-1]{TEXT}}, $diff[$i];
push @{$hunk[-1]{DISPLAY}},
($diff_use_color ? $colored[$i] : $diff[$i]);
}
return @hunk;
}
sub colored_diff_hunk {
my ($text) = @_;
# return the text, so that it can be passed to print()
my @ret;
for (@$text) {
if (!$diff_use_color) {
push @ret, $_;
next;
}
if (/^\+/) {
push @ret, colored($new_color, $_);
} elsif (/^\-/) {
push @ret, colored($old_color, $_);
} elsif (/^\@/) {
push @ret, colored($fraginfo_color, $_);
} elsif (/^ /) {
push @ret, colored($normal_color, $_);
} else {
push @ret, colored($metainfo_color, $_);
}
}
return @ret;
}
sub hunk_splittable {
my ($text) = @_;
@@ -578,9 +559,11 @@ sub parse_hunk_header {
}
sub split_hunk {
my ($text) = @_;
my ($text, $display) = @_;
my @split = ();
if (!defined $display) {
$display = $text;
}
# If there are context lines in the middle of a hunk,
# it can be split, but we would need to take care of
# overlaps later.
@@ -594,16 +577,19 @@ sub split_hunk {
my $i = $hunk_start - 1;
my $this = +{
TEXT => [],
DISPLAY => [],
OLD => $o_ofs,
NEW => $n_ofs,
OCNT => 0,
NCNT => 0,
ADDDEL => 0,
POSTCTX => 0,
USE => undef,
};
while (++$i < @$text) {
my $line = $text->[$i];
my $display = $display->[$i];
if ($line =~ /^ /) {
if ($this->{ADDDEL} &&
!defined $next_hunk_start) {
@@ -615,6 +601,7 @@ sub split_hunk {
$next_hunk_start = $i;
}
push @{$this->{TEXT}}, $line;
push @{$this->{DISPLAY}}, $display;
$this->{OCNT}++;
$this->{NCNT}++;
if (defined $next_hunk_start) {
@@ -637,6 +624,7 @@ sub split_hunk {
redo OUTER;
}
push @{$this->{TEXT}}, $line;
push @{$this->{DISPLAY}}, $display;
$this->{ADDDEL}++;
if ($line =~ /^-/) {
$this->{OCNT}++;
@@ -661,9 +649,14 @@ sub split_hunk {
" +$n_ofs" .
(($n_cnt != 1) ? ",$n_cnt" : '') .
" @@\n");
my $display_head = $head;
unshift @{$hunk->{TEXT}}, $head;
if ($diff_use_color) {
$display_head = colored($fraginfo_color, $head);
}
unshift @{$hunk->{DISPLAY}}, $display_head;
}
return map { $_->{TEXT} } @split;
return @split;
}
sub find_last_o_ctx {
@@ -794,7 +787,9 @@ sub patch_update_file {
my ($ix, $num);
my $path = shift;
my ($head, @hunk) = parse_diff($path);
print colored_diff_hunk($head->{TEXT});
for (@{$head->{DISPLAY}}) {
print;
}
$num = scalar @hunk;
$ix = 0;
@@ -836,7 +831,9 @@ sub patch_update_file {
if (hunk_splittable($hunk[$ix]{TEXT})) {
$other .= '/s';
}
print colored_diff_hunk($hunk[$ix]{TEXT});
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
print colored $prompt_color, "Stage this hunk [y/n/a/d$other/?]? ";
my $line = <STDIN>;
if ($line) {
@@ -889,14 +886,12 @@ sub patch_update_file {
next;
}
elsif ($other =~ /s/ && $line =~ /^s/) {
my @split = split_hunk($hunk[$ix]{TEXT});
my @split = split_hunk($hunk[$ix]{TEXT}, $hunk[$ix]{DISPLAY});
if (1 < @split) {
print colored $header_color, "Split into ",
scalar(@split), " hunks.\n";
}
splice(@hunk, $ix, 1,
map { +{ TEXT => $_, USE => undef } }
@split);
splice (@hunk, $ix, 1, @split);
$num = scalar @hunk;
next;
}

View File

@@ -324,8 +324,23 @@ bisect_next() {
bisect_visualize() {
bisect_next_check fail
if test $# = 0
then
case "${DISPLAY+set}" in
'') set git log ;;
set) set gitk ;;
esac
else
case "$1" in
git*|tig) ;;
-*) set git log "$@" ;;
*) set git "$@" ;;
esac
fi
not=$(git for-each-ref --format='%(refname)' "refs/bisect/good-*")
eval gitk refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
eval '"$@"' refs/bisect/bad --not $not -- $(cat "$GIT_DIR/BISECT_NAMES")
}
bisect_reset() {
@@ -449,7 +464,7 @@ case "$#" in
next)
# Not sure we want "next" at the UI level anymore.
bisect_next "$@" ;;
visualize)
visualize|view)
bisect_visualize "$@" ;;
reset)
bisect_reset "$@" ;;

149
git-browse-help.sh Executable file
View File

@@ -0,0 +1,149 @@
#!/bin/sh
#
# This program launch a web browser on the html page
# describing a git command.
#
# Copyright (c) 2007 Christian Couder
# Copyright (c) 2006 Theodore Y. Ts'o
#
# This file is heavily stolen from git-mergetool.sh, by
# Theodore Y. Ts'o (thanks) that is:
#
# Copyright (c) 2006 Theodore Y. Ts'o
#
# This file is licensed under the GPL v2, or a later version
# at the discretion of Junio C Hamano or any other official
# git maintainer.
#
USAGE='[--browser=browser|--tool=browser] [cmd to display] ...'
# This must be capable of running outside of git directory, so
# the vanilla git-sh-setup should not be used.
NONGIT_OK=Yes
. git-sh-setup
# Install data.
html_dir="@@HTMLDIR@@"
test -f "$html_dir/git.html" || die "No documentation directory found."
valid_tool() {
case "$1" in
firefox | iceweasel | konqueror | w3m | links | lynx | dillo)
;; # happy
*)
return 1
;;
esac
}
init_browser_path() {
test -z "$GIT_DIR" || browser_path=`git config browser.$1.path`
test -z "$browser_path" && browser_path=$1
}
while test $# != 0
do
case "$1" in
-b|--browser*|-t|--tool*)
case "$#,$1" in
*,*=*)
browser=`expr "z$1" : 'z-[^=]*=\(.*\)'`
;;
1,*)
usage ;;
*)
browser="$2"
shift ;;
esac
;;
--)
break
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
if test -z "$browser" && test -n "$GIT_DIR"
then
for opt in "help.browser" "web.browser"
do
browser="`git config $opt`"
test -z "$browser" || break
done
if test -n "$browser" && ! valid_tool "$browser"; then
echo >&2 "git config option $opt set to unknown browser: $browser"
echo >&2 "Resetting to default..."
unset browser
fi
fi
if test -z "$browser" ; then
if test -n "$DISPLAY"; then
browser_candidates="firefox iceweasel konqueror w3m links lynx dillo"
if test "$KDE_FULL_SESSION" = "true"; then
browser_candidates="konqueror $browser_candidates"
fi
else
browser_candidates="w3m links lynx"
fi
for i in $browser_candidates; do
init_browser_path $i
if type "$browser_path" > /dev/null 2>&1; then
browser=$i
break
fi
done
test -z "$browser" && die "No known browser available."
else
valid_tool "$browser" || die "Unknown browser '$browser'."
init_browser_path "$browser"
if ! type "$browser_path" > /dev/null 2>&1; then
die "The browser $browser is not available as '$browser_path'."
fi
fi
pages=$(for p in "$@"; do echo "$html_dir/$p.html" ; done)
test -z "$pages" && pages="$html_dir/git.html"
case "$browser" in
firefox|iceweasel)
# Check version because firefox < 2.0 does not support "-new-tab".
vers=$(expr "$($browser_path -version)" : '.* \([0-9][0-9]*\)\..*')
NEWTAB='-new-tab'
test "$vers" -lt 2 && NEWTAB=''
nohup "$browser_path" $NEWTAB $pages &
;;
konqueror)
case "$(basename "$browser_path")" in
konqueror)
# It's simpler to use kfmclient to open a new tab in konqueror.
browser_path="$(echo "$browser_path" | sed -e 's/konqueror$/kfmclient/')"
type "$browser_path" > /dev/null 2>&1 || die "No '$browser_path' found."
eval "$browser_path" newTab $pages
;;
kfmclient)
eval "$browser_path" newTab $pages
;;
*)
nohup "$browser_path" $pages &
;;
esac
;;
w3m|links|lynx)
eval "$browser_path" $pages
;;
dillo)
nohup "$browser_path" $pages &
;;
esac

View File

@@ -2,13 +2,13 @@
OPTIONS_KEEPDASHDASH=t
OPTIONS_SPEC="\
git-branch [options] [<branch>] [<paths>...]
git-checkout [options] [<branch>] [<paths>...]
--
b= create a new branch started at <branch>
l create the new branchs reflog
track tells if the new branch should track the remote branch
l create the new branch's reflog
track arrange that the new branch tracks the remote branch
f proceed even if the index or working tree is not HEAD
m performa three-way merge on local modifications if needed
m merge local modifications into the new branch
q,quiet be quiet
"
SUBDIRECTORY_OK=Sometimes

View File

@@ -244,7 +244,10 @@ fi
# it is local
if base=$(get_repo_base "$repo"); then
repo="$base"
local=yes
if test -z "$depth"
then
local=yes
fi
fi
dir="$2"
@@ -336,7 +339,8 @@ yes)
find objects -type f -print | sed -e 1q)
# objects directory should not be empty because
# we are cloning!
test -f "$repo/$sample_file" || exit
test -f "$repo/$sample_file" ||
die "fatal: cannot clone empty repository"
if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
then
rm -f "$GIT_DIR/objects/sample"

View File

@@ -24,6 +24,7 @@ fqgitdir="$GIT_DIR"
local="`git config --bool --get instaweb.local`"
httpd="`git config --get instaweb.httpd`"
browser="`git config --get instaweb.browser`"
test -z "$browser" && browser="`git config --get web.browser`"
port=`git config --get instaweb.port`
module_path="`git config --get instaweb.modulepath`"

View File

@@ -80,6 +80,10 @@ case "${1:-.}${2:-.}${3:-.}" in
echo "ERROR: $4: Not merging symbolic link changes."
exit 1
;;
*,160000,*)
echo "ERROR: $4: Not merging conflicting submodule changes."
exit 1
;;
esac
src2=`git-unpack-file $3`

View File

@@ -152,10 +152,11 @@ merge_file () {
exit 1
fi
BACKUP="$path.BACKUP.$$"
LOCAL="$path.LOCAL.$$"
REMOTE="$path.REMOTE.$$"
BASE="$path.BASE.$$"
ext="$$$(expr "$path" : '.*\(\.[^/]*\)$')"
BACKUP="$path.BACKUP.$ext"
LOCAL="$path.LOCAL.$ext"
REMOTE="$path.REMOTE.$ext"
BASE="$path.BASE.$ext"
mv -- "$path" "$BACKUP"
cp -- "$BACKUP" "$path"

View File

@@ -367,8 +367,10 @@ if ($thread && !defined $initial_reply_to && $prompting) {
} while (!defined $_);
$initial_reply_to = $_;
$initial_reply_to =~ s/^\s+<?/</;
$initial_reply_to =~ s/>?\s+$/>/;
}
if (defined $initial_reply_to && $_ ne "") {
$initial_reply_to =~ s/^\s*<?/</;
$initial_reply_to =~ s/>?\s*$/>/;
}
if (!defined $smtp_server) {

View File

@@ -122,30 +122,37 @@ get_author_ident_from_commit () {
LANG=C LC_ALL=C sed -ne "$pick_author_script"
}
# Make sure we are in a valid repository of a vintage we understand.
if [ -z "$SUBDIRECTORY_OK" ]
# Make sure we are in a valid repository of a vintage we understand,
# if we require to be in a git repository.
if test -n "$NONGIT_OK"
then
: ${GIT_DIR=.git}
test -z "$(git rev-parse --show-cdup)" || {
exit=$?
echo >&2 "You need to run this command from the toplevel of the working tree."
exit $exit
}
if git rev-parse --git-dir >/dev/null 2>&1
then
: ${GIT_DIR=.git}
fi
else
GIT_DIR=$(git rev-parse --git-dir) || {
exit=$?
echo >&2 "Failed to find a valid git directory."
exit $exit
if [ -z "$SUBDIRECTORY_OK" ]
then
: ${GIT_DIR=.git}
test -z "$(git rev-parse --show-cdup)" || {
exit=$?
echo >&2 "You need to run this command from the toplevel of the working tree."
exit $exit
}
else
GIT_DIR=$(git rev-parse --git-dir) || {
exit=$?
echo >&2 "Failed to find a valid git directory."
exit $exit
}
fi
test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
echo >&2 "Unable to determine absolute path of git directory"
exit 1
}
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
fi
test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
echo >&2 "Unable to determine absolute path of git directory"
exit 1
}
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
# Fix some commands on Windows
case $(uname -s) in
*MINGW*)

View File

@@ -529,7 +529,7 @@ sub cmd_find_rev {
"$head history\n";
}
my $desired_revision = substr($revision_or_hash, 1);
$result = $gs->rev_db_get($desired_revision);
$result = $gs->rev_map_get($desired_revision);
} else {
my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
$result = $rev;
@@ -1128,12 +1128,12 @@ sub working_head_info {
if (defined $url && defined $rev) {
next if $max{$url} and $max{$url} < $rev;
if (my $gs = Git::SVN->find_by_url($url)) {
my $c = $gs->rev_db_get($rev);
my $c = $gs->rev_map_get($rev);
if ($c && $c eq $hash) {
close $fh; # break the pipe
return ($url, $rev, $uuid, $gs);
} else {
$max{$url} ||= $gs->rev_db_max;
$max{$url} ||= $gs->rev_map_max;
}
}
}
@@ -1234,6 +1234,8 @@ sub md5sum {
package Git::SVN;
use strict;
use warnings;
use Fcntl qw/:DEFAULT :seek/;
use constant rev_map_fmt => 'NH40';
use vars qw/$default_repo_id $default_ref_id $_no_metadata $_follow_parent
$_repack $_repack_flags $_use_svm_props $_head
$_use_svnsync_props $no_reuse_existing $_minimize_url
@@ -1362,7 +1364,7 @@ sub fetch_all {
if ($fetch) {
foreach my $p (sort keys %$fetch) {
my $gs = Git::SVN->new($fetch->{$p}, $repo_id, $p);
my $lr = $gs->rev_db_max;
my $lr = $gs->rev_map_max;
if (defined $lr) {
$base = $lr if ($lr < $base);
}
@@ -1897,38 +1899,20 @@ sub last_rev_commit {
return ($rev, $c);
}
}
my $db_path = $self->db_path;
unless (-e $db_path) {
my $map_path = $self->map_path;
unless (-e $map_path) {
($self->{last_rev}, $self->{last_commit}) = (undef, undef);
return (undef, undef);
}
my $offset = -41; # from tail
my $rl;
open my $fh, '<', $db_path or croak "$db_path not readable: $!\n";
sysseek($fh, $offset, 2); # don't care for errors
sysread($fh, $rl, 41) == 41 or return (undef, undef);
chomp $rl;
while (('0' x40) eq $rl && sysseek($fh, 0, 1) != 0) {
$offset -= 41;
sysseek($fh, $offset, 2); # don't care for errors
sysread($fh, $rl, 41) == 41 or return (undef, undef);
chomp $rl;
}
if ($c && $c ne $rl) {
die "$db_path and ", $self->refname,
" inconsistent!:\n$c != $rl\n";
}
my $rev = sysseek($fh, 0, 1) or croak $!;
$rev = ($rev - 41) / 41;
close $fh or croak $!;
($self->{last_rev}, $self->{last_commit}) = ($rev, $c);
return ($rev, $c);
my ($rev, $commit) = $self->rev_map_max(1);
($self->{last_rev}, $self->{last_commit}) = ($rev, $commit);
return ($rev, $commit);
}
sub get_fetch_range {
my ($self, $min, $max) = @_;
$max ||= $self->ra->get_latest_revnum;
$min ||= $self->rev_db_max;
$min ||= $self->rev_map_max;
(++$min, $max);
}
@@ -2073,7 +2057,7 @@ sub do_git_commit {
" was r$lr, but we are about to fetch: ",
"r$log_entry->{revision}!\n";
}
if (my $c = $self->rev_db_get($log_entry->{revision})) {
if (my $c = $self->rev_map_get($log_entry->{revision})) {
croak "$log_entry->{revision} = $c already exists! ",
"Why are we refetching it?\n";
}
@@ -2116,14 +2100,14 @@ sub do_git_commit {
die "Failed to commit, invalid sha1: $commit\n";
}
$self->rev_db_set($log_entry->{revision}, $commit, 1);
$self->rev_map_set($log_entry->{revision}, $commit, 1);
$self->{last_rev} = $log_entry->{revision};
$self->{last_commit} = $commit;
print "r$log_entry->{revision}";
if (defined $log_entry->{svm_revision}) {
print " (\@$log_entry->{svm_revision})";
$self->rev_db_set($log_entry->{svm_revision}, $commit,
$self->rev_map_set($log_entry->{svm_revision}, $commit,
0, $self->svm_uuid);
}
print " = $commit ($self->{ref_id})\n";
@@ -2465,25 +2449,44 @@ sub set_tree {
}
}
sub rebuild_from_rev_db {
my ($self, $path) = @_;
my $r = -1;
open my $fh, '<', $path or croak "open: $!";
while (<$fh>) {
length($_) == 41 or croak "inconsistent size in ($_) != 41";
chomp($_);
++$r;
next if $_ eq ('0' x 40);
$self->rev_map_set($r, $_);
print "r$r = $_\n";
}
close $fh or croak "close: $!";
unlink $path or croak "unlink: $!";
}
sub rebuild {
my ($self) = @_;
my $db_path = $self->db_path;
return if (-e $db_path && ! -z $db_path);
my $map_path = $self->map_path;
return if (-e $map_path && ! -z $map_path);
return unless ::verify_ref($self->refname.'^0');
if (-f $self->{db_root}) {
rename $self->{db_root}, $db_path or die
"rename $self->{db_root} => $db_path failed: $!\n";
my ($dir, $base) = ($db_path =~ m#^(.*?)/?([^/]+)$#);
symlink $base, $self->{db_root} or die
"symlink $base => $self->{db_root} failed: $!\n";
if ($self->use_svm_props || $self->no_metadata) {
my $rev_db = $self->rev_db_path;
$self->rebuild_from_rev_db($rev_db);
if ($self->use_svm_props) {
my $svm_rev_db = $self->rev_db_path($self->svm_uuid);
$self->rebuild_from_rev_db($svm_rev_db);
}
$self->unlink_rev_db_symlink;
return;
}
print "Rebuilding $db_path ...\n";
my ($log, $ctx) = command_output_pipe("log", '--no-color', $self->refname);
my $latest;
print "Rebuilding $map_path ...\n";
my ($log, $ctx) =
command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,
$self->refname, '--');
my $full_url = $self->full_url;
remove_username($full_url);
my $svn_uuid;
my $svn_uuid = $self->ra_uuid;
my $c;
while (<$log>) {
if ( m{^commit ($::sha1)$} ) {
@@ -2499,46 +2502,85 @@ sub rebuild {
# if we merged or otherwise started elsewhere, this is
# how we break out of it
if ((defined $svn_uuid && ($uuid ne $svn_uuid)) ||
if (($uuid ne $svn_uuid) ||
($full_url && $url && ($url ne $full_url))) {
next;
}
$latest ||= $rev;
$svn_uuid ||= $uuid;
$self->rev_db_set($rev, $c);
$self->rev_map_set($rev, $c);
print "r$rev = $c\n";
}
command_close_pipe($log, $ctx);
print "Done rebuilding $db_path\n";
print "Done rebuilding $map_path\n";
my $rev_db_path = $self->rev_db_path;
if (-f $self->rev_db_path) {
unlink $self->rev_db_path or croak "unlink: $!";
}
$self->unlink_rev_db_symlink;
}
# rev_db:
# rev_map:
# Tie::File seems to be prone to offset errors if revisions get sparse,
# it's not that fast, either. Tie::File is also not in Perl 5.6. So
# one of my favorite modules is out :< Next up would be one of the DBM
# modules, but I'm not sure which is most portable... So I'll just
# go with something that's plain-text, but still capable of
# being randomly accessed. So here's my ultra-simple fixed-width
# database. All records are 40 characters + "\n", so it's easy to seek
# to a revision: (41 * rev) is the byte offset.
# A record of 40 0s denotes an empty revision.
# And yes, it's still pretty fast (faster than Tie::File).
# modules, but I'm not sure which is most portable...
#
# This is the replacement for the rev_db format, which was too big
# and inefficient for large repositories with a lot of sparse history
# (mainly tags)
#
# The format is this:
# - 24 bytes for every record,
# * 4 bytes for the integer representing an SVN revision number
# * 20 bytes representing the sha1 of a git commit
# - No empty padding records like the old format
# (except the last record, which can be overwritten)
# - new records are written append-only since SVN revision numbers
# increase monotonically
# - lookups on SVN revision number are done via a binary search
# - Piping the file to xxd -c24 is a good way of dumping it for
# viewing or editing (piped back through xxd -r), should the need
# ever arise.
# - The last record can be padding revision with an all-zero sha1
# This is used to optimize fetch performance when using multiple
# "fetch" directives in .git/config
#
# These files are disposable unless noMetadata or useSvmProps is set
sub _rev_db_set {
sub _rev_map_set {
my ($fh, $rev, $commit) = @_;
my $offset = $rev * 41;
# assume that append is the common case:
seek $fh, 0, 2 or croak $!;
my $pos = tell $fh;
if ($pos < $offset) {
for (1 .. (($offset - $pos) / 41)) {
print $fh (('0' x 40),"\n") or croak $!;
my $size = (stat($fh))[7];
($size % 24) == 0 or croak "inconsistent size: $size";
my $wr_offset = 0;
if ($size > 0) {
sysseek($fh, -24, SEEK_END) or croak "seek: $!";
my $read = sysread($fh, my $buf, 24) or croak "read: $!";
$read == 24 or croak "read only $read bytes (!= 24)";
my ($last_rev, $last_commit) = unpack(rev_map_fmt, $buf);
if ($last_commit eq ('0' x40)) {
if ($size >= 48) {
sysseek($fh, -48, SEEK_END) or croak "seek: $!";
$read = sysread($fh, $buf, 24) or
croak "read: $!";
$read == 24 or
croak "read only $read bytes (!= 24)";
($last_rev, $last_commit) =
unpack(rev_map_fmt, $buf);
if ($last_commit eq ('0' x40)) {
croak "inconsistent .rev_map\n";
}
}
if ($last_rev >= $rev) {
croak "last_rev is higher!: $last_rev >= $rev";
}
$wr_offset = -24;
}
}
seek $fh, $offset, 0 or croak $!;
print $fh $commit,"\n" or croak $!;
sysseek($fh, $wr_offset, SEEK_END) or croak "seek: $!";
syswrite($fh, pack(rev_map_fmt, $rev, $commit), 24) == 24 or
croak "write: $!";
}
sub mkfile {
@@ -2551,10 +2593,10 @@ sub mkfile {
}
}
sub rev_db_set {
sub rev_map_set {
my ($self, $rev, $commit, $update_ref, $uuid) = @_;
length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
my $db = $self->db_path($uuid);
my $db = $self->map_path($uuid);
my $db_lock = "$db.lock";
my $sig;
if ($update_ref) {
@@ -2569,16 +2611,18 @@ sub rev_db_set {
# and we can't afford to lose it because rebuild() won't work
if ($self->use_svm_props || $self->no_metadata) {
$sync = 1;
copy($db, $db_lock) or die "rev_db_set(@_): ",
copy($db, $db_lock) or die "rev_map_set(@_): ",
"Failed to copy: ",
"$db => $db_lock ($!)\n";
} else {
rename $db, $db_lock or die "rev_db_set(@_): ",
rename $db, $db_lock or die "rev_map_set(@_): ",
"Failed to rename: ",
"$db => $db_lock ($!)\n";
}
open my $fh, '+<', $db_lock or die "Couldn't open $db_lock: $!\n";
_rev_db_set($fh, $rev, $commit);
sysopen(my $fh, $db_lock, O_RDWR | O_CREAT)
or croak "Couldn't open $db_lock: $!\n";
_rev_map_set($fh, $rev, $commit);
if ($sync) {
$fh->flush or die "Couldn't flush $db_lock: $!\n";
$fh->sync or die "Couldn't sync $db_lock: $!\n";
@@ -2589,7 +2633,7 @@ sub rev_db_set {
command_noisy('update-ref', '-m', "r$rev",
$self->refname, $commit);
}
rename $db_lock, $db or die "rev_db_set(@_): ", "Failed to rename: ",
rename $db_lock, $db or die "rev_map_set(@_): ", "Failed to rename: ",
"$db_lock => $db ($!)\n";
delete $LOCKFILES{$db_lock};
if ($update_ref) {
@@ -2599,29 +2643,76 @@ sub rev_db_set {
}
}
sub rev_db_max {
my ($self) = @_;
# If want_commit, this will return an array of (rev, commit) where
# commit _must_ be a valid commit in the archive.
# Otherwise, it'll return the max revision (whether or not the
# commit is valid or just a 0x40 placeholder).
sub rev_map_max {
my ($self, $want_commit) = @_;
$self->rebuild;
my $db_path = $self->db_path;
my @stat = stat $db_path or return 0;
($stat[7] % 41) == 0 or die "$db_path inconsistent size: $stat[7]\n";
my $max = $stat[7] / 41;
(($max > 0) ? $max - 1 : 0);
my $map_path = $self->map_path;
stat $map_path or return $want_commit ? (0, undef) : 0;
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
my $size = (stat($fh))[7];
($size % 24) == 0 or croak "inconsistent size: $size";
if ($size == 0) {
close $fh or croak "close: $!";
return $want_commit ? (0, undef) : 0;
}
sysseek($fh, -24, SEEK_END) or croak "seek: $!";
sysread($fh, my $buf, 24) == 24 or croak "read: $!";
my ($r, $c) = unpack(rev_map_fmt, $buf);
if ($want_commit && $c eq ('0' x40)) {
if ($size < 48) {
return $want_commit ? (0, undef) : 0;
}
sysseek($fh, -48, SEEK_END) or croak "seek: $!";
sysread($fh, $buf, 24) == 24 or croak "read: $!";
($r, $c) = unpack(rev_map_fmt, $buf);
if ($c eq ('0'x40)) {
croak "Penultimate record is all-zeroes in $map_path";
}
}
close $fh or croak "close: $!";
$want_commit ? ($r, $c) : $r;
}
sub rev_db_get {
sub rev_map_get {
my ($self, $rev, $uuid) = @_;
my $ret;
my $offset = $rev * 41;
my $db_path = $self->db_path($uuid);
return undef unless -e $db_path;
open my $fh, '<', $db_path or croak $!;
if (sysseek($fh, $offset, 0) == $offset) {
my $read = sysread($fh, $ret, 40);
$ret = undef if ($read != 40 || $ret eq ('0'x40));
my $map_path = $self->map_path($uuid);
return undef unless -e $map_path;
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
my $size = (stat($fh))[7];
($size % 24) == 0 or croak "inconsistent size: $size";
if ($size == 0) {
close $fh or croak "close: $fh";
return undef;
}
close $fh or croak $!;
$ret;
my ($l, $u) = (0, $size - 24);
my ($r, $c, $buf);
while ($l <= $u) {
my $i = int(($l/24 + $u/24) / 2) * 24;
sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
sysread($fh, my $buf, 24) == 24 or croak "read: $!";
my ($r, $c) = unpack('NH40', $buf);
if ($r < $rev) {
$l = $i + 24;
} elsif ($r > $rev) {
$u = $i - 24;
} else { # $r == $rev
close($fh) or croak "close: $!";
return $c eq ('0' x 40) ? undef : $c;
}
}
close($fh) or croak "close: $!";
undef;
}
# Finds the first svn revision that exists on (if $eq_ok is true) or
@@ -2633,7 +2724,7 @@ sub find_rev_before {
--$rev unless $eq_ok;
$min_rev ||= 1;
while ($rev >= $min_rev) {
if (my $c = $self->rev_db_get($rev)) {
if (my $c = $self->rev_map_get($rev)) {
return ($rev, $c);
}
--$rev;
@@ -2648,9 +2739,9 @@ sub find_rev_before {
sub find_rev_after {
my ($self, $rev, $eq_ok, $max_rev) = @_;
++$rev unless $eq_ok;
$max_rev ||= $self->rev_db_max();
$max_rev ||= $self->rev_map_max;
while ($rev <= $max_rev) {
if (my $c = $self->rev_db_get($rev)) {
if (my $c = $self->rev_map_get($rev)) {
return ($rev, $c);
}
++$rev;
@@ -2673,13 +2764,32 @@ sub _new {
bless {
ref_id => $ref_id, dir => $dir, index => "$dir/index",
path => $path, config => "$ENV{GIT_DIR}/svn/config",
db_root => "$dir/.rev_db", repo_id => $repo_id }, $class;
map_root => "$dir/.rev_map", repo_id => $repo_id }, $class;
}
sub db_path {
# for read-only access of old .rev_db formats
sub unlink_rev_db_symlink {
my ($self) = @_;
my $link = $self->rev_db_path;
$link =~ s/\.[\w-]+$// or croak "missing UUID at the end of $link";
if (-l $link) {
unlink $link or croak "unlink: $link failed!";
}
}
sub rev_db_path {
my ($self, $uuid) = @_;
my $db_path = $self->map_path($uuid);
$db_path =~ s{/\.rev_map\.}{/\.rev_db\.}
or croak "map_path: $db_path does not contain '/.rev_map.' !";
$db_path;
}
# the new replacement for .rev_db
sub map_path {
my ($self, $uuid) = @_;
$uuid ||= $self->ra_uuid;
"$self->{db_root}.$uuid";
"$self->{map_root}.$uuid";
}
sub uri_encode {
@@ -3763,7 +3873,7 @@ sub gs_fetch_loop_common {
foreach my $gs ($self->match_globs(\%exists, $paths,
$globs, $r)) {
if ($gs->rev_db_max >= $r) {
if ($gs->rev_map_max >= $r) {
next;
}
next unless $gs->match_paths($paths, $r);
@@ -3792,8 +3902,9 @@ sub gs_fetch_loop_common {
# pre-fill the .rev_db since it'll eventually get filled in
# with '0' x40 if something new gets committed
foreach my $gs (@$gsv) {
next if defined $gs->rev_db_get($max);
$gs->rev_db_set($max, 0 x40);
next if $gs->rev_map_max >= $max;
next if defined $gs->rev_map_get($max);
$gs->rev_map_set($max, 0 x40);
}
foreach my $g (@$globs) {
my $k = "svn-remote.$g->{remote}.$g->{t}-maxRev";
@@ -3969,39 +4080,7 @@ sub cmt_showable {
}
sub log_use_color {
return 1 if $color;
my ($dc, $dcvar);
$dcvar = 'color.diff';
$dc = `git-config --get $dcvar`;
if ($dc eq '') {
# nothing at all; fallback to "diff.color"
$dcvar = 'diff.color';
$dc = `git-config --get $dcvar`;
}
chomp($dc);
if ($dc eq 'auto') {
my $pc;
$pc = `git-config --get color.pager`;
if ($pc eq '') {
# does not have it -- fallback to pager.color
$pc = `git-config --bool --get pager.color`;
}
else {
$pc = `git-config --bool --get color.pager`;
if ($?) {
$pc = 'false';
}
}
chomp($pc);
if (-t *STDOUT || (defined $pager && $pc eq 'true')) {
return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
}
return 0;
}
return 0 if $dc eq 'never';
return 1 if $dc eq 'always';
chomp($dc = `git-config --bool --get $dcvar`);
return ($dc eq 'true');
return $color || Git->repository->get_colorbool('color.diff');
}
sub git_svn_log_cmd {
@@ -4030,7 +4109,7 @@ sub git_svn_log_cmd {
push @cmd, @log_opts;
if (defined $r_max && $r_max == $r_min) {
push @cmd, '--max-count=1';
if (my $c = $gs->rev_db_get($r_max)) {
if (my $c = $gs->rev_map_get($r_max)) {
push @cmd, $c;
}
} elsif (defined $r_max) {
@@ -4060,6 +4139,7 @@ sub config_pager {
} elsif (length $pager == 0 || $pager eq 'cat') {
$pager = undef;
}
$ENV{GIT_PAGER_IN_USE} = defined($pager);
}
sub run_pager {
@@ -4311,6 +4391,16 @@ package Git::SVN::Migration;
# --use-separate-remotes option in git-clone (now default)
# - we do not automatically migrate to this (following
# the example set by core git)
#
# v5 layout: .rev_db.$UUID => .rev_map.$UUID
# - newer, more-efficient format that uses 24-bytes per record
# with no filler space.
# - use xxd -c24 < .rev_map.$UUID to view and debug
# - This is a one-way migration, repositories updated to the
# new format will not be able to use old git-svn without
# rebuilding the .rev_db. Rebuilding the rev_db is not
# possible if noMetadata or useSvmProps are set; but should
# be no problem for users that use the (sensible) defaults.
use strict;
use warnings;
use Carp qw/croak/;

View File

@@ -1,7 +1,5 @@
# Pass --without docs to rpmbuild if you don't want the documentation
%define python_path /usr/bin/python
Name: git
Version: @@VERSION@@
Release: 1%{?dist}
@@ -85,20 +83,20 @@ BuildRequires: perl(Error)
%description -n perl-Git
Perl interface to Git
%define path_settings ETC_GITCONFIG=/etc/gitconfig prefix=%{_prefix} mandir=%{_mandir} htmldir=%{_docdir}/%{name}-core-%{version}
%prep
%setup -q
%build
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" \
ETC_GITCONFIG=/etc/gitconfig \
prefix=%{_prefix} all %{!?_without_docs: doc}
%{path_settings} \
all %{!?_without_docs: doc}
%install
rm -rf $RPM_BUILD_ROOT
make %{_smp_mflags} CFLAGS="$RPM_OPT_FLAGS" DESTDIR=$RPM_BUILD_ROOT \
prefix=%{_prefix} mandir=%{_mandir} \
ETC_GITCONFIG=/etc/gitconfig \
PYTHON_PATH=%{python_path} \
%{path_settings} \
INSTALLDIRS=vendor install %{!?_without_docs: install-doc}
find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
find $RPM_BUILD_ROOT -type f -name '*.bs' -empty -exec rm -f {} ';'
@@ -174,6 +172,9 @@ rm -rf $RPM_BUILD_ROOT
%{!?_without_docs: %doc Documentation/technical}
%changelog
* Wed Dec 12 2007 Junio C Hamano <gitster@pobox.com>
- Adjust htmldir to point at /usr/share/doc/git-core-$version/
* Sun Jul 15 2007 Sean Estabrooks <seanlkml@sympatico.ca>
- Removed p4import.

86
help.c
View File

@@ -240,7 +240,58 @@ void list_common_cmds_help(void)
}
}
static const char *cmd_to_page(const char *git_cmd)
{
if (!git_cmd)
return "git";
else if (!prefixcmp(git_cmd, "git"))
return git_cmd;
else {
int page_len = strlen(git_cmd) + 4;
char *p = xmalloc(page_len + 1);
strcpy(p, "git-");
strcpy(p + 4, git_cmd);
p[page_len] = 0;
return p;
}
}
static void setup_man_path(void)
{
struct strbuf new_path;
const char *old_path = getenv("MANPATH");
strbuf_init(&new_path, 0);
/* We should always put ':' after our path. If there is no
* old_path, the ':' at the end will let 'man' to try
* system-wide paths after ours to find the manual page. If
* there is old_path, we need ':' as delimiter. */
strbuf_addstr(&new_path, GIT_MAN_PATH);
strbuf_addch(&new_path, ':');
if (old_path)
strbuf_addstr(&new_path, old_path);
setenv("MANPATH", new_path.buf, 1);
strbuf_release(&new_path);
}
static void show_man_page(const char *git_cmd)
{
const char *page = cmd_to_page(git_cmd);
setup_man_path();
execlp("man", "man", page, NULL);
}
static void show_info_page(const char *git_cmd)
{
const char *page = cmd_to_page(git_cmd);
setenv("INFOPATH", GIT_INFO_PATH, 1);
execlp("info", "info", "gitman", page, NULL);
}
static void show_html_page(const char *git_cmd)
{
#ifdef __MINGW32__
{
@@ -266,20 +317,8 @@ static void show_man_page(const char *git_cmd)
ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
}
#else
const char *page;
if (!prefixcmp(git_cmd, "git"))
page = git_cmd;
else {
int page_len = strlen(git_cmd) + 4;
char *p = xmalloc(page_len + 1);
strcpy(p, "git-");
strcpy(p + 4, git_cmd);
p[page_len] = 0;
page = p;
}
execlp("man", "man", page, NULL);
const char *page = cmd_to_page(git_cmd);
execl_git_cmd("browse-help", page, NULL);
#endif
}
@@ -297,22 +336,33 @@ int cmd_version(int argc, const char **argv, const char *prefix)
int cmd_help(int argc, const char **argv, const char *prefix)
{
const char *help_cmd = argc > 1 ? argv[1] : NULL;
const char *help_cmd = argv[1];
if (!help_cmd) {
if (argc < 2) {
printf("usage: %s\n\n", git_usage_string);
list_common_cmds_help();
exit(0);
}
else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
printf("usage: %s\n\n", git_usage_string);
list_commands();
exit(0);
}
else if (!strcmp(help_cmd, "--web") || !strcmp(help_cmd, "-w")) {
show_html_page(argc > 2 ? argv[2] : NULL);
}
else if (!strcmp(help_cmd, "--info") || !strcmp(help_cmd, "-i")) {
show_info_page(argc > 2 ? argv[2] : NULL);
}
else
#ifdef __MINGW32__
show_html_page(help_cmd);
#else
show_man_page(help_cmd);
#endif
return 0;
}

View File

@@ -1275,8 +1275,6 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
char *ep;
char timeout_header[25];
struct remote_lock *lock = NULL;
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
struct curl_slist *dav_headers = NULL;
struct xml_ctx ctx;
@@ -1345,6 +1343,8 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
if (start_active_slot(slot)) {
run_active_slot(slot);
if (results.curl_result == CURLE_OK) {
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
ctx.name = xcalloc(10, 1);
ctx.len = 0;
ctx.cdata = NULL;
@@ -1363,6 +1363,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
XML_GetErrorCode(parser)));
lock->timeout = -1;
}
XML_ParserFree(parser);
}
} else {
fprintf(stderr, "Unable to start LOCK request\n");
@@ -1525,8 +1526,6 @@ static void remote_ls(const char *path, int flags,
struct buffer out_buffer;
char *in_data;
char *out_data;
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
struct curl_slist *dav_headers = NULL;
struct xml_ctx ctx;
struct remote_ls_ctx ls;
@@ -1569,6 +1568,8 @@ static void remote_ls(const char *path, int flags,
if (start_active_slot(slot)) {
run_active_slot(slot);
if (results.curl_result == CURLE_OK) {
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
ctx.name = xcalloc(10, 1);
ctx.len = 0;
ctx.cdata = NULL;
@@ -1587,6 +1588,7 @@ static void remote_ls(const char *path, int flags,
XML_ErrorString(
XML_GetErrorCode(parser)));
}
XML_ParserFree(parser);
}
} else {
fprintf(stderr, "Unable to start PROPFIND request\n");
@@ -1620,8 +1622,6 @@ static int locking_available(void)
struct buffer out_buffer;
char *in_data;
char *out_data;
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
struct curl_slist *dav_headers = NULL;
struct xml_ctx ctx;
int lock_flags = 0;
@@ -1658,6 +1658,8 @@ static int locking_available(void)
if (start_active_slot(slot)) {
run_active_slot(slot);
if (results.curl_result == CURLE_OK) {
XML_Parser parser = XML_ParserCreate(NULL);
enum XML_Status result;
ctx.name = xcalloc(10, 1);
ctx.len = 0;
ctx.cdata = NULL;
@@ -1676,6 +1678,7 @@ static int locking_available(void)
XML_GetErrorCode(parser)));
lock_flags = 0;
}
XML_ParserFree(parser);
}
} else {
fprintf(stderr, "Unable to start PROPFIND request\n");

28
http.c
View File

@@ -4,31 +4,31 @@ int data_received;
int active_requests = 0;
#ifdef USE_CURL_MULTI
int max_requests = -1;
CURLM *curlm;
static int max_requests = -1;
static CURLM *curlm;
#endif
#ifndef NO_CURL_EASY_DUPHANDLE
CURL *curl_default;
static CURL *curl_default;
#endif
char curl_errorstr[CURL_ERROR_SIZE];
int curl_ssl_verify = -1;
char *ssl_cert = NULL;
static int curl_ssl_verify = -1;
static char *ssl_cert = NULL;
#if LIBCURL_VERSION_NUM >= 0x070902
char *ssl_key = NULL;
static char *ssl_key = NULL;
#endif
#if LIBCURL_VERSION_NUM >= 0x070908
char *ssl_capath = NULL;
static char *ssl_capath = NULL;
#endif
char *ssl_cainfo = NULL;
long curl_low_speed_limit = -1;
long curl_low_speed_time = -1;
int curl_ftp_no_epsv = 0;
char *curl_http_proxy = NULL;
static char *ssl_cainfo = NULL;
static long curl_low_speed_limit = -1;
static long curl_low_speed_time = -1;
static int curl_ftp_no_epsv = 0;
static char *curl_http_proxy = NULL;
struct curl_slist *pragma_header;
static struct curl_slist *pragma_header;
struct active_request_slot *active_queue_head = NULL;
static struct active_request_slot *active_queue_head = NULL;
size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb,
struct buffer *buffer)

18
http.h
View File

@@ -80,24 +80,6 @@ extern void http_cleanup(void);
extern int data_received;
extern int active_requests;
#ifndef NO_CURL_EASY_DUPHANDLE
extern CURL *curl_default;
#endif
extern char curl_errorstr[CURL_ERROR_SIZE];
extern int curl_ssl_verify;
extern char *ssl_cert;
#if LIBCURL_VERSION_NUM >= 0x070902
extern char *ssl_key;
#endif
#if LIBCURL_VERSION_NUM >= 0x070908
extern char *ssl_capath;
#endif
extern char *ssl_cainfo;
extern long curl_low_speed_limit;
extern long curl_low_speed_time;
extern struct curl_slist *pragma_header;
extern struct curl_slist *no_range_header;
#endif /* HTTP_H */

30
ident.c
View File

@@ -182,14 +182,15 @@ static const char *env_hint =
"Omit --global to set the identity only in this repository.\n"
"\n";
static const char *fmt_ident_1(const char *name, const char *email,
const char *date_str, int flag)
const char *fmt_ident(const char *name, const char *email,
const char *date_str, int flag)
{
static char buffer[1000];
char date[50];
int i;
int error_on_no_name = !!(flag & 01);
int name_addr_only = !!(flag & 02);
int error_on_no_name = (flag & IDENT_ERROR_ON_NO_NAME);
int warn_on_no_name = (flag & IDENT_WARN_ON_NO_NAME);
int name_addr_only = (flag & IDENT_NO_DATE);
setup_ident();
if (!name)
@@ -200,12 +201,12 @@ static const char *fmt_ident_1(const char *name, const char *email,
if (!*name) {
struct passwd *pw;
if (0 <= error_on_no_name &&
if ((warn_on_no_name || error_on_no_name) &&
name == git_default_name && env_hint) {
fprintf(stderr, env_hint, au_env, co_env);
env_hint = NULL; /* warn only once, for "git-var -l" */
}
if (0 < error_on_no_name)
if (error_on_no_name)
die("empty ident %s <%s> not allowed", name, email);
pw = getpwuid(getuid());
if (!pw)
@@ -234,30 +235,23 @@ static const char *fmt_ident_1(const char *name, const char *email,
return buffer;
}
const char *fmt_ident(const char *name, const char *email,
const char *date_str, int error_on_no_name)
{
int flag = (error_on_no_name ? 01 : 0);
return fmt_ident_1(name, email, date_str, flag);
}
const char *fmt_name(const char *name, const char *email)
{
return fmt_ident_1(name, email, NULL, 03);
return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE);
}
const char *git_author_info(int error_on_no_name)
const char *git_author_info(int flag)
{
return fmt_ident(getenv("GIT_AUTHOR_NAME"),
getenv("GIT_AUTHOR_EMAIL"),
getenv("GIT_AUTHOR_DATE"),
error_on_no_name);
flag);
}
const char *git_committer_info(int error_on_no_name)
const char *git_committer_info(int flag)
{
return fmt_ident(getenv("GIT_COMMITTER_NAME"),
getenv("GIT_COMMITTER_EMAIL"),
getenv("GIT_COMMITTER_DATE"),
error_on_no_name);
flag);
}

View File

@@ -42,9 +42,10 @@ int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev
continue;
if (right_bracket == left_bracket + 1)
continue;
for (end_of_name = left_bracket; end_of_name != buffer
&& isspace(end_of_name[-1]); end_of_name--)
/* keep on looking */
for (end_of_name = left_bracket;
end_of_name != buffer && isspace(end_of_name[-1]);
end_of_name--)
; /* keep on looking */
if (end_of_name == buffer)
continue;
name = xmalloc(end_of_name - buffer + 1);

View File

@@ -1046,14 +1046,16 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
free(result_buf.ptr);
result.clean = (merge_status == 0);
} else {
if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode)))
die("cannot merge modes?");
} else if (S_ISGITLINK(a->mode)) {
result.clean = 0;
hashcpy(result.sha, a->sha1);
} else if (S_ISLNK(a->mode)) {
hashcpy(result.sha, a->sha1);
if (!sha_eq(a->sha1, b->sha1))
result.clean = 0;
} else {
die("unsupported object type in the tree");
}
}

25
pager.c
View File

@@ -1,10 +1,12 @@
#include "cache.h"
/*
* This is split up from the rest of git so that we might do
* something different on Windows, for example.
* This is split up from the rest of git so that we can do
* something different on Windows.
*/
static int spawned_pager;
#ifndef __MINGW32__
static void run_pager(const char *pager)
{
@@ -24,12 +26,12 @@ static void run_pager(const char *pager)
#else
#include "run-command.h"
const char *pager_argv[] = { "sh", "-c", NULL, NULL };
static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
static struct child_process pager_process = {
.argv = pager_argv,
.in = -1
};
static void collect_pager(void)
static void wait_for_pager(void)
{
fflush(stdout);
close(1); /* signals EOF to pager */
@@ -59,7 +61,7 @@ void setup_pager(void)
else if (!*pager || !strcmp(pager, "cat"))
return;
pager_in_use = 1; /* means we are emitting to terminal */
spawned_pager = 1; /* means we are emitting to terminal */
#ifndef __MINGW32__
if (pipe(fd) < 0)
@@ -99,6 +101,17 @@ void setup_pager(void)
close(pager_process.in);
/* this makes sure that the parent terminates after the pager */
atexit(collect_pager);
atexit(wait_for_pager);
#endif
}
int pager_in_use(void)
{
const char *env;
if (spawned_pager)
return 1;
env = getenv("GIT_PAGER_IN_USE");
return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
}

2
path.c
View File

@@ -75,11 +75,13 @@ int git_mkstemp(char *path, size_t len, const char *template)
size_t n;
tmp = getenv("TMPDIR");
#ifdef __MINGW32__
/* on Windows it is TMP and TEMP */
if (!tmp)
tmp = getenv("TMP");
if (!tmp)
tmp = getenv("TEMP");
#endif
if (!tmp)
tmp = "/tmp";
n = snprintf(path, len, "%s/%s", tmp, template);

View File

@@ -17,9 +17,6 @@ if ($@ || $Error::VERSION < 0.15009) {
$pm{'private-Error.pm'} = '$(INST_LIBDIR)/Error.pm';
}
my %extra;
$extra{DESTDIR} = $ENV{DESTDIR} if $ENV{DESTDIR};
# redirect stdout, otherwise the message "Writing perl.mak for Git"
# disrupts the output for the target 'instlibdir'
open STDOUT, ">&STDERR";
@@ -29,6 +26,5 @@ WriteMakefile(
VERSION_FROM => 'Git.pm',
PM => \%pm,
MAKEFILE => 'perl.mak',
INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3',
%extra
INSTALLSITEMAN3DIR => '$(SITEPREFIX)/share/man/man3'
);

2
refs.c
View File

@@ -1094,7 +1094,7 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
adjust_shared_perm(log_file);
msglen = msg ? strlen(msg) : 0;
committer = git_committer_info(-1);
committer = git_committer_info(0);
maxlen = strlen(committer) + msglen + 100;
logrec = xmalloc(maxlen);
len = sprintf(logrec, "%s %s %s\n",

View File

@@ -139,6 +139,18 @@ void add_pending_object(struct rev_info *revs, struct object *obj, const char *n
add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
}
void add_head_to_pending(struct rev_info *revs)
{
unsigned char sha1[20];
struct object *obj;
if (get_sha1("HEAD", sha1))
return;
obj = parse_object(sha1);
if (!obj)
return;
add_pending_object(revs, obj, "HEAD");
}
static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
{
struct object *object;

View File

@@ -130,6 +130,8 @@ extern void add_object(struct object *obj,
extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
extern void add_head_to_pending(struct rev_info *);
enum commit_action {
commit_ignore,
commit_show,

11
setup.c
View File

@@ -82,21 +82,23 @@ const char *prefix_path(const char *prefix, int len, const char *path)
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
static char path[PATH_MAX];
char *p;
#ifndef __MINGW32__
if (!pfx || !*pfx || is_absolute_path(arg))
return arg;
memcpy(path, pfx, pfx_len);
strcpy(path + pfx_len, arg);
#else
/* don't add prefix to absolute paths */
char *p;
/* don't add prefix to absolute paths, but still replace '\' by '/' */
if (is_absolute_path(arg))
pfx_len = 0;
else
#endif
memcpy(path, pfx, pfx_len);
memcpy(path, pfx, pfx_len);
strcpy(path + pfx_len, arg);
for (p = path + pfx_len; *p; p++)
if (*p == '\\')
*p = '/';
#endif
return path;
}
@@ -427,7 +429,6 @@ int check_repository_format(void)
const char *setup_git_directory(void)
{
const char *retval = setup_git_directory_gently(NULL);
check_repository_format();
/* If the work tree is not the default one, recompute prefix */
if (inside_work_tree < 0) {

View File

@@ -83,20 +83,18 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)
return 0;
}
/* returns the number of chars to skip to first component */
static inline int is_path_absolute(const char *path)
static inline int offset_1st_component(const char *path)
{
#ifdef __MINGW32__
if (isalpha(path[0]) && path[1] == ':')
return 2 + (path[2] == '/');
/* TODO: C:dir/file 'relative' paths are not catered for */
#endif
return *path == '/';
}
int safe_create_leading_directories(char *path)
{
char *pos = path + is_path_absolute(path);
char *pos = path + offset_1st_component(path);
struct stat st;
while (pos) {

View File

@@ -82,3 +82,29 @@ stop_httpd () {
test -z "$SVN_HTTPD_PORT" && return
"$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k stop
}
convert_to_rev_db () {
perl -w -- - "$@" <<\EOF
use strict;
@ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
open my $rd, '<', $ARGV[0] or die "$!: couldn't open: $ARGV[0]";
my $size = (stat($rd))[7];
($size % 24) == 0 or die "Inconsistent size: $size";
while (sysread($rd, my $buf, 24) == 24) {
my ($r, $c) = unpack('NH40', $buf);
my $offset = $r * 41;
seek $wr, 0, 2 or die $!;
my $pos = tell $wr;
if ($pos < $offset) {
for (1 .. (($offset - $pos) / 41)) {
print $wr (('0' x 40),"\n") or die $!;
}
}
seek $wr, $offset, 0 or die $!;
print $wr $c,"\n" or die $!;
}
close $wr or die $!;
close $rd or die $!;
EOF
}

View File

@@ -117,4 +117,13 @@ EOF
git diff -b > out
test_expect_success 'another test, with -b' 'git diff expect out'
test_expect_success 'check mixed spaces and tabs in indent' '
# This is indented with SP HT SP.
echo " foo();" > x &&
git diff --check | grep "space before tab"
'
test_done

123
t/t4019-diff-wserror.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/bin/sh
test_description='diff whitespace error detection'
. ./test-lib.sh
test_expect_success setup '
git config diff.color.whitespace "blue reverse" &&
>F &&
git add F &&
echo " Eight SP indent" >>F &&
echo " HT and SP indent" >>F &&
echo "With trailing SP " >>F &&
echo "No problem" >>F
'
blue_grep='7;34m' ;# ESC [ 7 ; 3 4 m
test_expect_success default '
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
grep With error >/dev/null &&
grep No normal >/dev/null
'
test_expect_success 'without -trail' '
git config core.whitespace -trail
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
grep With normal >/dev/null &&
grep No normal >/dev/null
'
test_expect_success 'without -trail (attribute)' '
git config --unset core.whitespace
echo "F whitespace=-trail" >.gitattributes
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight normal >/dev/null &&
grep HT error >/dev/null &&
grep With normal >/dev/null &&
grep No normal >/dev/null
'
test_expect_success 'without -space' '
rm -f .gitattributes
git config core.whitespace -space
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
grep With error >/dev/null &&
grep No normal >/dev/null
'
test_expect_success 'without -space (attribute)' '
git config --unset core.whitespace
echo "F whitespace=-space" >.gitattributes
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight normal >/dev/null &&
grep HT normal >/dev/null &&
grep With error >/dev/null &&
grep No normal >/dev/null
'
test_expect_success 'with indent-non-tab only' '
rm -f .gitattributes
git config core.whitespace indent,-trailing,-space
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight error >/dev/null &&
grep HT normal >/dev/null &&
grep With normal >/dev/null &&
grep No normal >/dev/null
'
test_expect_success 'with indent-non-tab only (attribute)' '
git config --unset core.whitespace
echo "F whitespace=indent,-trailing,-space" >.gitattributes
git diff --color >output
grep "$blue_grep" output >error
grep -v "$blue_grep" output >normal
grep Eight error >/dev/null &&
grep HT normal >/dev/null &&
grep With normal >/dev/null &&
grep No normal >/dev/null
'
test_done

151
t/t4124-apply-ws-rule.sh Executable file
View File

@@ -0,0 +1,151 @@
#!/bin/sh
test_description='core.whitespace rules and git-apply'
. ./test-lib.sh
prepare_test_file () {
# A line that has character X is touched iff RULE is in effect:
# X RULE
# ! trailing-space
# @ space-before-tab
# # indent-with-non-tab
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
An_SP in an ordinary line>and a HT.
>A HT.
_>A SP and a HT (@).
_>_A SP, a HT and a SP (@).
_______Seven SP.
________Eight SP (#).
_______>Seven SP and a HT (@).
________>Eight SP and a HT (@#).
_______>_Seven SP, a HT and a SP (@).
________>_Eight SP, a HT and a SP (@#).
_______________Fifteen SP (#).
_______________>Fifteen SP and a HT (@#).
________________Sixteen SP (#).
________________>Sixteen SP and a HT (@#).
_____a__Five SP, a non WS, two SP.
A line with a (!) trailing SP_
A line with a (!) trailing HT>
EOF
}
apply_patch () {
>target &&
sed -e "s|\([ab]\)/file|\1/target|" <patch |
git apply "$@"
}
test_fix () {
# fix should not barf
apply_patch --whitespace=fix || return 1
# find touched lines
diff file target | sed -n -e "s/^> //p" >fixed
# the changed lines are all expeced to change
fixed_cnt=$(wc -l <fixed)
case "$1" in
'') expect_cnt=$fixed_cnt ;;
?*) expect_cnt=$(grep "[$1]" <fixed | wc -l) ;;
esac
test $fixed_cnt -eq $expect_cnt || return 1
# and we are not missing anything
case "$1" in
'') expect_cnt=0 ;;
?*) expect_cnt=$(grep "[$1]" <file | wc -l) ;;
esac
test $fixed_cnt -eq $expect_cnt || return 1
# Get the patch actually applied
git diff-files -p target >fixed-patch
test -s fixed-patch && return 0
# Make sure it is complaint-free
>target
git apply --whitespace=error-all <fixed-patch
}
test_expect_success setup '
>file &&
git add file &&
prepare_test_file >file &&
git diff-files -p >patch &&
>target &&
git add target
'
test_expect_success 'whitespace=nowarn, default rule' '
apply_patch --whitespace=nowarn &&
diff file target
'
test_expect_success 'whitespace=warn, default rule' '
apply_patch --whitespace=warn &&
diff file target
'
test_expect_success 'whitespace=error-all, default rule' '
apply_patch --whitespace=error-all && return 1
test -s target && return 1
: happy
'
test_expect_success 'whitespace=error-all, no rule' '
git config core.whitespace -trailing,-space-before,-indent &&
apply_patch --whitespace=error-all &&
diff file target
'
test_expect_success 'whitespace=error-all, no rule (attribute)' '
git config --unset core.whitespace &&
echo "target -whitespace" >.gitattributes &&
apply_patch --whitespace=error-all &&
diff file target
'
for t in - ''
do
case "$t" in '') tt='!' ;; *) tt= ;; esac
for s in - ''
do
case "$s" in '') ts='@' ;; *) ts= ;; esac
for i in - ''
do
case "$i" in '') ti='#' ;; *) ti= ;; esac
rule=${t}trailing,${s}space,${i}indent
rm -f .gitattributes
test_expect_success "rule=$rule" '
git config core.whitespace "$rule" &&
test_fix "$tt$ts$ti"
'
test_expect_success "rule=$rule (attributes)" '
git config --unset core.whitespace &&
echo "target whitespace=$rule" >.gitattributes &&
test_fix "$tt$ts$ti"
'
done
done
done
test_done

View File

@@ -641,6 +641,46 @@ test_expect_success 'creating a signed tag with -m message should succeed' '
git diff expect actual
'
get_tag_header u-signed-tag $commit commit $time >expect
echo 'Another message' >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect
test_expect_success 'sign with a given key id' '
git tag -u committer@example.com -m "Another message" u-signed-tag &&
get_tag_msg u-signed-tag >actual &&
git diff expect actual
'
test_expect_success 'sign with an unknown id (1)' '
! git tag -u author@example.com -m "Another message" o-signed-tag
'
test_expect_success 'sign with an unknown id (2)' '
! git tag -u DEADBEEF -m "Another message" o-signed-tag
'
cat >fakeeditor <<'EOF'
#!/bin/sh
test -n "$1" && exec >"$1"
echo A signed tag message
echo from a fake editor.
EOF
chmod +x fakeeditor
get_tag_header implied-sign $commit commit $time >expect
./fakeeditor >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect
test_expect_success '-u implies signed tag' '
GIT_EDITOR=./fakeeditor git-tag -u CDDE430D implied-sign &&
get_tag_msg implied-sign >actual &&
git diff expect actual
'
cat >sigmsgfile <<EOF
Another signed tag
message in a file.
@@ -668,13 +708,6 @@ test_expect_success 'creating a signed tag with -F - should succeed' '
git diff expect actual
'
cat >fakeeditor <<'EOF'
#!/bin/sh
test -n "$1" && exec >"$1"
echo A signed tag message
echo from a fake editor.
EOF
chmod +x fakeeditor
get_tag_header implied-annotate $commit commit $time >expect
./fakeeditor >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect

View File

@@ -20,6 +20,8 @@ Test switching across them.
. ./test-lib.sh
test_tick
fill () {
for i
do
@@ -30,9 +32,10 @@ fill () {
test_expect_success setup '
fill x y z > same &&
fill 1 2 3 4 5 6 7 8 >one &&
fill a b c d e >two &&
git add one two &&
git add same one two &&
git commit -m "Initial A one, A two" &&
git checkout -b renamer &&
@@ -74,16 +77,44 @@ test_expect_success "checkout with dirty tree without -m" '
'
test_expect_success "checkout with unrelated dirty tree without -m" '
git checkout -f master &&
fill 0 1 2 3 4 5 6 7 8 >same &&
cp same kept
git checkout side >messages &&
git diff same kept
(cat > messages.expect <<EOF
M same
EOF
) &&
touch messages.expect &&
git diff messages.expect messages
'
test_expect_success "checkout -m with dirty tree" '
git checkout -f master &&
git clean -f &&
fill 0 1 2 3 4 5 6 7 8 >one &&
git checkout -m side &&
git checkout -m side > messages &&
test "$(git symbolic-ref HEAD)" = "refs/heads/side" &&
(cat >expect.messages <<EOF
Merging side with local
Merging:
ab76817 Side M one, D two, A three
virtual local
found 1 common ancestor(s):
7329388 Initial A one, A two
Auto-merged one
M one
EOF
) &&
git diff expect.messages messages &&
fill "M one" "A three" "D two" >expect.master &&
git diff --name-status master >current.master &&
diff expect.master current.master &&
@@ -145,7 +176,16 @@ test_expect_success 'checkout -m with merge conflict' '
test_expect_success 'checkout to detach HEAD' '
git checkout -f renamer && git clean -f &&
git checkout renamer^ &&
git checkout renamer^ 2>messages &&
(cat >messages.expect <<EOF
Note: moving to "renamer^" which isn'"'"'t a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
git checkout -b <new_branch_name>
HEAD is now at 7329388... Initial A one, A two
EOF
) &&
git diff -w messages.expect messages &&
H=$(git rev-parse --verify HEAD) &&
M=$(git show-ref -s --verify refs/heads/master) &&
test "z$H" = "z$M" &&

View File

@@ -4,7 +4,7 @@
#
# FIXME: Test the various index usages, -i and -o, test reflog,
# signoff, hooks
# signoff
test_description='git-commit'
. ./test-lib.sh

View File

@@ -88,4 +88,35 @@ test_expect_success 'status with relative paths' '
'
cat > expect << \EOF
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: dir2/added
#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: dir1/modified
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
EOF
test_expect_success 'status without relative paths' '
git config status.relativePaths false
(cd dir1 && git status) > output &&
git diff expect output
'
test_done

94
t/t7503-pre-commit-hook.sh Executable file
View File

@@ -0,0 +1,94 @@
#!/bin/sh
test_description='pre-commit hook'
. ./test-lib.sh
test_expect_success 'with no hook' '
echo "foo" > file &&
git add file &&
git commit -m "first"
'
test_expect_success '--no-verify with no hook' '
echo "bar" > file &&
git add file &&
git commit --no-verify -m "bar"
'
# now install hook that always succeeds
HOOKDIR="$(git rev-parse --git-dir)/hooks"
HOOK="$HOOKDIR/pre-commit"
mkdir -p "$HOOKDIR"
cat > "$HOOK" <<EOF
#!/bin/sh
exit 0
EOF
chmod +x "$HOOK"
test_expect_success 'with succeeding hook' '
echo "more" >> file &&
git add file &&
git commit -m "more"
'
test_expect_success '--no-verify with succeeding hook' '
echo "even more" >> file &&
git add file &&
git commit --no-verify -m "even more"
'
# now a hook that fails
cat > "$HOOK" <<EOF
#!/bin/sh
exit 1
EOF
test_expect_failure 'with failing hook' '
echo "another" >> file &&
git add file &&
git commit -m "another"
'
test_expect_success '--no-verify with failing hook' '
echo "stuff" >> file &&
git add file &&
git commit --no-verify -m "stuff"
'
chmod -x "$HOOK"
if test "$(git config --bool core.filemode)" = false; then
say "executable bit not honored - skipping tests of non-executable hook"
else
test_expect_success 'with non-executable hook' '
echo "content" >> file &&
git add file &&
git commit -m "content"
'
test_expect_success '--no-verify with non-executable hook' '
echo "more content" >> file &&
git add file &&
git commit --no-verify -m "more content"
'
fi # non-executable hooks
test_done

226
t/t7504-commit-msg-hook.sh Executable file
View File

@@ -0,0 +1,226 @@
#!/bin/sh
test_description='commit-msg hook'
. ./test-lib.sh
test_expect_success 'with no hook' '
echo "foo" > file &&
git add file &&
git commit -m "first"
'
# set up fake editor for interactive editing
cat > fake-editor <<'EOF'
#!/bin/sh
cp FAKE_MSG "$1"
exit 0
EOF
chmod +x fake-editor
FAKE_EDITOR="$(pwd)/fake-editor"
export FAKE_EDITOR
test_expect_success 'with no hook (editor)' '
echo "more foo" >> file &&
git add file &&
echo "more foo" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit
'
test_expect_success '--no-verify with no hook' '
echo "bar" > file &&
git add file &&
git commit --no-verify -m "bar"
'
test_expect_success '--no-verify with no hook (editor)' '
echo "more bar" > file &&
git add file &&
echo "more bar" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
'
# now install hook that always succeeds
HOOKDIR="$(git rev-parse --git-dir)/hooks"
HOOK="$HOOKDIR/commit-msg"
mkdir -p "$HOOKDIR"
cat > "$HOOK" <<EOF
#!/bin/sh
exit 0
EOF
chmod +x "$HOOK"
test_expect_success 'with succeeding hook' '
echo "more" >> file &&
git add file &&
git commit -m "more"
'
test_expect_success 'with succeeding hook (editor)' '
echo "more more" >> file &&
git add file &&
echo "more more" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit
'
test_expect_success '--no-verify with succeeding hook' '
echo "even more" >> file &&
git add file &&
git commit --no-verify -m "even more"
'
test_expect_success '--no-verify with succeeding hook (editor)' '
echo "even more more" >> file &&
git add file &&
echo "even more more" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
'
# now a hook that fails
cat > "$HOOK" <<EOF
#!/bin/sh
exit 1
EOF
test_expect_failure 'with failing hook' '
echo "another" >> file &&
git add file &&
git commit -m "another"
'
test_expect_failure 'with failing hook (editor)' '
echo "more another" >> file &&
git add file &&
echo "more another" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit
'
test_expect_success '--no-verify with failing hook' '
echo "stuff" >> file &&
git add file &&
git commit --no-verify -m "stuff"
'
test_expect_success '--no-verify with failing hook (editor)' '
echo "more stuff" >> file &&
git add file &&
echo "more stuff" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
'
chmod -x "$HOOK"
if test "$(git config --bool core.filemode)" = false; then
say "executable bit not honored - skipping tests of non-executable hook"
else
test_expect_success 'with non-executable hook' '
echo "content" >> file &&
git add file &&
git commit -m "content"
'
test_expect_success 'with non-executable hook (editor)' '
echo "content again" >> file &&
git add file &&
echo "content again" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit -m "content again"
'
test_expect_success '--no-verify with non-executable hook' '
echo "more content" >> file &&
git add file &&
git commit --no-verify -m "more content"
'
test_expect_success '--no-verify with non-executable hook (editor)' '
echo "even more content" >> file &&
git add file &&
echo "even more content" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
'
fi # non-executable hooks
# now a hook that edits the commit message
cat > "$HOOK" <<'EOF'
#!/bin/sh
echo "new message" > "$1"
exit 0
EOF
chmod +x "$HOOK"
commit_msg_is () {
test "`git log --pretty=format:%s%b -1`" = "$1"
}
test_expect_success 'hook edits commit message' '
echo "additional" >> file &&
git add file &&
git commit -m "additional" &&
commit_msg_is "new message"
'
test_expect_success 'hook edits commit message (editor)' '
echo "additional content" >> file &&
git add file &&
echo "additional content" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit &&
commit_msg_is "new message"
'
test_expect_success "hook doesn't edit commit message" '
echo "plus" >> file &&
git add file &&
git commit --no-verify -m "plus" &&
commit_msg_is "plus"
'
test_expect_success "hook doesn't edit commit message (editor)" '
echo "more plus" >> file &&
git add file &&
echo "more plus" > FAKE_MSG &&
GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify &&
commit_msg_is "more plus"
'
test_done

View File

@@ -97,15 +97,19 @@ test_expect_success 'migrate --minimize on old inited layout' "
grep '^:refs/remotes/git-svn' fetch.out
"
test_expect_success ".rev_db auto-converted to .rev_db.UUID" "
test_expect_success ".rev_db auto-converted to .rev_map.UUID" "
git-svn fetch -i trunk &&
expect=$GIT_DIR/svn/trunk/.rev_db.* &&
test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
expect=\"\$(ls $GIT_DIR/svn/trunk/.rev_map.*)\" &&
test -n \"\$expect\" &&
mv \$expect $GIT_DIR/svn/trunk/.rev_db &&
rev_db=\$(echo \$expect | sed -e 's,_map,_db,') &&
convert_to_rev_db \$expect \$rev_db &&
rm -f \$expect &&
test -f \$rev_db &&
git-svn fetch -i trunk &&
test -L $GIT_DIR/svn/trunk/.rev_db &&
test -f \$expect &&
cmp \$expect $GIT_DIR/svn/trunk/.rev_db
test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
test ! -e $GIT_DIR/svn/trunk/.rev_db &&
test -f \$expect
"
test_done

View File

@@ -5,6 +5,8 @@
test_description='git-svn info'
. ./lib-git-svn.sh
say 'skipping svn-info test (has a race undiagnosed yet)'
test_done
ptouch() {
perl -w -e '

View File

@@ -326,6 +326,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
die("unable to set up diff options to follow renames");
diff_tree(t1, t2, base, &diff_opts);
diffcore_std(&diff_opts);
diff_tree_release_paths(&diff_opts);
/* Go through the new set of filepairing, and see if we find a more interesting one */
for (i = 0; i < q->nr; i++) {
@@ -342,6 +343,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
choice = p;
/* Update the path we use from now on.. */
diff_tree_release_paths(opt);
opt->paths[0] = xstrdup(p->one->path);
diff_tree_setup_paths(opt->paths, opt);
break;

4
var.c
View File

@@ -21,7 +21,7 @@ static void list_vars(void)
{
struct git_var *ptr;
for(ptr = git_vars; ptr->read; ptr++) {
printf("%s=%s\n", ptr->name, ptr->read(0));
printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
}
}
@@ -32,7 +32,7 @@ static const char *read_var(const char *var)
val = NULL;
for(ptr = git_vars; ptr->read; ptr++) {
if (strcmp(var, ptr->name) == 0) {
val = ptr->read(1);
val = ptr->read(IDENT_ERROR_ON_NO_NAME);
break;
}
}

View File

@@ -34,16 +34,12 @@ void maybe_flush_or_die(FILE *f, const char *desc)
return;
}
if (fflush(f)) {
#ifndef __MINGW32__
if (errno == EPIPE)
#else
/*
* On Windows, EPIPE is returned only by the first write()
* after the reading end has closed its handle; subsequent
* write()s return EINVAL.
*/
if (errno == EPIPE || errno == EINVAL)
#endif
exit(0);
die("write failure on %s: %s", desc, strerror(errno));
}

96
ws.c Normal file
View File

@@ -0,0 +1,96 @@
/*
* Whitespace rules
*
* Copyright (c) 2007 Junio C Hamano
*/
#include "cache.h"
#include "attr.h"
static struct whitespace_rule {
const char *rule_name;
unsigned rule_bits;
} whitespace_rule_names[] = {
{ "trailing-space", WS_TRAILING_SPACE },
{ "space-before-tab", WS_SPACE_BEFORE_TAB },
{ "indent-with-non-tab", WS_INDENT_WITH_NON_TAB },
};
unsigned parse_whitespace_rule(const char *string)
{
unsigned rule = WS_DEFAULT_RULE;
while (string) {
int i;
size_t len;
const char *ep;
int negated = 0;
string = string + strspn(string, ", \t\n\r");
ep = strchr(string, ',');
if (!ep)
len = strlen(string);
else
len = ep - string;
if (*string == '-') {
negated = 1;
string++;
len--;
}
if (!len)
break;
for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++) {
if (strncmp(whitespace_rule_names[i].rule_name,
string, len))
continue;
if (negated)
rule &= ~whitespace_rule_names[i].rule_bits;
else
rule |= whitespace_rule_names[i].rule_bits;
break;
}
string = ep;
}
return rule;
}
static void setup_whitespace_attr_check(struct git_attr_check *check)
{
static struct git_attr *attr_whitespace;
if (!attr_whitespace)
attr_whitespace = git_attr("whitespace", 10);
check[0].attr = attr_whitespace;
}
unsigned whitespace_rule(const char *pathname)
{
struct git_attr_check attr_whitespace_rule;
setup_whitespace_attr_check(&attr_whitespace_rule);
if (!git_checkattr(pathname, 1, &attr_whitespace_rule)) {
const char *value;
value = attr_whitespace_rule.value;
if (ATTR_TRUE(value)) {
/* true (whitespace) */
unsigned all_rule = 0;
int i;
for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
all_rule |= whitespace_rule_names[i].rule_bits;
return all_rule;
} else if (ATTR_FALSE(value)) {
/* false (-whitespace) */
return 0;
} else if (ATTR_UNSET(value)) {
/* reset to default (!whitespace) */
return whitespace_rule_cfg;
} else {
/* string */
return parse_whitespace_rule(value);
}
} else {
return whitespace_rule_cfg;
}
}

View File

@@ -8,6 +8,7 @@
#include "revision.h"
#include "diffcore.h"
int wt_status_relative_paths = 1;
int wt_status_use_color = 0;
static char wt_status_colors[][COLOR_MAXLEN] = {
"", /* WT_STATUS_HEADER: normal */
@@ -121,6 +122,9 @@ static char *quote_path(const char *in, int len,
}
}
if (!out->len)
strbuf_addstr(out, "./");
return out->buf;
}
@@ -397,6 +401,11 @@ int git_status_config(const char *k, const char *v)
if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
int slot = parse_status_slot(k, 13);
color_parse(v, k, wt_status_colors[slot]);
return 0;
}
if (!strcmp(k, "status.relativepaths")) {
wt_status_relative_paths = git_config_bool(k, v);
return 0;
}
return git_default_config(k, v);
}

View File

@@ -28,6 +28,7 @@ struct wt_status {
int git_status_config(const char *var, const char *value);
int wt_status_use_color;
int wt_status_relative_paths;
void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);