Merge GIT v1.5.2-rc1

This commit is contained in:
Johannes Sixt
2007-05-19 23:16:27 +02:00
67 changed files with 1490 additions and 413 deletions

View File

@@ -23,6 +23,7 @@ Lars Doelle <lars.doelle@on-line.de>
Lars Doelle <lars.doelle@on-line ! de>
Lukas Sandström <lukass@etek.chalmers.se>
Martin Langhoff <martin@catalyst.net.nz>
Michele Ballabio <barra_cuda@katamail.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
René Scharfe <rene.scharfe@lsrfire.ath.cx>

View File

@@ -0,0 +1,46 @@
GIT v1.5.1.3 Release Notes
==========================
Fixes since v1.5.1.2
--------------------
* Bugfixes
- git-add tried to optimize by finding common leading
directories across its arguments but botched, causing very
confused behaviour.
- unofficial rpm.spec file shipped with git was letting
ETC_GITCONFIG set to /usr/etc/gitconfig. Tweak the official
Makefile to make it harder for distro people to make the
same mistake, by setting the variable to /etc/gitconfig if
prefix is set to /usr.
- git-svn inconsistently stripped away username from the URL
only when svnsync_props was in use.
- git-svn got confused when handling symlinks on Mac OS.
- git-send-email was not quoting recipient names that have
period '.' in them. Also it did not allow overriding
envelope sender, which made it impossible to send patches to
certain subscriber-only lists.
- built-in write_tree() routine had a sequence that renamed a
file that is still open, which some systems did not like.
- when memory is very tight, sliding mmap code to read
packfiles incorrectly closed the fd that was still being
used to read the pack.
- import-tars contributed front-end for fastimport was passing
wrong directory modes without checking.
- git-fastimport trusted its input too much and allowed to
create corrupt tree objects with entries without a name.
- git-fetch needlessly barfed when too long reflog action
description was given by the caller.
Also contains various documentation updates.

View File

@@ -26,8 +26,14 @@ Updates since v1.5.1
considered a binary or text (the former would be treated by
'git diff' not to produce textual output; the latter can go
through the line endings conversion process in repositories
with core.autocrlf set), and specify a custom 3-way merge
driver.
with core.autocrlf set), expand and unexpand '$ident$' keyword
with blob object name, specify a custom 3-way merge driver,
and specify a custom diff driver. You can also apply
arbitrary filter to contents on check-in/check-out codepath
but this feature is an extremely sharp-edged razor and needs
to be handled with caution (do not use it unless you
understand the earlier mailing list discussion on keyward
expansion).
* The packfile format now optionally suports 64-bit index.
@@ -53,8 +59,21 @@ Updates since v1.5.1
commit -a" (i.e. update the index to match the working
tree); it obviously does not make a commit.
- "git clean" honors a new configuration, "clean.requireforce". When
set to true, this makes "git clean" a no-op, preventing you
from losing files by typing "git clean" when you meant to
say "make clean". You can still say "git clean -f" to
override this.
- "git log" family of commands learned --date={local,relative,default}
option. --date=relative is synonym to the --relative-date.
--date=local gives the timestamp in local timezone.
* Updated behavior of existing commands.
- When $GIT_COMMITTER_EMAIL or $GIT_AUTHOR_EMAIL is not set
but $EMAIL is set, the latter is used as a substitute.
- "git diff --stat" shows size of preimage and postimage blobs
for binary contents. Earlier it only said "Bin".
@@ -82,11 +101,17 @@ Updates since v1.5.1
- "gitview" (in contrib/ section) learned to better support
"git-annotate".
- "git diff $commit1:$path2 $commit2:$path2" can now report
mode changes between the two blobs.
- Local "git fetch" from a repository whose object store is
one of the alternates (e.g. fetching from the origin in a
repository created with "git clone -l -s") avoids
downloading objects unnecessary.
- "git blame" uses .mailmap to canonicalize the author name
just like "git shortlog" does.
* Builds
- git-p4import has never been installed; now there is an
@@ -102,11 +127,15 @@ Updates since v1.5.1
* Performance Tweaks
- optimized "git-rev-list --bisect" (hence "git-bisect").
- Optimized "git-rev-list --bisect" (hence "git-bisect").
- optimized "git-add $path" in a large directory, most of
- Optimized "git-add $path" in a large directory, most of
whose contents are ignored.
- The recursive merge strategy updated a worktree file that
was changed identically in two branches, when one of them
renamed it. We do not do that when there is no rename, so
match that behaviour.
Fixes since v1.5.1
------------------
@@ -133,12 +162,15 @@ this release, unless otherwise noted.
will not be backported to 1.5.1.x series, as it is rather an
intrusive change.
- git-fetch had trouble with a remote with insanely large number
of refs.
* Documentation updates
* Performance Tweaks
--
exec >/var/tmp/1
O=v1.5.1.2-242-g2d76548
O=v1.5.2-rc0-106-g07c785d
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

View File

@@ -1,5 +1,7 @@
Checklist (and a short version for the impatient):
Commits:
- make commits of logical units
- check for unnecessary whitespace with "git diff --check"
before committing
@@ -12,8 +14,14 @@ Checklist (and a short version for the impatient):
commit message (or just use the option "-s" when
committing) to confirm that you agree to the Developer's
Certificate of Origin
- do not PGP sign your patch
Patch:
- use "git format-patch -M" to create the patch
- send your patch to <git@vger.kernel.org>. If you use
git-send-email(1), please test it first by sending
email to yourself.
- do not PGP sign your patch
- do not attach your patch, but read in the mail
body, unless you cannot teach your mailer to
leave the formatting of the patch alone.

View File

@@ -9,8 +9,28 @@
--show-stats::
Include additional statistics at the end of blame output.
-L n,m::
Annotate only the specified line range (lines count from 1).
-L <start>,<end>::
Annotate only the given line range. <start> and <end> can take
one of these forms:
- number
+
If <start> or <end> is a number, it specifies an
absolute line number (lines count from 1).
+
- /regex/
+
This form will use the first line matching the given
POSIX regex. If <end> is a regex, it will search
starting at the line given by <start>.
+
- +offset or -offset
+
This is only valid for <end> and will specify a number
of lines before or after the line given by <start>.
+
-l::
Show long rev (Default: off).

View File

@@ -610,8 +610,8 @@ tar.umask::
user.email::
Your email address to be recorded in any newly created commits.
Can be overridden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL'
environment variables. See gitlink:git-commit-tree[1].
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
'EMAIL' environment variables. See gitlink:git-commit-tree[1].
user.name::
Your full name to be recorded in any newly created commits.

View File

@@ -319,10 +319,9 @@ argument to `git-commit-tree`.
`git-commit-tree` normally takes several arguments -- it wants to know
what the 'parent' of a commit was, but since this is the first commit
ever in this new repository, and it has no parents, we only need to pass in
the object name of the tree. However, `git-commit-tree`
also wants to get a commit message
on its standard input, and it will write out the resulting object name for the
commit to its standard output.
the object name of the tree. However, `git-commit-tree` also wants to get a
commit message on its standard input, and it will write out the resulting
object name for the commit to its standard output.
And this is where we create the `.git/refs/heads/master` file
which is pointed at by `HEAD`. This file is supposed to contain
@@ -1304,7 +1303,7 @@ So, we can use somebody else's work from a remote repository, but
how can *you* prepare a repository to let other people pull from
it?
Your do your real work in your working tree that has your
You do your real work in your working tree that has your
primary repository hanging under it as its `.git` subdirectory.
You *could* make that repository accessible remotely and ask
people to pull from it, but in practice that is not the way

View File

@@ -1,13 +1,20 @@
-q, \--quiet::
Pass --quiet to git-fetch-pack and silence any other internally
used programs.
-v, \--verbose::
Be verbose.
-a, \--append::
Append ref names and object names of fetched refs to the
existing contents of `.git/FETCH_HEAD`. Without this
option old data in `.git/FETCH_HEAD` will be overwritten.
\--upload-pack <upload-pack>::
When given, and the repository to fetch from is handled
by 'git-fetch-pack', '--exec=<upload-pack>' is passed to
the command to specify non-default path for the command
run on the other end.
When given, and the repository to fetch from is handled
by 'git-fetch-pack', '--exec=<upload-pack>' is passed to
the command to specify non-default path for the command
run on the other end.
-f, \--force::
When `git-fetch` is used with `<rbranch>:<lbranch>`
@@ -16,7 +23,7 @@
fetches is a descendant of `<lbranch>`. This option
overrides that check.
\--no-tags::
-n, \--no-tags::
By default, `git-fetch` fetches tags that point at
objects that are downloaded from the remote repository
and stores them locally. This option disables this

View File

@@ -8,7 +8,7 @@ git-blame - Show what revision and author last modified each line of a file
SYNOPSIS
--------
[verse]
'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m]
'git-blame' [-c] [-b] [--root] [-s] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m]
[-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
[<rev> | --contents <file>] [--] <file>
@@ -60,6 +60,9 @@ include::blame-options.txt[]
-n, --show-number::
Show line number in the original commit (Default: off).
-s::
Suppress author name and timestamp from the output.
THE PORCELAIN FORMAT
--------------------

View File

@@ -60,6 +60,8 @@ either `.git/config` file, or using the following environment variables.
GIT_AUTHOR_DATE
GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL
GIT_COMMITTER_DATE
EMAIL
(nb "<", ">" and "\n"s are stripped)

View File

@@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index
SYNOPSIS
--------
'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [<common diff options>] [<path>...]
'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|--no-index] [<common diff options>] [<path>...]
DESCRIPTION
-----------
@@ -36,7 +36,7 @@ omit diff output for unmerged entries and just show "Unmerged".
diff, similar to the way 'diff-tree' shows a merge
commit with these flags.
\-n,\--no-index::
--no-index::
Compare the two given files / directories.
-q::

View File

@@ -8,7 +8,8 @@ git-fmt-merge-msg - Produce a merge commit message
SYNOPSIS
--------
'git-fmt-merge-msg' <$GIT_DIR/FETCH_HEAD
git-fmt-merge-msg [--summary | --no-summary] <$GIT_DIR/FETCH_HEAD
git-fmt-merge-msg [--summary | --no-summray] -F <file>
DESCRIPTION
-----------
@@ -19,6 +20,28 @@ passed as the '<merge-message>' argument of `git-merge`.
This script is intended mostly for internal use by scripts
automatically invoking `git-merge`.
OPTIONS
-------
--summary::
In addition to branch names, populate the log message with
one-line descriptions from the actual commits that are being
merged.
--no-summary::
Do not list one-line descriptions from the actual commits being
merged.
--file <file>, -F <file>::
Take the list of merged objects from <file> instead of
stdin.
CONFIGURATION
-------------
merge.summary::
Whether to include summaries of merged commits in newly
merge commit messages. False by default.
SEE ALSO
--------

View File

@@ -12,12 +12,13 @@ SYNOPSIS
'git-grep' [--cached]
[-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
[-v | --invert-match] [-h|-H] [--full-name]
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
[-n] [-l | --files-with-matches] [-L | --files-without-match]
[-E | --extended-regexp] [-G | --basic-regexp]
[-F | --fixed-strings] [-n]
[-l | --files-with-matches] [-L | --files-without-match]
[-c | --count] [--all-match]
[-A <post-context>] [-B <pre-context>] [-C <context>]
[-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
[<tree>...]
[-f <file>] [-e] <pattern>
[--and|--or|--not|(|)|-e <pattern>...] [<tree>...]
[--] [<path>...]
DESCRIPTION
@@ -39,6 +40,9 @@ OPTIONS
Ignore case differences between the patterns and the
files.
-I::
Don't match the pattern in binary files.
-w | --word-regexp::
Match the pattern only at word boundary (either begin at the
beginning of a line, or preceded by a non-word character; end at
@@ -64,6 +68,10 @@ OPTIONS
Use POSIX extended/basic regexp for patterns. Default
is to use basic regexp.
-F | --fixed-strings::
Use fixed strings for patterns (don't interpret pattern
as a regex).
-n::
Prefix the line number to matching lines.
@@ -81,6 +89,9 @@ OPTIONS
line containing `--` between contiguous groups of
matches.
-<num>::
A shortcut for specifying -C<num>.
-f <file>::
Read patterns from <file>, one per line.

View File

@@ -39,6 +39,10 @@ commit-id::
<commit-id>['\t'<filename-as-in--w>]
--recover::
Verify that everything reachable from target is fetched. Used after
an earlier fetch is interrupted.
Author
------
Written by Linus Torvalds <torvalds@osdl.org>

View File

@@ -8,7 +8,7 @@ git-http-push - Push objects over HTTP/DAV to another repository
SYNOPSIS
--------
'git-http-push' [--complete] [--force] [--verbose] <url> <ref> [<ref>...]
'git-http-push' [--all] [--force] [--verbose] <url> <ref> [<ref>...]
DESCRIPTION
-----------
@@ -18,7 +18,7 @@ remote branch.
OPTIONS
-------
--complete::
--all::
Do not assume that the remote repository is complete in its
current state, and verify all objects in the entire local
ref's history exist in the remote repository.
@@ -34,6 +34,15 @@ OPTIONS
Report the list of objects being walked locally and the
list of objects successfully sent to the remote repository.
-d, -D::
Remove <ref> from remote repository. The specified branch
cannot be the remote HEAD. If -d is specified the following
other conditions must also be met:
- Remote HEAD must resolve to an object that exists locally
- Specified branch resolves to an object that exists locally
- Specified branch is an ancestor of the remote HEAD
<ref>...::
The remote refs to update.

View File

@@ -24,6 +24,16 @@ OPTIONS
Get all the objects.
-v::
Report what is downloaded.
-s::
Instead of regular file-to-file copying use symbolic links to the objects
in the remote repository.
-l::
Before attempting symlinks (if -s is specified) or file-to-file copying the
remote objects, try to hardlink the remote objects into the local
repository.
-n::
Never attempt to file-to-file copy remote objects. Only useful with
-s or -l command-line options.
-w <filename>::
Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
@@ -35,6 +45,10 @@ OPTIONS
<commit-id>['\t'<filename-as-in--w>]
--recover::
Verify that everything reachable from target is fetched. Used after
an earlier fetch is interrupted.
Author
------
Written by Junio C Hamano <junkio@cox.net>

View File

@@ -25,6 +25,7 @@ SYNOPSIS
[ \--cherry-pick ]
[ \--encoding[=<encoding>] ]
[ \--(author|committer|grep)=<pattern> ]
[ \--date={local|relative|default} ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
[ \--pretty | \--header ]
[ \--bisect ]
@@ -90,9 +91,20 @@ include::pretty-formats.txt[]
--relative-date::
Show dates relative to the current time, e.g. "2 hours ago".
Synonym for `--date=relative`.
--date={relative,local,default}::
Only takes effect for dates shown in human-readable format, such
as when using "--pretty".
+
`--date=relative` shows dates relative to the current time,
e.g. "2 hours ago".
+
`--date=local` shows timestamps in user's local timezone.
+
`--date=default` shows timestamps in the original timezone
(either committer's or author's).
--header::

View File

@@ -54,7 +54,7 @@ The --cc option must be repeated for each user you want on the cc list.
--in-reply-to::
Specify the contents of the first In-Reply-To header.
Subsequent emails will refer to the previous email
Subsequent emails will refer to the previous email
instead of this if --chain-reply-to is set (the default)
Only necessary if --compose is also set. If --compose
is not set, this will be prompted for.
@@ -68,8 +68,9 @@ The --cc option must be repeated for each user you want on the cc list.
all that is output.
--smtp-server::
If set, specifies the outgoing SMTP server to use. A full
pathname of a sendmail-like program can be specified instead;
If set, specifies the outgoing SMTP server to use (e.g.
`smtp.example.com` or a raw IP address). Alternatively it can
specify a full pathname of a sendmail-like program instead;
the program must support the `-i` option. Default value can
be specified by the 'sendemail.smtpserver' configuration
option; the built-in default is `/usr/sbin/sendmail` or
@@ -85,6 +86,15 @@ The --cc option must be repeated for each user you want on the cc list.
Do not add the From: address to the cc: list, if it shows up in a From:
line.
--dry-run::
Do everything except actually send the emails.
--envelope-sender::
Specify the envelope sender used to send the emails.
This is useful if your default address is not the address that is
subscribed to a list. If you use the sendmail binary, you must have
suitable privileges for the -f parameter.
--to::
Specify the primary recipient of the emails generated.
Generally, this will be the upstream maintainer of the

View File

@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
git-shortlog [-n|--number] [-s|--summary] [<committish>...]
git-shortlog [-n|--numbered] [-s|--summary] [<committish>...]
DESCRIPTION
-----------
@@ -22,14 +22,14 @@ Additionally, "[PATCH]" will be stripped from the commit description.
OPTIONS
-------
-h::
-h, \--help::
Print a short usage message and exit.
-n::
-n, \--numbered::
Sort output according to the number of commits per author instead
of author alphabetic order.
-s::
-s, \--summary::
Suppress commit description and provide a commit count summary only.
FILES

View File

@@ -159,6 +159,12 @@ New features:
Any other arguments are passed directly to `git log'
--
'find-rev'::
When given an SVN revision number of the form 'rN', returns the
corresponding git commit hash (this can optionally be followed by a
tree-ish to specify which branch should be searched). When given a
tree-ish, returns the corresponding SVN revision number.
'set-tree'::
You should consider using 'dcommit' instead of this command.
Commit specified commit or tree objects to SVN. This relies on

View File

@@ -29,6 +29,10 @@ in a coherent way to git enlightenment ;-).
The COMMAND is either a name of a Git command (see below) or an alias
as defined in the configuration file (see gitlink:git-config[1]).
Formatted and hyperlinked version of the latest git
documentation can be viewed at
`http://www.kernel.org/pub/software/scm/git/docs/`.
ifdef::stalenotes[]
[NOTE]
============
@@ -344,6 +348,8 @@ git Commits
'GIT_AUTHOR_DATE'::
'GIT_COMMITTER_NAME'::
'GIT_COMMITTER_EMAIL'::
'GIT_COMMITTER_DATE'::
'EMAIL'::
see gitlink:git-commit-tree[1]
git Diffs

View File

@@ -49,10 +49,12 @@ Set to a value::
Unspecified::
No glob pattern matches the path, and nothing says if
the path has or does not have the attribute.
the path has or does not have the attribute, the
attribute for the path is said to be Unspecified.
When more than one glob pattern matches the path, a later line
overrides an earlier line.
overrides an earlier line. This overriding is done per
attribute.
When deciding what attributes are assigned to a path, git
consults `$GIT_DIR/info/attributes` file (which has the highest
@@ -76,12 +78,17 @@ are attributes-aware.
Checking-out and checking-in
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The attribute `crlf` affects how the contents stored in the
These attributes affect how the contents stored in the
repository are copied to the working tree files when commands
such as `git checkout` and `git merge` run. It also affects how
such as `git checkout` and `git merge` run. They also affect how
git stores the contents you prepare in the working tree in the
repository upon `git add` and `git commit`.
`crlf`
^^^^^^
This attribute controls the line-ending convention.
Set::
Setting the `crlf` attribute on a path is meant to mark
@@ -127,6 +134,67 @@ converted to LF upon checkin, but there is no conversion done
upon checkout.
`ident`
^^^^^^^
When the attribute `ident` is set to a path, git replaces
`$ident$` in the blob object with `$ident:`, followed by
40-character hexadecimal blob object name, followed by a dollar
sign `$` upon checkout. Any byte sequence that begins with
`$ident:` and ends with `$` in the worktree file is replaced
with `$ident$` upon check-in.
Interaction between checkin/checkout attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the check-in codepath, the worktree file is first converted
with `ident` (if specified), and then with `crlf` (again, if
specified and applicable).
In the check-out codepath, the blob content is first converted
with `crlf`, and then `ident`.
`filter`
^^^^^^^^
A `filter` attribute can be set to a string value. This names
filter driver specified in the configuration.
A filter driver consists of `clean` command and `smudge`
command, either of which can be left unspecified. Upon
checkout, when `smudge` command is specified, the command is fed
the blob object from its standard input, and its standard output
is used to update the worktree file. Similarly, `clean` command
is used to convert the contents of worktree file upon checkin.
Missing filter driver definition in the config is not an error
but makes the filter a no-op passthru.
The content filtering is done to massage the content into a
shape that is more convenient for the platform, filesystem, and
the user to use. The keyword here is "more convenient" and not
"turning something unusable into usable". In other words, it is
"hanging yourself because we gave you a long rope" if your
project uses filtering mechanism in such a way that it makes
your project unusable unless the checkout is done with a
specific filter in effect.
Interaction between checkin/checkout attributes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the check-in codepath, the worktree file is first converted
with `filter` driver (if specified and corresponding driver
defined), then the result is processed with `ident` (if
specified), and then finally with `crlf` (again, if specified
and applicable).
In the check-out codepath, the blob content is first converted
with `crlf`, and then `ident` and fed to `filter`.
Generating diff text
~~~~~~~~~~~~~~~~~~~~
@@ -215,7 +283,7 @@ String::
merge driver. The built-in 3-way merge driver can be
explicitly specified by asking for "text" driver; the
built-in "take the current branch" driver can be
requested by "binary".
requested with "binary".
Defining a custom merge driver

View File

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

View File

@@ -141,7 +141,12 @@ prefix = $(HOME)
bindir = $(prefix)/bin
gitexecdir = $(bindir)
template_dir = $(prefix)/share/git-core/templates/
ETC_GITCONFIG = $(prefix)/etc/gitconfig
ifeq ($(prefix),/usr)
sysconfdir = /etc
else
sysconfdir = $(prefix)/etc
endif
ETC_GITCONFIG = $(sysconfdir)/gitconfig
# DESTDIR=
# default configuration for gitweb
@@ -160,7 +165,7 @@ GITWEB_FAVICON = git-favicon.png
GITWEB_SITE_HEADER =
GITWEB_SITE_FOOTER =
export prefix bindir gitexecdir template_dir
export prefix bindir gitexecdir template_dir sysconfdir
CC = gcc
AR = ar
@@ -283,7 +288,7 @@ LIB_H = \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
spawn-pipe.h \
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h
utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h mailmap.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -306,7 +311,7 @@ LIB_OBJS = \
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
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
convert.o attr.o decorate.o progress.o mailmap.o
BUILTIN_OBJS = \
builtin-add.o \
@@ -953,6 +958,10 @@ endif
### Testing rules
TEST_PROGRAMS = test-chmtime$X test-genrandom$X
all: $(TEST_PROGRAMS)
# GNU make supports exporting all variables by "export" without parameters.
# However, the environment gets quite big, and some programs have problems
# with that.
@@ -960,7 +969,7 @@ endif
export NO_SYMLINKS
export NO_SVN_TESTS
test: all test-chmtime$X test-genrandom$X
test: all
$(MAKE) -C t/ all
test-date$X: test-date.c date.o ctype.o

View File

@@ -16,16 +16,20 @@
#include "quote.h"
#include "xdiff-interface.h"
#include "cache-tree.h"
#include "path-list.h"
#include "mailmap.h"
static char blame_usage[] =
"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
"git-blame [-c] [-b] [-l] [--root] [-x] [-t] [-f] [-n] [-s] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
" -c Use the same output mode as git-annotate (Default: off)\n"
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
" -l Show long commit SHA1 (Default: off)\n"
" --root Do not treat root commits as boundaries (Default: off)\n"
" -t Show raw timestamp (Default: off)\n"
" -x Do not use .mailmap file\n"
" -f, --show-name Show original filename (Default: auto)\n"
" -n, --show-number Show original linenumber (Default: off)\n"
" -s Suppress author name and timestamp (Default: off)\n"
" -p, --porcelain Show in a format designed for machine consumption\n"
" -L n,m Process only line range n,m, counting from 1\n"
" -M, -C Find line movements within and across files\n"
@@ -42,6 +46,8 @@ static int show_root;
static int blank_boundary;
static int incremental;
static int cmd_is_annotate;
static int no_mailmap;
static struct path_list mailmap;
#ifndef DEBUG
#define DEBUG 0
@@ -1264,8 +1270,8 @@ static void get_ac_line(const char *inbuf, const char *what,
int bufsz, char *person, const char **mail,
unsigned long *time, const char **tz)
{
int len;
char *tmp, *endp;
int len, tzlen, maillen;
char *tmp, *endp, *timepos;
tmp = strstr(inbuf, what);
if (!tmp)
@@ -1291,17 +1297,42 @@ static void get_ac_line(const char *inbuf, const char *what,
while (*tmp != ' ')
tmp--;
*tz = tmp+1;
tzlen = (person+len)-(tmp+1);
*tmp = 0;
while (*tmp != ' ')
tmp--;
*time = strtoul(tmp, NULL, 10);
timepos = tmp;
*tmp = 0;
while (*tmp != ' ')
tmp--;
*mail = tmp + 1;
*tmp = 0;
maillen = timepos - tmp;
if (!mailmap.nr)
return;
/*
* mailmap expansion may make the name longer.
* make room by pushing stuff down.
*/
tmp = person + bufsz - (tzlen + 1);
memmove(tmp, *tz, tzlen);
tmp[tzlen] = 0;
*tz = tmp;
tmp = tmp - (maillen + 1);
memmove(tmp, *mail, maillen);
tmp[maillen] = 0;
*mail = tmp;
/*
* Now, convert e-mail using mailmap
*/
map_email(&mailmap, tmp + 1, person, tmp-person-1);
}
static void get_commit_info(struct commit *commit,
@@ -1483,6 +1514,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
#define OUTPUT_SHOW_NAME 020
#define OUTPUT_SHOW_NUMBER 040
#define OUTPUT_SHOW_SCORE 0100
#define OUTPUT_NO_AUTHOR 0200
static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
{
@@ -1577,10 +1609,15 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
if (opt & OUTPUT_SHOW_NUMBER)
printf(" %*d", max_orig_digits,
ent->s_lno + 1 + cnt);
printf(" (%-*.*s %10s %*d) ",
longest_author, longest_author, ci.author,
format_time(ci.author_time, ci.author_tz,
show_raw_time),
if (!(opt & OUTPUT_NO_AUTHOR))
printf(" (%-*.*s %10s",
longest_author, longest_author,
ci.author,
format_time(ci.author_time,
ci.author_tz,
show_raw_time));
printf(" %*d) ",
max_digits, ent->lno + 1 + cnt);
}
do {
@@ -2092,6 +2129,8 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
output_option |= OUTPUT_RAW_TIMESTAMP;
else if (!strcmp("-l", arg))
output_option |= OUTPUT_LONG_OBJECT_NAME;
else if (!strcmp("-s", arg))
output_option |= OUTPUT_NO_AUTHOR;
else if (!strcmp("-S", arg) && ++i < argc)
revs_file = argv[i];
else if (!prefixcmp(arg, "-M")) {
@@ -2134,6 +2173,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
else if (!strcmp("-p", arg) ||
!strcmp("--porcelain", arg))
output_option |= OUTPUT_PORCELAIN;
else if (!strcmp("-x", arg) ||
!strcmp("--no-mailmap", arg))
no_mailmap = 1;
else if (!strcmp("--", arg)) {
seen_dashdash = 1;
i++;
@@ -2333,6 +2375,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
die("reading graft file %s failed: %s",
revs_file, strerror(errno));
if (!no_mailmap)
read_mailmap(&mailmap, ".mailmap", NULL);
assign_blame(&sb, &revs, opt);
if (incremental)

View File

@@ -16,9 +16,8 @@
*/
static void init_buffer(char **bufp, unsigned int *sizep)
{
char *buf = xmalloc(BLOCKING);
*bufp = xmalloc(BLOCKING);
*sizep = 0;
*bufp = buf;
}
static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)

View File

@@ -10,7 +10,7 @@
#include "builtin.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [<common diff options>] [<path>...]"
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|--no-index] [<common diff options>] [<path>...]"
COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_files(int argc, const char **argv, const char *prefix)

View File

@@ -13,13 +13,10 @@
#include "log-tree.h"
#include "builtin.h"
/* NEEDSWORK: struct object has place for name but we _do_
* know mode when we extracted the blob out of a tree, which
* we currently lose.
*/
struct blobinfo {
unsigned char sha1[20];
const char *name;
unsigned mode;
};
static const char builtin_diff_usage[] =
@@ -35,7 +32,7 @@ static void stuff_change(struct diff_options *opt,
struct diff_filespec *one, *two;
if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&
!hashcmp(old_sha1, new_sha1))
!hashcmp(old_sha1, new_sha1) && (old_mode == new_mode))
return;
if (opt->reverse_diff) {
@@ -70,8 +67,12 @@ static int builtin_diff_b_f(struct rev_info *revs,
die("'%s': %s", path, strerror(errno));
if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
die("'%s': not a regular file or symlink", path);
if (blob[0].mode == S_IFINVALID)
blob[0].mode = canon_mode(st.st_mode);
stuff_change(&revs->diffopt,
canon_mode(st.st_mode), canon_mode(st.st_mode),
blob[0].mode, canon_mode(st.st_mode),
blob[0].sha1, null_sha1,
path, path);
diffcore_std(&revs->diffopt);
@@ -88,8 +89,14 @@ static int builtin_diff_blobs(struct rev_info *revs,
if (argc > 1)
usage(builtin_diff_usage);
if (blob[0].mode == S_IFINVALID)
blob[0].mode = mode;
if (blob[1].mode == S_IFINVALID)
blob[1].mode = mode;
stuff_change(&revs->diffopt,
mode, mode,
blob[0].mode, blob[1].mode,
blob[0].sha1, blob[1].sha1,
blob[0].name, blob[1].name);
diffcore_std(&revs->diffopt);
@@ -272,6 +279,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
die("more than two blobs given: '%s'", name);
hashcpy(blob[blobs].sha1, obj->sha1);
blob[blobs].name = name;
blob[blobs].mode = list->mode;
blobs++;
continue;

View File

@@ -35,16 +35,13 @@ static int update_ref(const char *action,
unsigned char *sha1,
unsigned char *oldval)
{
int len;
char msg[1024];
char *rla = getenv("GIT_REFLOG_ACTION");
static struct ref_lock *lock;
if (!rla)
rla = "(reflog update)";
len = snprintf(msg, sizeof(msg), "%s: %s", rla, action);
if (sizeof(msg) <= len)
die("insanely long action");
snprintf(msg, sizeof(msg), "%s: %s", rla, action);
lock = lock_any_ref_for_update(refname, oldval);
if (!lock)
return 1;

View File

@@ -219,6 +219,7 @@ static int fsck_tree(struct tree *item)
{
int retval;
int has_full_path = 0;
int has_empty_name = 0;
int has_zero_pad = 0;
int has_bad_modes = 0;
int has_dup_entries = 0;
@@ -242,6 +243,8 @@ static int fsck_tree(struct tree *item)
if (strchr(name, '/'))
has_full_path = 1;
if (!*name)
has_empty_name = 1;
has_zero_pad |= *(char *)desc.buffer == '0';
update_tree_entry(&desc);
@@ -291,6 +294,9 @@ static int fsck_tree(struct tree *item)
if (has_full_path) {
objwarning(&item->object, "contains full pathnames");
}
if (has_empty_name) {
objwarning(&item->object, "contains empty pathname");
}
if (has_zero_pad) {
objwarning(&item->object, "contains zero-padded file modes");
}

View File

@@ -95,7 +95,7 @@ static void show_commit(struct commit *commit)
static char pretty_header[16384];
pretty_print_commit(revs.commit_format, commit, ~0,
pretty_header, sizeof(pretty_header),
revs.abbrev, NULL, NULL, revs.relative_date);
revs.abbrev, NULL, NULL, revs.date_mode);
printf("%s%c", pretty_header, hdr_termination);
}
fflush(stdout);

View File

@@ -5,6 +5,7 @@
#include "path-list.h"
#include "revision.h"
#include "utf8.h"
#include "mailmap.h"
static const char shortlog_usage[] =
"git-shortlog [-n] [-s] [<commit-id>... ]";
@@ -26,83 +27,6 @@ static int compare_by_number(const void *a1, const void *a2)
static struct path_list mailmap = {NULL, 0, 0, 0};
static int read_mailmap(const char *filename)
{
char buffer[1024];
FILE *f = fopen(filename, "r");
if (f == NULL)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
char *end_of_name, *left_bracket, *right_bracket;
char *name, *email;
int i;
if (buffer[0] == '#') {
static const char abbrev[] = "# repo-abbrev:";
int abblen = sizeof(abbrev) - 1;
int len = strlen(buffer);
if (len && buffer[len - 1] == '\n')
buffer[--len] = 0;
if (!strncmp(buffer, abbrev, abblen)) {
char *cp;
if (common_repo_prefix)
free(common_repo_prefix);
common_repo_prefix = xmalloc(len);
for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
strcpy(common_repo_prefix, cp);
}
continue;
}
if ((left_bracket = strchr(buffer, '<')) == NULL)
continue;
if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
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 */
if (end_of_name == buffer)
continue;
name = xmalloc(end_of_name - buffer + 1);
strlcpy(name, buffer, end_of_name - buffer + 1);
email = xmalloc(right_bracket - left_bracket);
for (i = 0; i < right_bracket - left_bracket - 1; i++)
email[i] = tolower(left_bracket[i + 1]);
email[right_bracket - left_bracket - 1] = '\0';
path_list_insert(email, &mailmap)->util = name;
}
fclose(f);
return 0;
}
static int map_email(char *email, char *name, int maxlen)
{
char *p;
struct path_list_item *item;
/* autocomplete common developers */
p = strchr(email, '>');
if (!p)
return 0;
*p = '\0';
/* downcase the email address */
for (p = email; *p; p++)
*p = tolower(*p);
item = path_list_lookup(email, &mailmap);
if (item != NULL) {
const char *realname = (const char *)item->util;
strncpy(name, realname, maxlen);
return 1;
}
return 0;
}
static void insert_author_oneline(struct path_list *list,
const char *author, int authorlen,
const char *oneline, int onelinelen)
@@ -184,7 +108,7 @@ static void read_from_stdin(struct path_list *list)
(bob = strchr(buffer + 7, '<')) != NULL) {
char buffer2[1024], offset = 0;
if (map_email(bob + 1, buffer, sizeof(buffer)))
if (map_email(&mailmap, bob + 1, buffer, sizeof(buffer)))
bob = buffer + strlen(buffer);
else {
offset = 8;
@@ -238,7 +162,7 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list)
die("Invalid commit buffer: %s",
sha1_to_hex(commit->object.sha1));
if (map_email(bracket + 1, scratch,
if (map_email(&mailmap, bracket + 1, scratch,
sizeof(scratch))) {
author = scratch;
authorlen = strlen(scratch);
@@ -359,8 +283,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
if (argc > 1)
die ("unrecognized argument: %s", argv[1]);
if (!access(".mailmap", R_OK))
read_mailmap(".mailmap");
read_mailmap(&mailmap, ".mailmap", &common_repo_prefix);
if (rev.pending.nr == 0) {
if (isatty(0))

View File

@@ -36,8 +36,10 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
die("git-write-tree: error building trees");
if (0 <= newfd) {
if (!write_cache(newfd, active_cache, active_nr)
&& !close(newfd))
&& !close(newfd)) {
commit_lock_file(lock_file);
newfd = -1;
}
}
/* Not being able to write is fine -- we are only interested
* in updating the cache-tree part, and if the next caller
@@ -55,6 +57,8 @@ int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
else
hashcpy(sha1, active_cache_tree->sha1);
if (0 <= newfd)
close(newfd);
rollback_lock_file(lock_file);
return 0;

66
cache.h
View File

@@ -24,6 +24,9 @@
#define DTYPE(de) DT_UNKNOWN
#endif
/* unknown mode (impossible combination S_IFIFO|S_IFCHR) */
#define S_IFINVALID 0030000
/*
* A "directory link" is a link to another git directory.
*
@@ -143,9 +146,37 @@ static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned in
#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
extern struct cache_entry **active_cache;
extern unsigned int active_nr, active_alloc, active_cache_changed;
extern struct cache_tree *active_cache_tree;
struct index_state {
struct cache_entry **cache;
unsigned int cache_nr, cache_alloc, cache_changed;
struct cache_tree *cache_tree;
time_t timestamp;
void *mmap;
size_t mmap_size;
};
extern struct index_state the_index;
#ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
#define active_cache (the_index.cache)
#define active_nr (the_index.cache_nr)
#define active_alloc (the_index.cache_alloc)
#define active_cache_changed (the_index.cache_changed)
#define active_cache_tree (the_index.cache_tree)
#define read_cache() read_index(&the_index)
#define read_cache_from(path) read_index_from(&the_index, (path))
#define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
#define discard_cache() discard_index(&the_index)
#define cache_name_pos(name, namelen) index_name_pos(&the_index,(name),(namelen))
#define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
#define refresh_cache(flags) refresh_index(&the_index, flags)
#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))
#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))
#endif
enum object_type {
OBJ_BAD = -1,
@@ -195,23 +226,23 @@ extern void verify_non_filename(const char *prefix, const char *name);
#define alloc_nr(x) (((x)+16)*3/2)
/* Initialize and use the cache information */
extern int read_cache(void);
extern int read_cache_from(const char *path);
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
extern int discard_cache(void);
extern int read_index(struct index_state *);
extern int read_index_from(struct index_state *, const char *path);
extern int write_index(struct index_state *, int newfd);
extern int discard_index(struct index_state *);
extern int verify_path(const char *path);
extern int cache_name_pos(const char *name, int namelen);
extern int index_name_pos(struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
extern int add_cache_entry(struct cache_entry *ce, int option);
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_cache_entry_at(int pos);
extern int remove_file_from_cache(const char *path);
extern int add_file_to_cache(const char *path, int verbose);
extern int remove_index_entry_at(struct index_state *, int pos);
extern int remove_file_from_index(struct index_state *, const char *path);
extern int add_file_to_index(struct index_state *, const char *path, int verbose);
extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);
extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
extern int read_pipe(int fd, char** return_buf, unsigned long* return_size);
@@ -223,7 +254,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
extern int refresh_cache(unsigned int flags);
extern int refresh_index(struct index_state *, unsigned int flags);
struct lock_file {
struct lock_file *next;
@@ -340,6 +371,7 @@ static inline unsigned int hexval(unsigned int c)
#define DEFAULT_ABBREV 7
extern int get_sha1(const char *str, unsigned char *sha1);
extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
extern int get_sha1_hex(const char *hex, unsigned char *sha1);
extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1);
@@ -358,7 +390,7 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned long *size,
unsigned char *sha1_ret);
enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT };
enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT, DATE_LOCAL };
const char *show_date(unsigned long time, int timezone, enum date_mode mode);
const char *show_rfc2822_date(unsigned long time, int timezone);
int parse_date(const char *date, char *buf, int bufsize);
@@ -378,7 +410,7 @@ struct checkout {
refresh_cache:1;
};
extern int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath);
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
extern struct alternate_object_database {
struct alternate_object_database *next;

View File

@@ -526,7 +526,7 @@ static int add_rfc2047(char *buf, const char *line, int len,
}
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
const char *line, int relative_date,
const char *line, enum date_mode dmode,
const char *encoding)
{
char *date;
@@ -569,7 +569,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
switch (fmt) {
case CMIT_FMT_MEDIUM:
ret += sprintf(buf + ret, "Date: %s\n",
show_date(time, tz, relative_date));
show_date(time, tz, dmode));
break;
case CMIT_FMT_EMAIL:
ret += sprintf(buf + ret, "Date: %s\n",
@@ -577,7 +577,7 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
break;
case CMIT_FMT_FULLER:
ret += sprintf(buf + ret, "%sDate: %s\n", what,
show_date(time, tz, relative_date));
show_date(time, tz, dmode));
break;
default:
/* notin' */
@@ -919,7 +919,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
char *buf, unsigned long space,
int abbrev, const char *subject,
const char *after_subject,
int relative_date)
enum date_mode dmode)
{
int hdr = 1, body = 0, seen_title = 0;
unsigned long offset = 0;
@@ -1023,14 +1023,14 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
offset += add_user_info("Author", fmt,
buf + offset,
line + 7,
relative_date,
dmode,
encoding);
if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
offset += add_user_info("Commit", fmt,
buf + offset,
line + 10,
relative_date,
dmode,
encoding);
continue;
}

View File

@@ -61,7 +61,7 @@ enum cmit_fmt {
};
extern enum cmit_fmt get_commit_format(const char *arg);
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, int relative_date);
extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
/** Removes the first commit from a list sorted by date, and adds all
* of its parents.

View File

@@ -52,6 +52,7 @@ foreach my $tar_file (@ARGV)
Z8 Z1 Z100 Z6
Z2 Z32 Z32 Z8 Z8 Z*', $_;
last unless $name;
next if $name =~ m{/\z};
$mode = oct $mode;
$size = oct $size;
$mtime = oct $mtime;
@@ -64,7 +65,12 @@ foreach my $tar_file (@ARGV)
}
print FI "\n";
my $path = "$prefix$name";
my $path;
if ($prefix) {
$path = "$prefix/$name";
} else {
$path = "$name";
}
$files{$path} = [$next_mark++, $mode];
$commit_time = $mtime if $mtime > $commit_time;

View File

@@ -302,7 +302,7 @@ generate_update_branch_email()
# List all of the revisions that were removed by this update, in a fast forward
# update, this list will be empty, because rev-list O ^N is empty. For a non
# fast forward, O ^N is the list of removed revisions
fastforward=""
fast_forward=""
rev=""
for rev in $(git rev-list $newrev..$oldrev)
do
@@ -327,36 +327,67 @@ generate_update_branch_email()
if [ -z "$fastforward" ]; then
echo " from $oldrev ($oldrev_type)"
else
# 1. Existing revisions were removed. In this case newrev is a
# subset of oldrev - this is the reverse of a fast-forward,
# a rewind
# 2. New revisions were added on top of an old revision, this is
# a rewind and addition.
# (1) certainly happened, (2) possibly. When (2) hasn't happened,
# we set a flag to indicate that no log printout is required.
echo ""
echo "This update added new revisions after undoing old revisions. That is to"
echo "say, the old revision is not a strict subset of the new revision. This"
echo "situation occurs when you --force push a change and generate a"
echo "repository containing something like this:"
echo ""
echo " * -- * -- B -- O -- O -- O ($oldrev)"
echo " \\"
echo " N -- N -- N ($newrev)"
echo ""
echo "When this happens we assume that you've already had alert emails for all"
echo "of the O revisions, and so we here report only the revisions in the N"
echo "branch from the common base, B."
# Find the common ancestor of the old and new revisions and compare
# it with newrev
baserev=$(git merge-base $oldrev $newrev)
rewind_only=""
if [ "$baserev" = "$newrev" ]; then
echo "This update discarded existing revisions and left the branch pointing at"
echo "a previous point in the repository history."
echo ""
echo " * -- * -- N ($newrev)"
echo " \\"
echo " O -- O -- O ($oldrev)"
echo ""
echo "The removed revisions are not necessarilly gone - if another reference"
echo "still refers to them they will stay in the repository."
rewind_only=1
else
echo "This update added new revisions after undoing existing revisions. That is"
echo "to say, the old revision is not a strict subset of the new revision. This"
echo "situation occurs when you --force push a change and generate a repository"
echo "containing something like this:"
echo ""
echo " * -- * -- B -- O -- O -- O ($oldrev)"
echo " \\"
echo " N -- N -- N ($newrev)"
echo ""
echo "When this happens we assume that you've already had alert emails for all"
echo "of the O revisions, and so we here report only the revisions in the N"
echo "branch from the common base, B."
fi
fi
echo ""
echo "Those revisions listed above that are new to this repository have"
echo "not appeared on any other notification email; so we list those"
echo "revisions in full, below."
if [ -z "$rewind_only" ]; then
echo "Those revisions listed above that are new to this repository have"
echo "not appeared on any other notification email; so we list those"
echo "revisions in full, below."
echo ""
echo $LOGBEGIN
git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $oldrev..$newrev
echo ""
echo $LOGBEGIN
git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $oldrev..$newrev
# XXX: Need a way of detecting whether git rev-list actually outputted
# anything, so that we can issue a "no new revisions added by this
# update" message
# XXX: Need a way of detecting whether git rev-list actually outputted
# anything, so that we can issue a "no new revisions added by this
# update" message
echo $LOGEND
echo $LOGEND
else
echo "No new revisions were added by this update."
fi
# The diffstat is shown from the old revision to the new revision. This
# is to show the truth of what happened in this change. There's no point
@@ -556,7 +587,7 @@ if [ -z "$GIT_DIR" ]; then
exit 1
fi
projectdesc=$(sed -e '1p' "$GIT_DIR/description")
projectdesc=$(sed -ne '1p' "$GIT_DIR/description")
# Check if the description is unchanged from it's default, and shorten it to a
# more manageable length if it is
if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null

425
convert.c
View File

@@ -1,5 +1,6 @@
#include "cache.h"
#include "attr.h"
#include "run-command.h"
/*
* convert.c - convert a file when checking it out and checking it in.
@@ -200,13 +201,350 @@ static char *crlf_to_worktree(const char *path, const char *src, unsigned long *
return buffer;
}
static int filter_buffer(const char *path, const char *src,
unsigned long size, const char *cmd)
{
/*
* Spawn cmd and feed the buffer contents through its stdin.
*/
struct child_process child_process;
int pipe_feed[2];
int write_err, status;
memset(&child_process, 0, sizeof(child_process));
if (pipe(pipe_feed) < 0) {
error("cannot create pipe to run external filter %s", cmd);
return 1;
}
child_process.pid = fork();
if (child_process.pid < 0) {
error("cannot fork to run external filter %s", cmd);
close(pipe_feed[0]);
close(pipe_feed[1]);
return 1;
}
if (!child_process.pid) {
dup2(pipe_feed[0], 0);
close(pipe_feed[0]);
close(pipe_feed[1]);
execlp("sh", "sh", "-c", cmd, NULL);
return 1;
}
close(pipe_feed[0]);
write_err = (write_in_full(pipe_feed[1], src, size) < 0);
if (close(pipe_feed[1]))
write_err = 1;
if (write_err)
error("cannot feed the input to external filter %s", cmd);
status = finish_command(&child_process);
if (status)
error("external filter %s failed %d", cmd, -status);
return (write_err || status);
}
static char *apply_filter(const char *path, const char *src,
unsigned long *sizep, const char *cmd)
{
/*
* Create a pipeline to have the command filter the buffer's
* contents.
*
* (child --> cmd) --> us
*/
const int SLOP = 4096;
int pipe_feed[2];
int status;
char *dst;
unsigned long dstsize, dstalloc;
struct child_process child_process;
if (!cmd)
return NULL;
memset(&child_process, 0, sizeof(child_process));
if (pipe(pipe_feed) < 0) {
error("cannot create pipe to run external filter %s", cmd);
return NULL;
}
fflush(NULL);
child_process.pid = fork();
if (child_process.pid < 0) {
error("cannot fork to run external filter %s", cmd);
close(pipe_feed[0]);
close(pipe_feed[1]);
return NULL;
}
if (!child_process.pid) {
dup2(pipe_feed[1], 1);
close(pipe_feed[0]);
close(pipe_feed[1]);
exit(filter_buffer(path, src, *sizep, cmd));
}
close(pipe_feed[1]);
dstalloc = *sizep;
dst = xmalloc(dstalloc);
dstsize = 0;
while (1) {
ssize_t numread = xread(pipe_feed[0], dst + dstsize,
dstalloc - dstsize);
if (numread <= 0) {
if (!numread)
break;
error("read from external filter %s failed", cmd);
free(dst);
dst = NULL;
break;
}
dstsize += numread;
if (dstalloc <= dstsize + SLOP) {
dstalloc = dstsize + SLOP;
dst = xrealloc(dst, dstalloc);
}
}
if (close(pipe_feed[0])) {
error("read from external filter %s failed", cmd);
free(dst);
dst = NULL;
}
status = finish_command(&child_process);
if (status) {
error("external filter %s failed %d", cmd, -status);
free(dst);
dst = NULL;
}
if (dst)
*sizep = dstsize;
return dst;
}
static struct convert_driver {
const char *name;
struct convert_driver *next;
char *smudge;
char *clean;
} *user_convert, **user_convert_tail;
static int read_convert_config(const char *var, const char *value)
{
const char *ep, *name;
int namelen;
struct convert_driver *drv;
/*
* External conversion drivers are configured using
* "filter.<name>.variable".
*/
if (prefixcmp(var, "filter.") || (ep = strrchr(var, '.')) == var + 6)
return 0;
name = var + 7;
namelen = ep - name;
for (drv = user_convert; drv; drv = drv->next)
if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
break;
if (!drv) {
char *namebuf;
drv = xcalloc(1, sizeof(struct convert_driver));
namebuf = xmalloc(namelen + 1);
memcpy(namebuf, name, namelen);
namebuf[namelen] = 0;
drv->name = namebuf;
drv->next = NULL;
*user_convert_tail = drv;
user_convert_tail = &(drv->next);
}
ep++;
/*
* filter.<name>.smudge and filter.<name>.clean specifies
* the command line:
*
* command-line
*
* The command-line will not be interpolated in any way.
*/
if (!strcmp("smudge", ep)) {
if (!value)
return error("%s: lacks value", var);
drv->smudge = strdup(value);
return 0;
}
if (!strcmp("clean", ep)) {
if (!value)
return error("%s: lacks value", var);
drv->clean = strdup(value);
return 0;
}
return 0;
}
static void setup_convert_check(struct git_attr_check *check)
{
static struct git_attr *attr_crlf;
static struct git_attr *attr_ident;
static struct git_attr *attr_filter;
if (!attr_crlf)
if (!attr_crlf) {
attr_crlf = git_attr("crlf", 4);
check->attr = attr_crlf;
attr_ident = git_attr("ident", 5);
attr_filter = git_attr("filter", 6);
user_convert_tail = &user_convert;
git_config(read_convert_config);
}
check[0].attr = attr_crlf;
check[1].attr = attr_ident;
check[2].attr = attr_filter;
}
static int count_ident(const char *cp, unsigned long size)
{
/*
* "$ident: 0000000000000000000000000000000000000000 $" <=> "$ident$"
*/
int cnt = 0;
char ch;
while (size) {
ch = *cp++;
size--;
if (ch != '$')
continue;
if (size < 6)
break;
if (memcmp("ident", cp, 5))
continue;
ch = cp[5];
cp += 6;
size -= 6;
if (ch == '$')
cnt++; /* $ident$ */
if (ch != ':')
continue;
/*
* "$ident: ... "; scan up to the closing dollar sign and discard.
*/
while (size) {
ch = *cp++;
size--;
if (ch == '$') {
cnt++;
break;
}
}
}
return cnt;
}
static char *ident_to_git(const char *path, const char *src, unsigned long *sizep, int ident)
{
int cnt;
unsigned long size;
char *dst, *buf;
if (!ident)
return NULL;
size = *sizep;
cnt = count_ident(src, size);
if (!cnt)
return NULL;
buf = xmalloc(size);
for (dst = buf; size; size--) {
char ch = *src++;
*dst++ = ch;
if ((ch == '$') && (6 <= size) &&
!memcmp("ident:", src, 6)) {
unsigned long rem = size - 6;
const char *cp = src + 6;
do {
ch = *cp++;
if (ch == '$')
break;
rem--;
} while (rem);
if (!rem)
continue;
memcpy(dst, "ident$", 6);
dst += 6;
size -= (cp - src);
src = cp;
}
}
*sizep = dst - buf;
return buf;
}
static char *ident_to_worktree(const char *path, const char *src, unsigned long *sizep, int ident)
{
int cnt;
unsigned long size;
char *dst, *buf;
unsigned char sha1[20];
if (!ident)
return NULL;
size = *sizep;
cnt = count_ident(src, size);
if (!cnt)
return NULL;
hash_sha1_file(src, size, "blob", sha1);
buf = xmalloc(size + cnt * 43);
for (dst = buf; size; size--) {
const char *cp;
char ch = *src++;
*dst++ = ch;
if ((ch != '$') || (size < 6) || memcmp("ident", src, 5))
continue;
if (src[5] == ':') {
/* discard up to but not including the closing $ */
unsigned long rem = size - 6;
cp = src + 6;
do {
ch = *cp++;
if (ch == '$')
break;
rem--;
} while (rem);
if (!rem)
continue;
size -= (cp - src);
} else if (src[5] == '$')
cp = src + 5;
else
continue;
memcpy(dst, "ident: ", 7);
dst += 7;
memcpy(dst, sha1_to_hex(sha1), 40);
dst += 40;
*dst++ = ' ';
size -= (cp - src);
src = cp;
*dst++ = *src++;
size--;
}
*sizep = dst - buf;
return buf;
}
static int git_path_check_crlf(const char *path, struct git_attr_check *check)
@@ -224,26 +562,93 @@ static int git_path_check_crlf(const char *path, struct git_attr_check *check)
return CRLF_GUESS;
}
static struct convert_driver *git_path_check_convert(const char *path,
struct git_attr_check *check)
{
const char *value = check->value;
struct convert_driver *drv;
if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
return NULL;
for (drv = user_convert; drv; drv = drv->next)
if (!strcmp(value, drv->name))
return drv;
return NULL;
}
static int git_path_check_ident(const char *path, struct git_attr_check *check)
{
const char *value = check->value;
return !!ATTR_TRUE(value);
}
char *convert_to_git(const char *path, const char *src, unsigned long *sizep)
{
struct git_attr_check check[1];
struct git_attr_check check[3];
int crlf = CRLF_GUESS;
int ident = 0;
char *filter = NULL;
char *buf, *buf2;
setup_convert_check(check);
if (!git_checkattr(path, 1, check)) {
crlf = git_path_check_crlf(path, check);
if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
struct convert_driver *drv;
crlf = git_path_check_crlf(path, check + 0);
ident = git_path_check_ident(path, check + 1);
drv = git_path_check_convert(path, check + 2);
if (drv && drv->clean)
filter = drv->clean;
}
return crlf_to_git(path, src, sizep, crlf);
buf = apply_filter(path, src, sizep, filter);
buf2 = crlf_to_git(path, buf ? buf : src, sizep, crlf);
if (buf2) {
free(buf);
buf = buf2;
}
buf2 = ident_to_git(path, buf ? buf : src, sizep, ident);
if (buf2) {
free(buf);
buf = buf2;
}
return buf;
}
char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep)
{
struct git_attr_check check[1];
struct git_attr_check check[3];
int crlf = CRLF_GUESS;
int ident = 0;
char *filter = NULL;
char *buf, *buf2;
setup_convert_check(check);
if (!git_checkattr(path, 1, check)) {
crlf = git_path_check_crlf(path, check);
if (!git_checkattr(path, ARRAY_SIZE(check), check)) {
struct convert_driver *drv;
crlf = git_path_check_crlf(path, check + 0);
ident = git_path_check_ident(path, check + 1);
drv = git_path_check_convert(path, check + 2);
if (drv && drv->smudge)
filter = drv->smudge;
}
return crlf_to_worktree(path, src, sizep, crlf);
buf = ident_to_worktree(path, src, sizep, ident);
buf2 = crlf_to_worktree(path, buf ? buf : src, sizep, crlf);
if (buf2) {
free(buf);
buf = buf2;
}
buf2 = apply_filter(path, buf ? buf : src, sizep, filter);
if (buf2) {
free(buf);
buf = buf2;
}
return buf;
}

35
date.c
View File

@@ -55,6 +55,32 @@ static struct tm *time_to_tm(unsigned long time, int tz)
return gmtime(&t);
}
/*
* What value of "tz" was in effect back then at "time" in the
* local timezone?
*/
static int local_tzoffset(unsigned long time)
{
time_t t, t_local;
struct tm tm;
int offset, eastwest;
t = time;
localtime_r(&t, &tm);
t_local = my_mktime(&tm);
if (t_local < t) {
eastwest = -1;
offset = t - t_local;
} else {
eastwest = 1;
offset = t_local - t;
}
offset /= 60; /* in minutes */
offset = (offset % 60) + ((offset / 60) * 100);
return offset * eastwest;
}
const char *show_date(unsigned long time, int tz, enum date_mode mode)
{
struct tm *tm;
@@ -102,6 +128,9 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
/* Else fall back on absolute format.. */
}
if (mode == DATE_LOCAL)
tz = local_tzoffset(time);
tm = time_to_tm(time, tz);
if (!tm)
return NULL;
@@ -109,12 +138,14 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
sprintf(timebuf, "%04d-%02d-%02d", tm->tm_year + 1900,
tm->tm_mon + 1, tm->tm_mday);
else
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+05d",
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d%c%+05d",
weekday_names[tm->tm_wday],
month_names[tm->tm_mon],
tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec,
tm->tm_year + 1900, tz);
tm->tm_year + 1900,
(mode == DATE_LOCAL) ? 0 : ' ',
tz);
return timebuf;
}

View File

@@ -1,8 +1,8 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
#ifndef _DIFFCORE_H_
#define _DIFFCORE_H_
#ifndef DIFFCORE_H
#define DIFFCORE_H
/* This header file is internal between diff.c and its diff transformers
* (e.g. diffcore-rename, diffcore-pickaxe). Never include this header

14
entry.c
View File

@@ -1,7 +1,7 @@
#include "cache.h"
#include "blob.h"
static void create_directories(const char *path, struct checkout *state)
static void create_directories(const char *path, const struct checkout *state)
{
int len = strlen(path);
char *buf = xmalloc(len + 1);
@@ -33,7 +33,7 @@ static void remove_subtree(const char *path)
char *name;
if (!dir)
die("cannot opendir %s", path);
die("cannot opendir %s (%s)", path, strerror(errno));
strcpy(pathbuf, path);
name = pathbuf + strlen(path);
*name++ = '/';
@@ -45,15 +45,15 @@ static void remove_subtree(const char *path)
continue;
strcpy(name, de->d_name);
if (lstat(pathbuf, &st))
die("cannot lstat %s", pathbuf);
die("cannot lstat %s (%s)", pathbuf, strerror(errno));
if (S_ISDIR(st.st_mode))
remove_subtree(pathbuf);
else if (unlink(pathbuf))
die("cannot unlink %s", pathbuf);
die("cannot unlink %s (%s)", pathbuf, strerror(errno));
}
closedir(dir);
if (rmdir(path))
die("cannot rmdir %s", path);
die("cannot rmdir %s (%s)", path, strerror(errno));
}
static int create_file(const char *path, unsigned int mode)
@@ -75,7 +75,7 @@ static void *read_blob_entry(struct cache_entry *ce, const char *path, unsigned
return NULL;
}
static int write_entry(struct cache_entry *ce, char *path, struct checkout *state, int to_tempfile)
static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
{
int fd;
long wrote;
@@ -163,7 +163,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
return 0;
}
int checkout_entry(struct cache_entry *ce, struct checkout *state, char *topath)
int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
{
static char path[PATH_MAX + 1];
struct stat st;

View File

@@ -1,5 +1,5 @@
#ifndef __GIT_EXEC_CMD_H_
#define __GIT_EXEC_CMD_H_
#ifndef GIT_EXEC_CMD_H
#define GIT_EXEC_CMD_H
extern void git_set_exec_path(const char *exec_path);
extern const char* git_exec_path(void);
@@ -8,4 +8,4 @@ extern int execl_git_cmd(const char *cmd, ...);
extern int spawnv_git_cmd(const char **argv, int pin[2], int pout[2]);
#endif /* __GIT_EXEC_CMD_H_ */
#endif /* GIT_EXEC_CMD_H */

View File

@@ -673,7 +673,7 @@ static void fixup_header_footer(void)
buf = xmalloc(buf_sz);
for (;;) {
size_t n = xread(pack_fd, buf, buf_sz);
ssize_t n = xread(pack_fd, buf, buf_sz);
if (!n)
break;
if (n < 0)
@@ -904,6 +904,12 @@ static int store_object(
if (e->offset) {
duplicate_count_by_type[type]++;
return 1;
} else if (find_sha1_pack(sha1, packed_git)) {
e->type = type;
e->pack_id = MAX_PACK_ID;
e->offset = 1; /* just not zero! */
duplicate_count_by_type[type]++;
return 1;
}
if (last && last->data && last->depth < max_depth) {
@@ -1193,6 +1199,8 @@ static int tree_content_set(
n = slash1 - p;
else
n = strlen(p);
if (!n)
die("Empty path component found in input");
for (i = 0; i < t->entry_count; i++) {
e = t->entries[i];
@@ -2021,6 +2029,7 @@ static void import_marks(const char *input_file)
e = insert_object(sha1);
e->type = type;
e->pack_id = MAX_PACK_ID;
e->offset = 1; /* just not zero! */
}
insert_mark(mark, e);
}
@@ -2086,6 +2095,7 @@ int main(int argc, const char **argv)
if (i != argc)
usage(fast_import_usage);
prepare_packed_git();
start_packfile();
for (;;) {
read_next_command();

View File

@@ -78,7 +78,7 @@ do
git-mailinfo $keep_subject $utf8 \
.dotest/msg .dotest/patch <$i >.dotest/info || exit 1
test -s .dotest/patch || {
echo "Patch is empty. Was is split wrong?"
echo "Patch is empty. Was it split wrong?"
exit 1
}
git-stripspace < .dotest/msg > .dotest/msg-clean

View File

@@ -171,13 +171,13 @@ extern size_t gitstrlcpy(char *, const char *, size_t);
extern uintmax_t gitstrtoumax(const char *, char **, int);
#endif
extern void release_pack_memory(size_t);
extern void release_pack_memory(size_t, int);
static inline char* xstrdup(const char *str)
{
char *ret = strdup(str);
if (!ret) {
release_pack_memory(strlen(str) + 1);
release_pack_memory(strlen(str) + 1, -1);
ret = strdup(str);
if (!ret)
die("Out of memory, strdup failed");
@@ -191,7 +191,7 @@ static inline void *xmalloc(size_t size)
if (!ret && !size)
ret = malloc(1);
if (!ret) {
release_pack_memory(size);
release_pack_memory(size, -1);
ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
@@ -210,7 +210,7 @@ static inline void *xrealloc(void *ptr, size_t size)
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret) {
release_pack_memory(size);
release_pack_memory(size, -1);
ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
@@ -226,7 +226,7 @@ static inline void *xcalloc(size_t nmemb, size_t size)
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
if (!ret) {
release_pack_memory(nmemb * size);
release_pack_memory(nmemb * size, -1);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
@@ -243,7 +243,7 @@ static inline void *xmmap(void *start, size_t length,
if (ret == MAP_FAILED) {
if (!length)
return NULL;
release_pack_memory(length);
release_pack_memory(length, fd);
ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED)
die("Out of memory? mmap failed: %s", strerror(errno));

View File

@@ -74,7 +74,7 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
echo $patch_name
(cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
test -s .dotest/patch || {
echo "Patch is empty. Was is split wrong?"
echo "Patch is empty. Was it split wrong?"
exit 1
}

View File

@@ -77,6 +77,10 @@ Options:
--quiet Make git-send-email less verbose. One line per email
should be all that is output.
--dry-run Do everything except actually send the emails.
--envelope-sender Specify the envelope sender used to send the emails.
EOT
exit(1);
}
@@ -137,6 +141,7 @@ my (@to,@cc,@initial_cc,@bcclist,@xh,
my ($chain_reply_to, $quiet, $suppress_from, $no_signed_off_cc,
$dry_run) = (1, 0, 0, 0, 0);
my $smtp_server;
my $envelope_sender;
# Example reply to:
#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
@@ -175,6 +180,7 @@ my $rc = GetOptions("from=s" => \$from,
"suppress-from" => \$suppress_from,
"no-signed-off-cc|no-signed-off-by-cc" => \$no_signed_off_cc,
"dry-run" => \$dry_run,
"envelope-sender=s" => \$envelope_sender,
);
unless ($rc) {
@@ -268,6 +274,7 @@ sub expand_aliases {
}
@to = expand_aliases(@to);
@to = (map { sanitize_address_rfc822($_) } @to);
@initial_cc = expand_aliases(@initial_cc);
@bcclist = expand_aliases(@bcclist);
@@ -377,7 +384,7 @@ if (@files) {
}
# Variables we set as part of the loop over files
our ($message_id, $cc, %mail, $subject, $reply_to, $references, $message);
our ($message_id, %mail, $subject, $reply_to, $references, $message);
sub extract_valid_address {
my $address = shift;
@@ -418,7 +425,6 @@ sub make_message_id
$cc = "";
$time = time - scalar $#files;
sub unquote_rfc2047 {
@@ -430,26 +436,37 @@ sub unquote_rfc2047 {
return "$_";
}
# If an address contains a . in the name portion, the name must be quoted.
sub sanitize_address_rfc822
{
my ($recipient) = @_;
my ($recipient_name) = ($recipient =~ /^(.*?)\s+</);
if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) {
my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/);
$recipient = "\"$name\"$addr";
}
return $recipient;
}
sub send_message
{
my @recipients = unique_email_list(@to);
@cc = (map { sanitize_address_rfc822($_) } @cc);
my $to = join (",\n\t", @recipients);
@recipients = unique_email_list(@recipients,@cc,@bcclist);
@recipients = (map { extract_valid_address($_) } @recipients);
my $date = format_2822_time($time++);
my $gitversion = '@@GIT_VERSION@@';
if ($gitversion =~ m/..GIT_VERSION../) {
$gitversion = Git::version();
}
my ($author_name) = ($from =~ /^(.*?)\s+</);
if ($author_name && $author_name =~ /\./ && $author_name !~ /^".*"$/) {
my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
$from = "\"$name\"$addr";
}
my $cc = join(", ", unique_email_list(@cc));
my $ccline = "";
if ($cc ne '') {
$ccline = "\nCc: $cc";
}
$from = sanitize_address_rfc822($from);
my $header = "From: $from
To: $to${ccline}
Subject: $subject
@@ -466,22 +483,27 @@ X-Mailer: git-send-email $gitversion
$header .= join("\n", @xh) . "\n";
}
my @sendmail_parameters = ('-i', @recipients);
my $raw_from = $from;
$raw_from = $envelope_sender if (defined $envelope_sender);
$raw_from = extract_valid_address($raw_from);
unshift (@sendmail_parameters,
'-f', $raw_from) if(defined $envelope_sender);
if ($dry_run) {
# We don't want to send the email.
} elsif ($smtp_server =~ m#^/#) {
my $pid = open my $sm, '|-';
defined $pid or die $!;
if (!$pid) {
exec($smtp_server,'-i',
map { extract_valid_address($_) }
@recipients) or die $!;
exec($smtp_server, @sendmail_parameters) or die $!;
}
print $sm "$header\n$message";
close $sm or die $?;
} else {
require Net::SMTP;
$smtp ||= Net::SMTP->new( $smtp_server );
$smtp->mail( $from ) or die $smtp->message;
$smtp->mail( $raw_from ) or die $smtp->message;
$smtp->to( @recipients ) or die $smtp->message;
$smtp->data or die $smtp->message;
$smtp->datasend("$header\n$message") or die $smtp->message;
@@ -489,13 +511,15 @@ X-Mailer: git-send-email $gitversion
$smtp->ok or die "Failed to send $subject\n".$smtp->message;
}
if ($quiet) {
printf "Sent %s\n", $subject;
printf (($dry_run ? "Dry-" : "")."Sent %s\n", $subject);
} else {
print "OK. Log says:\nDate: $date\n";
if ($smtp) {
print (($dry_run ? "Dry-" : "")."OK. Log says:\nDate: $date\n");
if ($smtp_server !~ m#^/#) {
print "Server: $smtp_server\n";
print "MAIL FROM:<$raw_from>\n";
print "RCPT TO:".join(',',(map { "<$_>" } @recipients))."\n";
} else {
print "Sendmail: $smtp_server\n";
print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
}
print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
if ($smtp) {
@@ -590,7 +614,6 @@ foreach my $t (@files) {
$message = "From: $author_not_sender\n\n$message";
}
$cc = join(", ", unique_email_list(@cc));
send_message();

View File

@@ -141,6 +141,8 @@ my %cmd = (
'color' => \$Git::SVN::Log::color,
'pager=s' => \$Git::SVN::Log::pager,
} ],
'find-rev' => [ \&cmd_find_rev, "Translate between SVN revision numbers and tree-ish",
{ } ],
'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory",
{ 'merge|m|M' => \$_merge,
'verbose|v' => \$_verbose,
@@ -428,6 +430,27 @@ sub cmd_dcommit {
command_noisy(@finish, $gs->refname);
}
sub cmd_find_rev {
my $revision_or_hash = shift;
my $result;
if ($revision_or_hash =~ /^r\d+$/) {
my $head = shift;
$head ||= 'HEAD';
my @refs;
my (undef, undef, undef, $gs) = working_head_info($head, \@refs);
unless ($gs) {
die "Unable to determine upstream SVN information from ",
"$head history\n";
}
my $desired_revision = substr($revision_or_hash, 1);
$result = $gs->rev_db_get($desired_revision);
} else {
my (undef, $rev, undef) = cmt_metadata($revision_or_hash);
$result = $rev;
}
print "$result\n" if $result;
}
sub cmd_rebase {
command_noisy(qw/update-index --refresh/);
my ($url, $rev, $uuid, $gs) = working_head_info('HEAD');
@@ -771,19 +794,19 @@ sub cmt_metadata {
sub working_head_info {
my ($head, $refs) = @_;
my ($fh, $ctx) = command_output_pipe('rev-list', $head);
while (<$fh>) {
chomp;
my ($url, $rev, $uuid) = cmt_metadata($_);
while (my $hash = <$fh>) {
chomp($hash);
my ($url, $rev, $uuid) = cmt_metadata($hash);
if (defined $url && defined $rev) {
if (my $gs = Git::SVN->find_by_url($url)) {
my $c = $gs->rev_db_get($rev);
if ($c && $c eq $_) {
if ($c && $c eq $hash) {
close $fh; # break the pipe
return ($url, $rev, $uuid, $gs);
}
}
}
unshift @$refs, $_ if $refs;
unshift @$refs, $hash if $refs;
}
command_close_pipe($fh, $ctx);
(undef, undef, undef, undef);
@@ -1064,7 +1087,10 @@ sub init_remote_config {
sub find_by_url { # repos_root and, path are optional
my ($class, $full_url, $repos_root, $path) = @_;
return undef unless defined $full_url;
remove_username($full_url);
remove_username($repos_root) if defined $repos_root;
my $remotes = read_all_remotes();
if (defined $full_url && defined $repos_root && !defined $path) {
$path = $full_url;
@@ -1072,6 +1098,7 @@ sub find_by_url { # repos_root and, path are optional
}
foreach my $repo_id (keys %$remotes) {
my $u = $remotes->{$repo_id}->{url} or next;
remove_username($u);
next if defined $repos_root && $repos_root ne $u;
my $fetch = $remotes->{$repo_id}->{fetch} || {};
@@ -1866,11 +1893,14 @@ sub make_log_entry {
} elsif ($self->use_svnsync_props) {
my $full_url = $self->svnsync->{url};
$full_url .= "/$self->{path}" if length $self->{path};
remove_username($full_url);
my $uuid = $self->svnsync->{uuid};
$log_entry{metadata} = "$full_url\@$rev $uuid";
$email ||= "$author\@$uuid"
} else {
$log_entry{metadata} = $self->metadata_url. "\@$rev " .
my $url = $self->metadata_url;
remove_username($url);
$log_entry{metadata} = "$url\@$rev " .
$self->ra->get_uuid;
$email ||= "$author\@" . $self->ra->get_uuid;
}
@@ -2439,9 +2469,9 @@ sub close_file {
my $got = $md5->hexdigest;
die "Checksum mismatch: $path\n",
"expected: $exp\n got: $got\n" if ($got ne $exp);
seek($fh, 0, 0) or croak $!;
sysseek($fh, 0, 0) or croak $!;
if ($fb->{mode_b} == 120000) {
read($fh, my $buf, 5) == 5 or croak $!;
sysread($fh, my $buf, 5) == 5 or croak $!;
$buf eq 'link ' or die "$path has mode 120000",
"but is not a link\n";
}

1
http.c
View File

@@ -300,6 +300,7 @@ void http_cleanup(void)
curl_global_cleanup();
curl_slist_free_all(pragma_header);
pragma_header = NULL;
}
struct active_request_slot *get_active_slot(void)

View File

@@ -201,6 +201,8 @@ const char *fmt_ident(const char *name, const char *email,
setup_ident();
if (!name)
name = git_default_name;
if (!email)
email = getenv("EMAIL");
if (!email)
email = git_default_email;

View File

@@ -267,7 +267,7 @@ void show_log(struct rev_info *opt, const char *sep)
if (opt->reflog_info) {
show_reflog_message(opt->reflog_info,
opt->commit_format == CMIT_FMT_ONELINE,
opt->relative_date);
opt->date_mode);
if (opt->commit_format == CMIT_FMT_ONELINE) {
printf("%s", sep);
return;
@@ -280,7 +280,7 @@ void show_log(struct rev_info *opt, const char *sep)
*/
len = pretty_print_commit(opt->commit_format, commit, ~0u, this_header,
sizeof(this_header), abbrev, subject,
extra_headers, opt->relative_date);
extra_headers, opt->date_mode);
if (opt->add_signoff)
len = append_signoff(this_header, sizeof(this_header), len,

92
mailmap.c Normal file
View File

@@ -0,0 +1,92 @@
#include "cache.h"
#include "path-list.h"
#include "mailmap.h"
int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev)
{
char buffer[1024];
FILE *f = fopen(filename, "r");
if (f == NULL)
return 1;
while (fgets(buffer, sizeof(buffer), f) != NULL) {
char *end_of_name, *left_bracket, *right_bracket;
char *name, *email;
int i;
if (buffer[0] == '#') {
static const char abbrev[] = "# repo-abbrev:";
int abblen = sizeof(abbrev) - 1;
int len = strlen(buffer);
if (!repo_abbrev)
continue;
if (len && buffer[len - 1] == '\n')
buffer[--len] = 0;
if (!strncmp(buffer, abbrev, abblen)) {
char *cp;
if (repo_abbrev)
free(*repo_abbrev);
*repo_abbrev = xmalloc(len);
for (cp = buffer + abblen; isspace(*cp); cp++)
; /* nothing */
strcpy(*repo_abbrev, cp);
}
continue;
}
if ((left_bracket = strchr(buffer, '<')) == NULL)
continue;
if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
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 */
if (end_of_name == buffer)
continue;
name = xmalloc(end_of_name - buffer + 1);
strlcpy(name, buffer, end_of_name - buffer + 1);
email = xmalloc(right_bracket - left_bracket);
for (i = 0; i < right_bracket - left_bracket - 1; i++)
email[i] = tolower(left_bracket[i + 1]);
email[right_bracket - left_bracket - 1] = '\0';
path_list_insert(email, map)->util = name;
}
fclose(f);
return 0;
}
int map_email(struct path_list *map, const char *email, char *name, int maxlen)
{
char *p;
struct path_list_item *item;
char buf[1024], *mailbuf;
int i;
/* autocomplete common developers */
p = strchr(email, '>');
if (!p)
return 0;
if (p - email + 1 < sizeof(buf))
mailbuf = buf;
else
mailbuf = xmalloc(p - email + 1);
/* downcase the email address */
for (i = 0; i < p - email; i++)
mailbuf[i] = tolower(email[i]);
mailbuf[i] = 0;
item = path_list_lookup(mailbuf, map);
if (mailbuf != buf)
free(mailbuf);
if (item != NULL) {
const char *realname = (const char *)item->util;
strlcpy(name, realname, maxlen);
return 1;
}
return 0;
}

7
mailmap.h Normal file
View File

@@ -0,0 +1,7 @@
#ifndef MAILMAP_H
#define MAILMAP_H
int read_mailmap(struct path_list *map, const char *filename, char **repo_abbrev);
int map_email(struct path_list *mailmap, const char *email, char *name, int maxlen);
#endif

View File

@@ -1342,20 +1342,31 @@ static int process_renames(struct path_list *a_renames,
mfi = merge_file(o, a, b,
a_branch, b_branch);
if (mfi.merge || !mfi.clean)
output(1, "Renamed %s => %s", ren1_src, ren1_dst);
if (mfi.merge)
output(2, "Auto-merged %s", ren1_dst);
if (!mfi.clean) {
output(1, "CONFLICT (rename/modify): Merge conflict in %s",
ren1_dst);
clean_merge = 0;
if (mfi.clean &&
sha_eq(mfi.sha, ren1->pair->two->sha1) &&
mfi.mode == ren1->pair->two->mode)
/*
* This messaged is part of
* t6022 test. If you change
* it update the test too.
*/
output(3, "Skipped %s (merged same as existing)", ren1_dst);
else {
if (mfi.merge || !mfi.clean)
output(1, "Renamed %s => %s", ren1_src, ren1_dst);
if (mfi.merge)
output(2, "Auto-merged %s", ren1_dst);
if (!mfi.clean) {
output(1, "CONFLICT (rename/modify): Merge conflict in %s",
ren1_dst);
clean_merge = 0;
if (!index_only)
update_stages(ren1_dst,
o, a, b, 1);
if (!index_only)
update_stages(ren1_dst,
o, a, b, 1);
}
update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
}
update_file(mfi.clean, mfi.sha, mfi.mode, ren1_dst);
}
}
}

View File

@@ -230,6 +230,11 @@ int object_list_contains(struct object_list *list, struct object *obj)
}
void add_object_array(struct object *obj, const char *name, struct object_array *array)
{
add_object_array_with_mode(obj, name, array, S_IFINVALID);
}
void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode)
{
unsigned nr = array->nr;
unsigned alloc = array->alloc;
@@ -243,5 +248,6 @@ void add_object_array(struct object *obj, const char *name, struct object_array
}
objects[nr].item = obj;
objects[nr].name = name;
objects[nr].mode = mode;
array->nr = ++nr;
}

View File

@@ -17,6 +17,7 @@ struct object_array {
struct object_array_entry {
struct object *item;
const char *name;
unsigned mode;
} *objects;
};
@@ -77,5 +78,6 @@ int object_list_contains(struct object_list *list, struct object *obj);
/* Object array handling .. */
void add_object_array(struct object *obj, const char *name, struct object_array *array);
void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
#endif /* OBJECT_H */

View File

@@ -1,5 +1,5 @@
#ifndef _PATH_LIST_H_
#define _PATH_LIST_H_
#ifndef PATH_LIST_H
#define PATH_LIST_H
struct path_list_item {
char *path;
@@ -19,4 +19,4 @@ void path_list_clear(struct path_list *list, int free_items);
struct path_list_item *path_list_insert(const char *path, struct path_list *list);
struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
#endif /* _PATH_LIST_H_ */
#endif /* PATH_LIST_H */

View File

@@ -1,5 +1,5 @@
#ifndef __progress_h__
#define __progress_h__
#ifndef PROGRESS_H
#define PROGRESS_H
struct progress {
const char *prefix;

View File

@@ -3,6 +3,7 @@
*
* Copyright (C) Linus Torvalds, 2005
*/
#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "cache-tree.h"
#include "refs.h"
@@ -19,14 +20,7 @@
#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
struct cache_entry **active_cache;
static time_t index_file_timestamp;
unsigned int active_nr, active_alloc, active_cache_changed;
struct cache_tree *active_cache_tree;
static void *cache_mmap;
static size_t cache_mmap_size;
struct index_state the_index;
/*
* This only updates the "non-critical" parts of the directory
@@ -196,7 +190,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
return changed;
}
int ce_match_stat(struct cache_entry *ce, struct stat *st, int options)
int ie_match_stat(struct index_state *istate,
struct cache_entry *ce, struct stat *st, int options)
{
unsigned int changed;
int ignore_valid = options & 01;
@@ -228,8 +223,8 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st, int options)
* carefully than others.
*/
if (!changed &&
index_file_timestamp &&
index_file_timestamp <= ntohl(ce->ce_mtime.sec)) {
istate->timestamp &&
istate->timestamp <= ntohl(ce->ce_mtime.sec)) {
if (assume_racy_is_modified)
changed |= DATA_CHANGED;
else
@@ -239,10 +234,11 @@ int ce_match_stat(struct cache_entry *ce, struct stat *st, int options)
return changed;
}
int ce_modified(struct cache_entry *ce, struct stat *st, int really)
int ie_modified(struct index_state *istate,
struct cache_entry *ce, struct stat *st, int really)
{
int changed, changed_fs;
changed = ce_match_stat(ce, st, really);
changed = ie_match_stat(istate, ce, st, really);
if (!changed)
return 0;
/*
@@ -310,15 +306,15 @@ int cache_name_compare(const char *name1, int flags1, const char *name2, int fla
return 0;
}
int cache_name_pos(const char *name, int namelen)
int index_name_pos(struct index_state *istate, const char *name, int namelen)
{
int first, last;
first = 0;
last = active_nr;
last = istate->cache_nr;
while (last > first) {
int next = (last + first) >> 1;
struct cache_entry *ce = active_cache[next];
struct cache_entry *ce = istate->cache[next];
int cmp = cache_name_compare(name, namelen, ce->name, ntohs(ce->ce_flags));
if (!cmp)
return next;
@@ -332,27 +328,29 @@ int cache_name_pos(const char *name, int namelen)
}
/* Remove entry, return true if there are more entries to go.. */
int remove_cache_entry_at(int pos)
int remove_index_entry_at(struct index_state *istate, int pos)
{
active_cache_changed = 1;
active_nr--;
if (pos >= active_nr)
istate->cache_changed = 1;
istate->cache_nr--;
if (pos >= istate->cache_nr)
return 0;
memmove(active_cache + pos, active_cache + pos + 1, (active_nr - pos) * sizeof(struct cache_entry *));
memmove(istate->cache + pos,
istate->cache + pos + 1,
(istate->cache_nr - pos) * sizeof(struct cache_entry *));
return 1;
}
int remove_file_from_cache(const char *path)
int remove_file_from_index(struct index_state *istate, const char *path)
{
int pos = cache_name_pos(path, strlen(path));
int pos = index_name_pos(istate, path, strlen(path));
if (pos < 0)
pos = -pos-1;
while (pos < active_nr && !strcmp(active_cache[pos]->name, path))
remove_cache_entry_at(pos);
while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
remove_index_entry_at(istate, pos);
return 0;
}
int add_file_to_cache(const char *path, int verbose)
int add_file_to_index(struct index_state *istate, const char *path, int verbose)
{
int size, namelen;
struct stat st;
@@ -382,19 +380,19 @@ int add_file_to_cache(const char *path, int verbose)
* from it, otherwise assume unexecutable regular file.
*/
struct cache_entry *ent;
int pos = cache_name_pos(path, namelen);
int pos = index_name_pos(istate, path, namelen);
ent = (0 <= pos) ? active_cache[pos] : NULL;
ent = (0 <= pos) ? istate->cache[pos] : NULL;
ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
}
if (index_path(ce->sha1, path, &st, 1))
die("unable to index file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
die("unable to add %s to index",path);
if (verbose)
printf("add '%s'\n", path);
cache_tree_invalidate_path(active_cache_tree, path);
cache_tree_invalidate_path(istate->cache_tree, path);
return 0;
}
@@ -498,15 +496,16 @@ inside:
* Do we have another file that has the beginning components being a
* proper superset of the name we're trying to add?
*/
static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replace)
static int has_file_name(struct index_state *istate,
const struct cache_entry *ce, int pos, int ok_to_replace)
{
int retval = 0;
int len = ce_namelen(ce);
int stage = ce_stage(ce);
const char *name = ce->name;
while (pos < active_nr) {
struct cache_entry *p = active_cache[pos++];
while (pos < istate->cache_nr) {
struct cache_entry *p = istate->cache[pos++];
if (len >= ce_namelen(p))
break;
@@ -521,7 +520,7 @@ static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replac
retval = -1;
if (!ok_to_replace)
break;
remove_cache_entry_at(--pos);
remove_index_entry_at(istate, --pos);
}
return retval;
}
@@ -530,7 +529,8 @@ static int has_file_name(const struct cache_entry *ce, int pos, int ok_to_replac
* Do we have another file with a pathname that is a proper
* subset of the name we're trying to add?
*/
static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace)
static int has_dir_name(struct index_state *istate,
const struct cache_entry *ce, int pos, int ok_to_replace)
{
int retval = 0;
int stage = ce_stage(ce);
@@ -548,7 +548,7 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
}
len = slash - name;
pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage)));
pos = index_name_pos(istate, name, ntohs(create_ce_flags(len, stage)));
if (pos >= 0) {
/*
* Found one, but not so fast. This could
@@ -558,11 +558,11 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
* it is Ok to have a directory at the same
* path.
*/
if (stage || active_cache[pos]->ce_mode) {
if (stage || istate->cache[pos]->ce_mode) {
retval = -1;
if (!ok_to_replace)
break;
remove_cache_entry_at(pos);
remove_index_entry_at(istate, pos);
continue;
}
}
@@ -574,8 +574,8 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
* already matches the sub-directory, then we know
* we're ok, and we can exit.
*/
while (pos < active_nr) {
struct cache_entry *p = active_cache[pos];
while (pos < istate->cache_nr) {
struct cache_entry *p = istate->cache[pos];
if ((ce_namelen(p) <= len) ||
(p->name[len] != '/') ||
memcmp(p->name, name, len))
@@ -602,7 +602,9 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
* from the cache so the caller should recompute the insert position.
* When this happens, we return non-zero.
*/
static int check_file_directory_conflict(const struct cache_entry *ce, int pos, int ok_to_replace)
static int check_file_directory_conflict(struct index_state *istate,
const struct cache_entry *ce,
int pos, int ok_to_replace)
{
int retval;
@@ -617,28 +619,28 @@ static int check_file_directory_conflict(const struct cache_entry *ce, int pos,
* first, since removing those will not change the position
* in the array.
*/
retval = has_file_name(ce, pos, ok_to_replace);
retval = has_file_name(istate, ce, pos, ok_to_replace);
/*
* Then check if the path might have a clashing sub-directory
* before it.
*/
return retval + has_dir_name(ce, pos, ok_to_replace);
return retval + has_dir_name(istate, ce, pos, ok_to_replace);
}
int add_cache_entry(struct cache_entry *ce, int option)
int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
{
int pos;
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
/* existing match? Just replace it. */
if (pos >= 0) {
active_cache_changed = 1;
active_cache[pos] = ce;
istate->cache_changed = 1;
istate->cache[pos] = ce;
return 0;
}
pos = -pos-1;
@@ -647,10 +649,10 @@ int add_cache_entry(struct cache_entry *ce, int option)
* Inserting a merged entry ("stage 0") into the index
* will always replace all non-merged entries..
*/
if (pos < active_nr && ce_stage(ce) == 0) {
while (ce_same_name(active_cache[pos], ce)) {
if (pos < istate->cache_nr && ce_stage(ce) == 0) {
while (ce_same_name(istate->cache[pos], ce)) {
ok_to_add = 1;
if (!remove_cache_entry_at(pos))
if (!remove_index_entry_at(istate, pos))
break;
}
}
@@ -661,25 +663,29 @@ int add_cache_entry(struct cache_entry *ce, int option)
return -1;
if (!skip_df_check &&
check_file_directory_conflict(ce, pos, ok_to_replace)) {
check_file_directory_conflict(istate, ce, pos, ok_to_replace)) {
if (!ok_to_replace)
return error("'%s' appears as both a file and as a directory", ce->name);
pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
return error("'%s' appears as both a file and as a directory",
ce->name);
pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
pos = -pos-1;
}
/* Make sure the array is big enough .. */
if (active_nr == active_alloc) {
active_alloc = alloc_nr(active_alloc);
active_cache = xrealloc(active_cache, active_alloc * sizeof(struct cache_entry *));
if (istate->cache_nr == istate->cache_alloc) {
istate->cache_alloc = alloc_nr(istate->cache_alloc);
istate->cache = xrealloc(istate->cache,
istate->cache_alloc * sizeof(struct cache_entry *));
}
/* Add it in.. */
active_nr++;
if (active_nr > pos)
memmove(active_cache + pos + 1, active_cache + pos, (active_nr - pos - 1) * sizeof(ce));
active_cache[pos] = ce;
active_cache_changed = 1;
istate->cache_nr++;
if (istate->cache_nr > pos)
memmove(istate->cache + pos + 1,
istate->cache + pos,
(istate->cache_nr - pos - 1) * sizeof(ce));
istate->cache[pos] = ce;
istate->cache_changed = 1;
return 0;
}
@@ -694,7 +700,8 @@ int add_cache_entry(struct cache_entry *ce, int option)
* For example, you'd want to do this after doing a "git-read-tree",
* to link up the stat cache details with the proper files.
*/
static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really, int *err)
static struct cache_entry *refresh_cache_ent(struct index_state *istate,
struct cache_entry *ce, int really, int *err)
{
struct stat st;
struct cache_entry *updated;
@@ -706,7 +713,7 @@ static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really,
return NULL;
}
changed = ce_match_stat(ce, &st, really);
changed = ie_match_stat(istate, ce, &st, really);
if (!changed) {
if (really && assume_unchanged &&
!(ce->ce_flags & htons(CE_VALID)))
@@ -715,7 +722,7 @@ static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really,
return ce;
}
if (ce_modified(ce, &st, really)) {
if (ie_modified(istate, ce, &st, really)) {
if (err)
*err = EINVAL;
return NULL;
@@ -738,7 +745,7 @@ static struct cache_entry *refresh_cache_ent(struct cache_entry *ce, int really,
return updated;
}
int refresh_cache(unsigned int flags)
int refresh_index(struct index_state *istate, unsigned int flags)
{
int i;
int has_errors = 0;
@@ -747,14 +754,14 @@ int refresh_cache(unsigned int flags)
int quiet = (flags & REFRESH_QUIET) != 0;
int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
for (i = 0; i < active_nr; i++) {
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce, *new;
int cache_errno = 0;
ce = active_cache[i];
ce = istate->cache[i];
if (ce_stage(ce)) {
while ((i < active_nr) &&
! strcmp(active_cache[i]->name, ce->name))
while ((i < istate->cache_nr) &&
! strcmp(istate->cache[i]->name, ce->name))
i++;
i--;
if (allow_unmerged)
@@ -764,7 +771,7 @@ int refresh_cache(unsigned int flags)
continue;
}
new = refresh_cache_ent(ce, really, &cache_errno);
new = refresh_cache_ent(istate, ce, really, &cache_errno);
if (new == ce)
continue;
if (!new) {
@@ -775,7 +782,7 @@ int refresh_cache(unsigned int flags)
* means the index is not valid anymore.
*/
ce->ce_flags &= ~htons(CE_VALID);
active_cache_changed = 1;
istate->cache_changed = 1;
}
if (quiet)
continue;
@@ -783,18 +790,18 @@ int refresh_cache(unsigned int flags)
has_errors = 1;
continue;
}
active_cache_changed = 1;
/* You can NOT just free active_cache[i] here, since it
istate->cache_changed = 1;
/* You can NOT just free istate->cache[i] here, since it
* might not be necessarily malloc()ed but can also come
* from mmap(). */
active_cache[i] = new;
istate->cache[i] = new;
}
return has_errors;
}
struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
{
return refresh_cache_ent(ce, really, NULL);
return refresh_cache_ent(&the_index, ce, really, NULL);
}
static int verify_hdr(struct cache_header *hdr, unsigned long size)
@@ -814,11 +821,12 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
return 0;
}
static int read_index_extension(const char *ext, void *data, unsigned long sz)
static int read_index_extension(struct index_state *istate,
const char *ext, void *data, unsigned long sz)
{
switch (CACHE_EXT(ext)) {
case CACHE_EXT_TREE:
active_cache_tree = cache_tree_read(data, sz);
istate->cache_tree = cache_tree_read(data, sz);
break;
default:
if (*ext < 'A' || 'Z' < *ext)
@@ -830,13 +838,13 @@ static int read_index_extension(const char *ext, void *data, unsigned long sz)
return 0;
}
int read_cache(void)
int read_index(struct index_state *istate)
{
return read_cache_from(get_index_file());
return read_index_from(istate, get_index_file());
}
/* remember to discard_cache() before reading a different cache! */
int read_cache_from(const char *path)
int read_index_from(struct index_state *istate, const char *path)
{
int fd, i;
struct stat st;
@@ -844,11 +852,11 @@ int read_cache_from(const char *path)
struct cache_header *hdr;
errno = EBUSY;
if (cache_mmap)
return active_nr;
if (istate->mmap)
return istate->cache_nr;
errno = ENOENT;
index_file_timestamp = 0;
istate->timestamp = 0;
fd = open(path, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT)
@@ -856,33 +864,35 @@ int read_cache_from(const char *path)
die("index file open failed (%s)", strerror(errno));
}
if (!fstat(fd, &st)) {
cache_mmap_size = xsize_t(st.st_size);
errno = EINVAL;
if (cache_mmap_size >= sizeof(struct cache_header) + 20)
cache_mmap = xmmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
else
die("index file smaller than expected");
} else
if (fstat(fd, &st))
die("cannot stat the open index (%s)", strerror(errno));
errno = EINVAL;
istate->mmap_size = xsize_t(st.st_size);
if (istate->mmap_size < sizeof(struct cache_header) + 20)
die("index file smaller than expected");
istate->mmap = xmmap(NULL, istate->mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
close(fd);
hdr = cache_mmap;
if (verify_hdr(hdr, cache_mmap_size) < 0)
hdr = istate->mmap;
if (verify_hdr(hdr, istate->mmap_size) < 0)
goto unmap;
active_nr = ntohl(hdr->hdr_entries);
active_alloc = alloc_nr(active_nr);
active_cache = xcalloc(active_alloc, sizeof(struct cache_entry *));
istate->cache_nr = ntohl(hdr->hdr_entries);
istate->cache_alloc = alloc_nr(istate->cache_nr);
istate->cache = xcalloc(istate->cache_alloc, sizeof(struct cache_entry *));
offset = sizeof(*hdr);
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset);
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce;
ce = (struct cache_entry *)((char *)(istate->mmap) + offset);
offset = offset + ce_size(ce);
active_cache[i] = ce;
istate->cache[i] = ce;
}
index_file_timestamp = st.st_mtime;
while (offset <= cache_mmap_size - 20 - 8) {
istate->timestamp = st.st_mtime;
while (offset <= istate->mmap_size - 20 - 8) {
/* After an array of active_nr index entries,
* there can be arbitrary number of extended
* sections, each of which is prefixed with
@@ -890,35 +900,37 @@ int read_cache_from(const char *path)
* in 4-byte network byte order.
*/
unsigned long extsize;
memcpy(&extsize, (char *) cache_mmap + offset + 4, 4);
memcpy(&extsize, (char *)(istate->mmap) + offset + 4, 4);
extsize = ntohl(extsize);
if (read_index_extension(((const char *) cache_mmap) + offset,
(char *) cache_mmap + offset + 8,
if (read_index_extension(istate,
((const char *) (istate->mmap)) + offset,
(char *) (istate->mmap) + offset + 8,
extsize) < 0)
goto unmap;
offset += 8;
offset += extsize;
}
return active_nr;
return istate->cache_nr;
unmap:
munmap(cache_mmap, cache_mmap_size);
munmap(istate->mmap, istate->mmap_size);
errno = EINVAL;
die("index file corrupt");
}
int discard_cache(void)
int discard_index(struct index_state *istate)
{
int ret;
active_nr = active_cache_changed = 0;
index_file_timestamp = 0;
cache_tree_free(&active_cache_tree);
if (cache_mmap == NULL)
istate->cache_nr = 0;
istate->cache_changed = 0;
istate->timestamp = 0;
cache_tree_free(&(istate->cache_tree));
if (istate->mmap == NULL)
return 0;
ret = munmap(cache_mmap, cache_mmap_size);
cache_mmap = NULL;
cache_mmap_size = 0;
ret = munmap(istate->mmap, istate->mmap_size);
istate->mmap = NULL;
istate->mmap_size = 0;
/* no need to throw away allocated active_cache */
return ret;
@@ -1037,11 +1049,13 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
}
}
int write_cache(int newfd, struct cache_entry **cache, int entries)
int write_index(struct index_state *istate, int newfd)
{
SHA_CTX c;
struct cache_header hdr;
int i, removed;
struct cache_entry **cache = istate->cache;
int entries = istate->cache_nr;
for (i = removed = 0; i < entries; i++)
if (!cache[i]->ce_mode)
@@ -1059,17 +1073,17 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
struct cache_entry *ce = cache[i];
if (!ce->ce_mode)
continue;
if (index_file_timestamp &&
index_file_timestamp <= ntohl(ce->ce_mtime.sec))
if (istate->timestamp &&
istate->timestamp <= ntohl(ce->ce_mtime.sec))
ce_smudge_racily_clean_entry(ce);
if (ce_write(&c, newfd, ce, ce_size(ce)) < 0)
return -1;
}
/* Write extension data here */
if (active_cache_tree) {
if (istate->cache_tree) {
unsigned long sz;
void *data = cache_tree_write(active_cache_tree, &sz);
void *data = cache_tree_write(istate->cache_tree, &sz);
if (data &&
!write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
!ce_write(&c, newfd, data, sz))

View File

@@ -115,10 +115,15 @@ void mark_parents_uninteresting(struct commit *commit)
}
void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
{
add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
}
void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
{
if (revs->no_walk && (obj->flags & UNINTERESTING))
die("object ranges do not make sense when not walking revisions");
add_object_array(obj, name, &revs->pending);
add_object_array_with_mode(obj, name, &revs->pending, mode);
if (revs->reflog_info && obj->type == OBJ_COMMIT)
add_reflog_for_walk(revs->reflog_info,
(struct commit *)obj, name);
@@ -723,6 +728,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
int flags,
int cant_be_filename)
{
unsigned mode;
char *dotdot;
struct object *object;
unsigned char sha1[20];
@@ -796,12 +802,12 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
local_flags = UNINTERESTING;
arg++;
}
if (get_sha1(arg, sha1))
if (get_sha1_with_mode(arg, sha1, &mode))
return -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
object = get_reference(revs, arg, sha1, flags ^ local_flags);
add_pending_object(revs, object, arg);
add_pending_object_with_mode(revs, object, arg, mode);
return 0;
}
@@ -1105,7 +1111,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
continue;
}
if (!strcmp(arg, "--relative-date")) {
revs->relative_date = 1;
revs->date_mode = DATE_RELATIVE;
continue;
}
if (!strncmp(arg, "--date=", 7)) {
if (!strcmp(arg + 7, "relative"))
revs->date_mode = DATE_RELATIVE;
else if (!strcmp(arg + 7, "local"))
revs->date_mode = DATE_LOCAL;
else if (!strcmp(arg + 7, "default"))
revs->date_mode = DATE_NORMAL;
else
die("unknown date format %s", arg);
continue;
}
@@ -1177,10 +1194,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (def && !revs->pending.nr) {
unsigned char sha1[20];
struct object *object;
if (get_sha1(def, sha1))
unsigned mode;
if (get_sha1_with_mode(def, sha1, &mode))
die("bad default revision '%s'", def);
object = get_reference(revs, def, sha1, 0);
add_pending_object(revs, object, def);
add_pending_object_with_mode(revs, object, def, mode);
}
if (revs->topo_order)

View File

@@ -63,8 +63,8 @@ struct rev_info {
/* Format info */
unsigned int shown_one:1,
abbrev_commit:1,
relative_date:1;
abbrev_commit:1;
enum date_mode date_mode;
const char **ignore_packed; /* pretend objects in these are unpacked */
int num_ignore_packed;
@@ -131,5 +131,6 @@ extern void add_object(struct object *obj,
const char *name);
extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
extern void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode);
#endif

View File

@@ -557,7 +557,7 @@ static void scan_windows(struct packed_git *p,
}
}
static int unuse_one_window(struct packed_git *current)
static int unuse_one_window(struct packed_git *current, int keep_fd)
{
struct packed_git *p, *lru_p = NULL;
struct pack_window *lru_w = NULL, *lru_l = NULL;
@@ -573,7 +573,7 @@ static int unuse_one_window(struct packed_git *current)
lru_l->next = lru_w->next;
else {
lru_p->windows = lru_w->next;
if (!lru_p->windows && lru_p != current) {
if (!lru_p->windows && lru_p->pack_fd != keep_fd) {
close(lru_p->pack_fd);
lru_p->pack_fd = -1;
}
@@ -585,10 +585,10 @@ static int unuse_one_window(struct packed_git *current)
return 0;
}
void release_pack_memory(size_t need)
void release_pack_memory(size_t need, int fd)
{
size_t cur = pack_mapped;
while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
while (need >= (cur - pack_mapped) && unuse_one_window(NULL, fd))
; /* nothing */
}
@@ -723,7 +723,7 @@ unsigned char* use_pack(struct packed_git *p,
win->len = (size_t)len;
pack_mapped += win->len;
while (packed_git_limit < pack_mapped
&& unuse_one_window(p))
&& unuse_one_window(p, p->pack_fd))
; /* nothing */
win->base = xmmap(NULL, win->len,
PROT_READ, MAP_PRIVATE,

View File

@@ -643,11 +643,17 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
*/
int get_sha1(const char *name, unsigned char *sha1)
{
int ret, bracket_depth;
unsigned unused;
return get_sha1_with_mode(name, sha1, &unused);
}
int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
{
int ret, bracket_depth;
int namelen = strlen(name);
const char *cp;
*mode = S_IFINVALID;
prepare_alt_odb();
ret = get_sha1_1(name, namelen, sha1);
if (!ret)
@@ -685,6 +691,7 @@ int get_sha1(const char *name, unsigned char *sha1)
break;
if (ce_stage(ce) == stage) {
hashcpy(sha1, ce->sha1);
*mode = ntohl(ce->ce_mode);
return 0;
}
pos++;
@@ -703,7 +710,7 @@ int get_sha1(const char *name, unsigned char *sha1)
unsigned char tree_sha1[20];
if (!get_sha1_1(name, cp-name, tree_sha1))
return get_tree_entry(tree_sha1, cp+1, sha1,
&unused);
mode);
}
return ret;
}

48
t/t0021-conversion.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/sh
test_description='blob conversion via gitattributes'
. ./test-lib.sh
cat <<\EOF >rot13.sh
tr '[a-zA-Z]' '[n-za-mN-ZA-M]'
EOF
chmod +x rot13.sh
test_expect_success setup '
git config filter.rot13.smudge ./rot13.sh &&
git config filter.rot13.clean ./rot13.sh &&
{
echo "*.t filter=rot13"
echo "*.i ident"
} >.gitattributes &&
{
echo a b c d e f g h i j k l m
echo n o p q r s t u v w x y z
echo '\''$ident$'\''
} >test &&
cat test >test.t &&
cat test >test.o &&
cat test >test.i &&
git add test test.t test.i &&
rm -f test test.t test.i &&
git checkout -- test test.t test.i
'
script='s/^\$ident: \([0-9a-f]*\) \$/\1/p'
test_expect_success check '
cmp test.o test &&
cmp test.o test.t &&
# ident should be stripped in the repository
git diff --raw --exit-code :test :test.i &&
id=$(git rev-parse --verify :test) &&
embedded=$(sed -ne "$script" test.i) &&
test "z$id" = "z$embedded"
'
test_done

View File

@@ -47,6 +47,8 @@ git branch white &&
git branch red &&
git branch blue &&
git branch yellow &&
git branch change &&
git branch change+rename &&
sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
mv A+ A &&
@@ -77,6 +79,17 @@ rm -f A M &&
git update-index --add --remove A C M N &&
git commit -m "blue renames A->C, M->N" &&
git checkout change &&
sed -e "/^g /s/.*/g : changed line/" <A >A+ &&
mv A+ A &&
git commit -q -a -m "changed" &&
git checkout change+rename &&
sed -e "/^g /s/.*/g : changed line/" <A >B &&
rm A &&
git update-index --add B &&
git commit -q -a -m "changed and renamed" &&
git checkout master'
test_expect_success 'pull renaming branch into unrenaming one' \
@@ -318,4 +331,14 @@ test_expect_success 'interference with untracked working tree file' '
git reset --hard anchor
'
test_expect_success 'merge of identical changes in a renamed file' '
rm -f A M N
git reset --hard &&
git checkout change+rename &&
GIT_MERGE_VERBOSITY=3 git merge change | grep "^Skipped B" &&
git reset --hard HEAD^ &&
git checkout change &&
GIT_MERGE_VERBOSITY=3 git merge change+rename | grep "^Skipped B"
'
test_done

View File

@@ -16,6 +16,7 @@ unset AUTHOR_EMAIL
unset AUTHOR_NAME
unset COMMIT_AUTHOR_EMAIL
unset COMMIT_AUTHOR_NAME
unset EMAIL
unset GIT_ALTERNATE_OBJECT_DIRECTORIES
unset GIT_AUTHOR_DATE
GIT_AUTHOR_EMAIL=author@example.com
@@ -36,6 +37,10 @@ export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
export EDITOR VISUAL
# Protect ourselves from common misconfiguration to export
# CDPATH into the environment
unset CDPATH
case $(echo $GIT_TRACE |tr "[A-Z]" "[a-z]") in
1|2|true)
echo "* warning: Some tests will not work if GIT_TRACE" \