Merge with git://repo.or.cz/alt-git.git#master

This commit is contained in:
Johannes Sixt
2007-03-06 10:02:25 +01:00
180 changed files with 3436 additions and 853 deletions

1
.gitignore vendored
View File

@@ -13,6 +13,7 @@ git-archive
git-bisect
git-blame
git-branch
git-bundle
git-cat-file
git-check-ref-format
git-checkout

View File

@@ -37,6 +37,7 @@ INSTALL?=install
DOC_REF = origin/man
-include ../config.mak.autogen
-include ../config.mak
#
# Please note that there is a minor bug in asciidoc.
@@ -104,8 +105,11 @@ clean:
user-manual.xml: user-manual.txt user-manual.conf
$(ASCIIDOC) -b docbook -d book $<
XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
XSLTOPTS = --nonet --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xmlto html-nochunks $<
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
glossary.html : glossary.txt sort_glossary.pl
cat $< | \

View File

@@ -0,0 +1,58 @@
GIT v1.5.0.3 Release Notes
==========================
Fixes since v1.5.0.2
--------------------
* Bugfixes
- 'git.el' honors the commit coding system from the configuration.
- 'blameview' in contrib/ correctly digs deeper when a line is
clicked.
- 'http-push' correctly makes sure the remote side has leading
path. Earlier it started in the middle of the path, and
incorrectly.
- 'git-merge' did not exit with non-zero status when the
working tree was dirty and cannot fast forward. It does
now.
- 'cvsexportcommit' does not lose yet-to-be-used message file.
- int-vs-size_t typefix when running combined diff on files
over 2GB long.
- 'git apply --whitespace=strip' should not touch unmodified
lines.
- 'git-mailinfo' choke when a logical header line was too long.
- 'git show A..B' did not error out. Negative ref ("not A" in
this example) does not make sense for the purpose of the
command, so now it errors out.
- 'git fmt-merge-msg --file' without file parameter did not
correctly error out.
- 'git archimport' barfed upon encountering a commit without
summary.
- 'git index-pack' did not protect itself from getting a short
read out of pread(2).
- 'git http-push' had a few buffer overruns.
- Build dependency fixes to rebuild fetch.o when other headers
change.
* Documentation updates
- user-manual updates.
- Options to 'git remote add' were described insufficiently.
- Configuration format.suffix was not documented.
- Other formatting and spelling fixes.

View File

@@ -19,17 +19,86 @@ Updates since v1.5.0
- "git diff" learned --ignore-space-at-eol. This is a weaker
form of --ignore-space-change.
- "git diff --no-index pathA pathB" can be used as diff
replacement with git specific enhancements.
- "git diff --pretty=format:<string>" to allow more flexible
custom log output.
- "git name-rev" learned --refs=<pattern>, to limit the tags
used for naming the given revisions only to the ones
matching the given pattern.
- "git remote update" is to run "git fetch" for defined remotes
to update tracking branches.
- "git cvsimport" can now take '-d' to talk with a CVS
repository different from what are recorded in CVS/Root
(overriding it with environment CVSROOT does not work).
- "git bundle" can help sneaker-netting your changes between
repositories.
- A new configuration "core.symlinks" can be used to disable
symlinks on filesystems that do not support them; they are
checked out as regular files instead.
* Updated behaviour of existing commands.
- git-svn got almost a rewrite.
- core.autocrlf configuration, when set to 'true', makes git
to convert CRLF at the end of lines in text files to LF when
reading from the filesystem, and convert in reverse when
writing to the filesystem. The variable can be set to
'input', in which case the conversion happens only while
reading from the filesystem but files are written out with
LF at the end of lines. Currently, which paths to consider
'text' (i.e. be subjected to the autocrlf mechanism) is
decided purely based on the contents, but the plan is to
allow users to explicitly override this heuristic based on
paths.
- The behaviour of 'git-apply', when run in a subdirectory,
without --index nor --cached were inconsistent with that of
the command with these options. This was fixed to match the
behaviour with --index. A patch that is meant to be applied
with -p1 from the toplevel of the project tree can be
applied with any custom -p<n> option. A patch that is not
relative to the toplevel needs to be applied with -p<n>
option with or without --index (or --cached).
- "git diff" outputs a trailing HT when pathnames have embedded
SP on +++/--- header lines, in order to help "GNU patch" to
parse its output. "git apply" was already updated to accept
this modified output format since ce74618d (Sep 22, 2006).
- "git cvsserver" runs hooks/update and honors its exit status.
- "git cvsserver" can be told to send everything with -kb.
- "git diff --check" also honors the --color output option.
- "git name-rev" used to stress the fact that a ref is a tag too
much, by saying something like "v1.2.3^0~22". It now says
"v1.2.3~22" in such a case (it still says "v1.2.3^0" if it does
not talk about an ancestor of the commit that is tagged, which
makes sense).
- "git rev-list --boundary" now shows boundary markers for the
commits omitted by --max-age and --max-count condition.
- The configuration mechanism now reads $(prefix)/etc/gitconfig.
- "git apply --verbose" shows what preimage lines were wanted
when it couldn't find them.
- "git status" in a read-only repository got a bit saner.
- "git fetch" (hence "git clone" and "git pull") are less
noisy when the output does not go to tty.
* Hooks
- The sample update hook to show how to send out notification
@@ -39,6 +108,10 @@ Updates since v1.5.0
--
exec >/var/tmp/1
O=v1.5.0-49-g69bc0e2
O=v1.5.0.3-268-g3ddad98
echo O=`git describe master`
git shortlog --no-merges $O..master ^maint
# Local Variables:
# mode: text
# End:

View File

@@ -41,10 +41,6 @@ while ($changed) {
while (my ($text, $included) = each %include) {
if (! exists $included{$text} &&
(my $base = $text) =~ s/\.txt$//) {
my ($suffix) = '1';
if ($base eq 'git') {
$suffix = '7'; # yuck...
}
print "$base.html $base.$suffix : ", join(" ", keys %$included), "\n";
print "$base.html $base.xml : ", join(" ", keys %$included), "\n";
}
}

View File

@@ -70,6 +70,7 @@ git-archive mainporcelain
git-bisect mainporcelain
git-blame ancillaryinterrogators
git-branch mainporcelain
git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-checkout-index plumbingmanipulators
git-checkout mainporcelain

View File

@@ -117,6 +117,13 @@ core.fileMode::
the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default.
core.symlinks::
If false, symbolic links are checked out as small plain files that
contain the link text. gitlink:git-update-index[1] and
gitlink:git-add[1] will not change the recorded type to regular
file. Useful on filesystems like FAT that do not support
symbolic links. True by default.
core.gitProxy::
A "proxy command" to execute (as 'command host port') instead
of establishing direct connection to the remote server when
@@ -341,6 +348,11 @@ format.headers::
Additional email headers to include in a patch to be submitted
by mail. See gitlink:git-format-patch[1].
format.suffix::
The default for format-patch is to output files with the suffix
`.patch`. Use this variable to change that suffix (make sure to
include the dot if you want it).
gc.packrefs::
`git gc` does not run `git pack-refs` in a bare repository by
default so that older dumb-transport clients can still fetch

View File

@@ -109,7 +109,7 @@ sure it is in your path. Then cd to a checked out CVS working directory
of the project you are interested in and run gitlink:git-cvsimport[1]:
-------------------------------------------
$ git cvsimport -C <destination>
$ git cvsimport -C <destination> <module>
-------------------------------------------
This puts a git archive of the named CVS module in the directory

View File

@@ -52,7 +52,7 @@ OPTIONS
-f::
Allow adding otherwise ignored files.
\i, \--interactive::
-i, \--interactive::
Add modified contents in the working tree interactively to
the index.

View File

@@ -8,7 +8,8 @@ git-branch - List, create, or delete branches
SYNOPSIS
--------
[verse]
'git-branch' [--color | --no-color] [-r | -a] [-v [--abbrev=<length>]]
'git-branch' [--color | --no-color] [-r | -a]
[-v [--abbrev=<length> | --no-abbrev]]
'git-branch' [-l] [-f] <branchname> [<start-point>]
'git-branch' (-m | -M) [<oldbranch>] <newbranch>
'git-branch' (-d | -D) [-r] <branchname>...
@@ -80,6 +81,9 @@ OPTIONS
Alter minimum display length for sha1 in output listing,
default value is 7.
--no-abbrev::
Display the full sha1s in output listing rather than abbreviating them.
<branchname>::
The name of the branch to create or delete.
The new branch name must pass all checks defined by

View File

@@ -0,0 +1,139 @@
git-bundle(1)
=============
NAME
----
git-bundle - Move objects and refs by archive
SYNOPSIS
--------
'git-bundle' create <file> [git-rev-list args]
'git-bundle' verify <file>
'git-bundle' list-heads <file> [refname...]
'git-bundle' unbundle <file> [refname...]
DESCRIPTION
-----------
Some workflows require that one or more branches of development on one
machine be replicated on another machine, but the two machines cannot
be directly connected so the interactive git protocols (git, ssh,
rsync, http) cannot be used. This command provides support for
git-fetch and git-pull to operate by packaging objects and references
in an archive at the originating machine, then importing those into
another repository using gitlink:git-fetch[1] and gitlink:git-pull[1]
after moving the archive by some means (i.e., by sneakernet). As no
direct connection between repositories exists, the user must specify a
basis for the bundle that is held by the destination repository: the
bundle assumes that all objects in the basis are already in the
destination repository.
OPTIONS
-------
create <file>::
Used to create a bundle named 'file'. This requires the
git-rev-list arguments to define the bundle contents.
verify <file>::
Used to check that a bundle file is valid and will apply
cleanly to the current repository. This includes checks on the
bundle format itself as well as checking that the prerequisite
commits exist and are fully linked in the current repository.
git-bundle prints a list of missing commits, if any, and exits
with non-zero status.
list-heads <file>::
Lists the references defined in the bundle. If followed by a
list of references, only references matching those given are
printed out.
unbundle <file>::
Passes the objects in the bundle to gitlink:git-index-pack[1]
for storage in the repository, then prints the names of all
defined references. If a reflist is given, only references
matching those in the given list are printed. This command is
really plumbing, intended to be called only by
gitlink:git-fetch[1].
[git-rev-list-args...]::
A list of arguments, acceptable to git-rev-parse and
git-rev-list, that specify the specific objects and references
to transport. For example, "master~10..master" causes the
current master reference to be packaged along with all objects
added since its 10th ancestor commit. There is no explicit
limit to the number of references and objects that may be
packaged.
[refname...]::
A list of references used to limit the references reported as
available. This is principally of use to git-fetch, which
expects to receive only those references asked for and not
necessarily everything in the pack (in this case, git-bundle is
acting like gitlink:git-fetch-pack[1]).
SPECIFYING REFERENCES
---------------------
git-bundle will only package references that are shown by
git-show-ref: this includes heads, tags, and remote heads. References
such as master~1 cannot be packaged, but are perfectly suitable for
defining the basis. More than one reference may be packaged, and more
than one basis can be specified. The objects packaged are those not
contained in the union of the given bases. Each basis can be
specified explicitly (e.g., ^master~10), or implicitly (e.g.,
master~10..master, master --since=10.days.ago).
It is very important that the basis used be held by the destination.
It is okay to err on the side of conservatism, causing the bundle file
to contain objects already in the destination as these are ignored
when unpacking at the destination.
EXAMPLE
-------
Assume two repositories exist as R1 on machine A, and R2 on machine B.
For whatever reason, direct connection between A and B is not allowed,
but we can move data from A to B via some mechanism (CD, email, etc).
We want to update R2 with developments made on branch master in R1.
We set a tag in R1 (lastR2bundle) after the previous such transport,
and move it afterwards to help build the bundle.
in R1 on A:
$ git-bundle create mybundle master ^lastR2bundle
$ git tag -f lastR2bundle master
(move mybundle from A to B by some mechanism)
in R2 on B:
$ git-bundle verify mybundle
$ git-fetch mybundle refspec
where refspec is refInBundle:localRef
Also, with something like this in your config:
[remote "bundle"]
url = /home/me/tmp/file.bdl
fetch = refs/heads/*:refs/remotes/origin/*
You can first sneakernet the bundle file to ~/tmp/file.bdl and
then these commands:
$ git ls-remote bundle
$ git fetch bundle
$ git pull bundle
would treat it as if it is talking with a remote side over the
network.
Author
------
Written by Mark Levedahl <mdl123@verizon.net>
GIT
---
Part of the gitlink:git[7] suite

View File

@@ -96,11 +96,6 @@ If you need to pass multiple options, separate them with a comma.
-s <subst>::
Substitute the character "/" in branch names with <subst>
-A <author-conv-file>::
CVS by default uses the Unix username when writing its
commit logs. Using this option and an author-conv-file
in this format
-a::
Import all commits, including recent ones. cvsimport by default
skips commits that have a timestamp less than 10 minutes ago.
@@ -112,6 +107,10 @@ If you need to pass multiple options, separate them with a comma.
Limit the number of commits imported. Workaround for cases where
cvsimport leaks memory.
-A <author-conv-file>::
CVS by default uses the Unix username when writing its
commit logs. Using this option and an author-conv-file
in this format
+
---------
exon=Andreas Ericsson <ae@op5.se>

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] [<common diff options>] [<path>...]
'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc|-n|--no-index] [<common diff options>] [<path>...]
DESCRIPTION
-----------
@@ -36,6 +36,9 @@ 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::
Compare the two given files / directories.
-q::
Remain silent even on nonexistent files

View File

@@ -23,6 +23,10 @@ tree and the index file, or the index file and the working tree.
further add to the index but you still haven't. You can
stage these changes by using gitlink:git-add[1].
If exactly two paths are given, and at least one is untracked,
compare the two files / directories. This behavior can be
forced by --no-index.
'git-diff' [--options] --cached [<commit>] [--] [<path>...]::
This form is to view the changes you staged for the next

View File

@@ -8,7 +8,7 @@ git-fetch-pack - Receive missing objects from another repository
SYNOPSIS
--------
'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [-v] [<host>:]<directory> [<refs>...]
'git-fetch-pack' [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]
DESCRIPTION
-----------
@@ -63,6 +63,9 @@ OPTIONS
\--depth=<n>::
Limit fetching to ancestor-chains not longer than n.
\--no-progress::
Do not show the progress.
\-v::
Run verbosely.

View File

@@ -42,10 +42,10 @@ OPTIONS
--patches <dir>::
The directory to find the quilt patches and the
quilt series file.
The default for the patch directory is patches
or the value of the $QUILT_PATCHES environment
variable.
+
The default for the patch directory is patches
or the value of the $QUILT_PATCHES environment
variable.
Author
------

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git-remote'
'git-remote' add <name> <url>
'git-remote' add [-t <branch>] [-m <branch>] [-f] <name> <url>
'git-remote' show <name>
'git-remote' prune <name>
'git-remote' update [group]
@@ -77,8 +77,8 @@ gitlink:git-config[1]).
Examples
--------
Add a new remote, fetch, and check out a branch from it:
* Add a new remote, fetch, and check out a branch from it
+
------------
$ git remote
origin
@@ -98,6 +98,17 @@ $ git checkout -b nfs linux-nfs/master
...
------------
* Imitate 'git clone' but track only selected branches
+
------------
$ mkdir project.git
$ cd project.git
$ git init
$ git remote add -f -t master -m master origin git://example.com/git.git/
$ git merge origin
------------
See Also
--------
gitlink:git-fetch[1]

View File

@@ -190,6 +190,13 @@ blobs contained in a commit.
and dereference the tag recursively until a non-tag object is
found.
* A colon, followed by a slash, followed by a text: this names
a commit whose commit message starts with the specified text.
This name returns the youngest matching commit which is
reachable from any ref. If the commit message starts with a
'!', you have to repeat that; the special sequence ':/!',
followed by something else than '!' is reserved for now.
* A suffix ':' followed by a path; this names the blob or tree
at the given path in the tree-ish object named by the part
before the colon.

View File

@@ -26,13 +26,13 @@ The options available are:
--bcc::
Specify a "Bcc:" value for each email.
The --bcc option must be repeated for each user you want on the bcc list.
+
The --bcc option must be repeated for each user you want on the bcc list.
--cc::
Specify a starting "Cc:" value for each email.
The --cc option must be repeated for each user you want on the cc list.
+
The --cc option must be repeated for each user you want on the cc list.
--chain-reply-to, --no-chain-reply-to::
If this is set, each email will be sent as a reply to the previous
@@ -87,8 +87,8 @@ The options available are:
Specify the primary recipient of the emails generated.
Generally, this will be the upstream maintainer of the
project involved.
The --to option must be repeated for each user you want on the to list.
+
The --to option must be repeated for each user you want on the to list.
Author

View File

@@ -48,15 +48,15 @@ git show v1.0.0::
Shows the tag `v1.0.0`, along with the object the tags
points at.
git show v1.0.0^{tree}::
git show v1.0.0^\{tree\}::
Shows the tree pointed to by the tag `v1.0.0`.
git show next~10:Documentation/README
git show next~10:Documentation/README::
Shows the contents of the file `Documentation/README` as
they were current in the 10th last commit of the branch
`next`.
git show master:Makefile master:t/Makefile
git show master:Makefile master:t/Makefile::
Concatenates the contents of said Makefiles in the head
of the branch `master`.

View File

@@ -63,7 +63,7 @@ COMMANDS
transports (eg svn+ssh://), you must include the username in
the URL, eg svn+ssh://foo@svn.bar.com/project
--prefix=<prefix>
--prefix=<prefix>::
This allows one to specify a prefix which is prepended
to the names of remotes if trunk/branches/tags are
specified. The prefix does not automatically include a
@@ -94,16 +94,16 @@ COMMANDS
This fetches revisions from the SVN parent of the current HEAD
and rebases the current (uncommitted to SVN) work against it.
This works similarly to 'svn update' or 'git-pull' except that
it preserves linear history with 'git-rebase' instead of
'git-merge' for ease of dcommit-ing with git-svn.
This works similarly to 'svn update' or 'git-pull' except that
it preserves linear history with 'git-rebase' instead of
'git-merge' for ease of dcommit-ing with git-svn.
This accepts all options that 'git-svn fetch' and 'git-rebase'
accepts. However '--fetch-all' only fetches from the current
[svn-remote], and not all [svn-remote] definitions.
This accepts all options that 'git-svn fetch' and 'git-rebase'
accepts. However '--fetch-all' only fetches from the current
[svn-remote], and not all [svn-remote] definitions.
Like 'git-rebase'; this requires that the working tree be clean
and have no uncommitted changes.
Like 'git-rebase'; this requires that the working tree be clean
and have no uncommitted changes.
'dcommit'::
Commit each diff from a specified head directly to the SVN
@@ -117,29 +117,40 @@ COMMANDS
alternative to HEAD.
This is advantageous over 'set-tree' (below) because it produces
cleaner, more linear history.
--
'log'::
This should make it easy to look up svn log messages when svn
users refer to -r/--revision numbers.
+
The following features from `svn log' are supported:
+
--
--revision=<n>[:<n>];;
is supported, non-numeric args are not:
HEAD, NEXT, BASE, PREV, etc ...
-v/--verbose;;
it's not completely compatible with the --verbose
output in svn log, but reasonably close.
--limit=<n>;;
is NOT the same as --max-count, doesn't count
merged/excluded commits
--incremental;;
supported
--
+
New features:
+
--
--show-commit;;
shows the git commit sha1, as well
--oneline;;
our version of --pretty=oneline
--
+
Any other arguments are passed directly to `git log'
The following features from `svn log' are supported:
--revision=<n>[:<n>] - is supported, non-numeric args are not:
HEAD, NEXT, BASE, PREV, etc ...
-v/--verbose - it's not completely compatible with
the --verbose output in svn log, but
reasonably close.
--limit=<n> - is NOT the same as --max-count,
doesn't count merged/excluded commits
--incremental - supported
New features:
--show-commit - shows the git commit sha1, as well
--oneline - our version of --pretty=oneline
Any other arguments are passed directly to `git log'
--
'set-tree'::
You should consider using 'dcommit' instead of this command.
Commit specified commit or tree objects to SVN. This relies on
@@ -256,16 +267,18 @@ config key: svn.authorsfile
Make git-svn less verbose.
--repack[=<n>]::
--repack-flags=<flags>
These should help keep disk usage sane for large fetches
with many revisions.
--repack-flags=<flags>::
--repack takes an optional argument for the number of revisions
to fetch before repacking. This defaults to repacking every
1000 commits fetched if no argument is specified.
These should help keep disk usage sane for large fetches
with many revisions.
--repack-flags are passed directly to gitlink:git-repack[1].
--repack takes an optional argument for the number of revisions
to fetch before repacking. This defaults to repacking every
1000 commits fetched if no argument is specified.
--repack-flags are passed directly to gitlink:git-repack[1].
[verse]
config key: svn.repack
config key: svn.repackflags
@@ -323,28 +336,30 @@ CONFIG FILE-ONLY OPTIONS
svn.noMetadata::
svn-remote.<name>.noMetadata::
This gets rid of the git-svn-id: lines at the end of every commit.
If you lose your .git/svn/git-svn/.rev_db file, git-svn will not
be able to rebuild it and you won't be able to fetch again,
either. This is fine for one-shot imports.
This gets rid of the git-svn-id: lines at the end of every commit.
The 'git-svn log' command will not work on repositories using
this, either. Using this conflicts with the 'useSvmProps'
option for (hopefully) obvious reasons.
If you lose your .git/svn/git-svn/.rev_db file, git-svn will not
be able to rebuild it and you won't be able to fetch again,
either. This is fine for one-shot imports.
The 'git-svn log' command will not work on repositories using
this, either. Using this conflicts with the 'useSvmProps'
option for (hopefully) obvious reasons.
svn.useSvmProps::
svn-remote.<name>.useSvmProps::
This allows git-svn to re-map repository URLs and UUIDs from
mirrors created using SVN::Mirror (or svk) for metadata.
If an SVN revision has a property, "svm:headrev", it is likely
that the revision was created by SVN::Mirror (also used by SVK).
The property contains a repository UUID and a revision. We want
to make it look like we are mirroring the original URL, so
introduce a helper function that returns the original identity
URL and UUID, and use it when generating metadata in commit
messages.
This allows git-svn to re-map repository URLs and UUIDs from
mirrors created using SVN::Mirror (or svk) for metadata.
If an SVN revision has a property, "svm:headrev", it is likely
that the revision was created by SVN::Mirror (also used by SVK).
The property contains a repository UUID and a revision. We want
to make it look like we are mirroring the original URL, so
introduce a helper function that returns the original identity
URL and UUID, and use it when generating metadata in commit
messages.
svn.useSvnsyncProps::
svn-remote.<name>.useSvnsyncprops::
@@ -369,8 +384,8 @@ section because they affect the 'git-svn-id:' metadata line.
--
Basic Examples
~~~~~~~~~~~~~~
BASIC EXAMPLES
--------------
Tracking and contributing to a the trunk of a Subversion-managed project:
@@ -405,7 +420,7 @@ Tracking and contributing to an entire Subversion-managed project
# with the appropriate name):
git reset --hard remotes/trunk
# You may only dcommit to one branch/tag/trunk at a time. The usage
# of dcommit/rebase/show-ignore should be teh same as above.
# of dcommit/rebase/show-ignore should be the same as above.
------------------------------------------------------------------------
REBASE VS. PULL/MERGE

View File

@@ -295,6 +295,11 @@ in the index and the file mode on the filesystem if they differ only on
executable bit. On such an unfortunate filesystem, you may
need to use `git-update-index --chmod=`.
Quite similarly, if `core.symlinks` configuration variable is set
to 'false' (see gitlink:git-config[1]), symbolic links are checked out
as plain files, and this command does not modify a recorded file mode
from symbolic link to regular file.
The command looks at `core.ignorestat` configuration variable. See
'Using "assume unchanged" bit' section above.

View File

@@ -8,7 +8,7 @@ git-upload-pack - Send objects packed back to git-fetch-pack
SYNOPSIS
--------
'git-upload-pack' <directory>
'git-upload-pack' [--strict] [--timeout=<n>] <directory>
DESCRIPTION
-----------
@@ -23,6 +23,13 @@ repository. For push operations, see 'git-send-pack'.
OPTIONS
-------
\--strict::
Do not try <directory>/.git/ if <directory> is no git directory.
\--timeout=<n>::
Interrupt transfer after <n> seconds of inactivity.
<directory>::
The repository to sync from.

View File

@@ -35,14 +35,14 @@ ifdef::stalenotes[]
You are reading the documentation for the latest version of git.
Documentation for older releases are available here:
* link:v1.5.0.2/git.html[documentation for release 1.5.0.2]
* link:v1.5.0.3/git.html[documentation for release 1.5.0.3]
* link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
* link:v1.5.0.2/RelNotes-1.5.0.2.txt[release notes for 1.5.0.2]
* link:v1.5.0.1/RelNotes-1.5.0.1.txt[release notes for 1.5.0.1]
* link:v1.5.0/git.html[documentation for release 1.5.0]
* link:v1.5.0/RelNotes-1.5.0.txt[release notes for 1.5.0]
* link:v1.4.4.4/git.html[documentation for release 1.4.4.4]

View File

@@ -73,6 +73,11 @@ DAG::
objects is acyclic (there is no chain which begins and ends with the
same object).
dangling object::
An unreachable object which is not reachable even from other
unreachable objects; a dangling object has no references to it
from any reference or object in the repository.
dircache::
You are *waaaaay* behind.
@@ -350,6 +355,10 @@ tag::
unmerged index::
An index which contains unmerged index entries.
unreachable object::
An object which is not reachable from a branch, tag, or any
other reference.
working tree::
The set of files and directories currently being worked on,
i.e. you can work in your working tree without using git at all.

View File

@@ -77,9 +77,53 @@ displayed in full, regardless of whether --abbrev or
true parent commits, without taking grafts nor history
simplification into account.
* 'format:'
+
The 'format:' format allows you to specify which information
you want to show. It works a little bit like printf format,
with the notable exception that you get a newline with '%n'
instead of '\n'.
E.g, 'format:"The author of %h was %an, %ar%nThe title was >>%s<<"'
would show something like this:
The author of fe6e0ee was Junio C Hamano, 23 hours ago
The title was >>t4119: test autocomputing -p<n> for traditional diff input.<<
The placeholders are:
- '%H': commit hash
- '%h': abbreviated commit hash
- '%T': tree hash
- '%t': abbreviated tree hash
- '%P': parent hashes
- '%p': abbreviated parent hashes
- '%an': author name
- '%ae': author email
- '%ad': author date
- '%aD': author date, RFC2822 style
- '%ar': author date, relative
- '%at': author date, UNIX timestamp
- '%cn': committer name
- '%ce': committer email
- '%cd': committer date
- '%cD': committer date, RFC2822 style
- '%cr': committer date, relative
- '%ct': committer date, UNIX timestamp
- '%e': encoding
- '%s': subject
- '%b': body
- '%Cred': switch color to red
- '%Cgreen': switch color to green
- '%Cblue': switch color to blue
- '%Creset': reset color
- '%n': newline
--encoding[=<encoding>]::
The commit objects record the encoding used for the log message
in their encoding header; this option can be used to tell the
command to re-code the commit log message in the encoding
preferred by the user. For non plumbing commands this
defaults to UTF-8.

View File

@@ -2,7 +2,7 @@ Git User's Manual
_________________
This manual is designed to be readable by someone with basic unix
commandline skills, but no previous knowledge of git.
command-line skills, but no previous knowledge of git.
Chapter 1 gives a brief overview of git commands, without any
explanation; you may prefer to skip to chapter 2 on a first reading.
@@ -391,15 +391,20 @@ index 8be626f..d7aac9d 100644
As you can see, a commit shows who made the latest change, what they
did, and why.
Every commit has a 40-hexdigit id, sometimes called the "object name"
or the "SHA1 id", shown on the first line of the "git show" output.
You can usually refer to a commit by a shorter name, such as a tag or a
branch name, but this longer name can also be useful. Most
importantly, it is a globally unique name for this commit: so if you
tell somebody else the object name (for example in email), then you are
guaranteed that name will refer to the same commit in their repository
that it does in yours (assuming their repository has that commit at
all).
Every commit has a 40-hexdigit id, sometimes called the "object name" or the
"SHA1 id", shown on the first line of the "git show" output. You can usually
refer to a commit by a shorter name, such as a tag or a branch name, but this
longer name can also be useful. Most importantly, it is a globally unique
name for this commit: so if you tell somebody else the object name (for
example in email), then you are guaranteed that name will refer to the same
commit in their repository that it does in yours (assuming their repository
has that commit at all). Since the object name is computed as a hash over the
contents of the commit, you are guaranteed that the commit can never change
without its name also changing.
In fact, in <<git-internals>> we shall see that everything stored in git
history, including file data and directory contents, is stored in an object
with a name that is a hash of its contents.
Understanding history: commits, parents, and reachability
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1163,18 +1168,46 @@ the working tree in a special state that gives you all the
information you need to help resolve the merge.
Files with conflicts are marked specially in the index, so until you
resolve the problem and update the index, git commit will fail:
resolve the problem and update the index, gitlink:git-commit[1] will
fail:
-------------------------------------------------
$ git commit
file.txt: needs merge
-------------------------------------------------
Also, git status will list those files as "unmerged".
Also, gitlink:git-status[1] will list those files as "unmerged", and the
files with conflicts will have conflict markers added, like this:
-------------------------------------------------
<<<<<<< HEAD:file.txt
Hello world
=======
Goodbye
>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt
-------------------------------------------------
All you need to do is edit the files to resolve the conflicts, and then
-------------------------------------------------
$ git add file.txt
$ git commit
-------------------------------------------------
Note that the commit message will already be filled in for you with
some information about the merge. Normally you can just use this
default message unchanged, but you may add additional commentary of
your own if desired.
The above is all you need to know to resolve a simple merge. But git
also provides more information to help resolve conflicts:
Getting conflict-resolution help during a merge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All of the changes that git was able to merge automatically are
already added to the index file, so gitlink:git-diff[1] shows only
the conflicts. Also, it uses a somewhat unusual syntax:
the conflicts. It uses an unusual syntax:
-------------------------------------------------
$ git diff
@@ -1195,14 +1228,32 @@ conflict will have two parents instead of the usual one: one parent
will be HEAD, the tip of the current branch; the other will be the
tip of the other branch, which is stored temporarily in MERGE_HEAD.
The diff above shows the differences between the working-tree version
of file.txt and two previous version: one version from HEAD, and one
from MERGE_HEAD. So instead of preceding each line by a single "+"
or "-", it now uses two columns: the first column is used for
differences between the first parent and the working directory copy,
and the second for differences between the second parent and the
working directory copy. Thus after resolving the conflict in the
obvious way, the diff will look like:
During the merge, the index holds three versions of each file. Each of
these three "file stages" represents a different version of the file:
-------------------------------------------------
$ git show :1:file.txt # the file in a common ancestor of both branches
$ git show :2:file.txt # the version from HEAD, but including any
# nonconflicting changes from MERGE_HEAD
$ git show :3:file.txt # the version from MERGE_HEAD, but including any
# nonconflicting changes from HEAD.
-------------------------------------------------
Since the stage 2 and stage 3 versions have already been updated with
nonconflicting changes, the only remaining differences between them are
the important ones; thus gitlink:git-diff[1] can use the information in
the index to show only those conflicts.
The diff above shows the differences between the working-tree version of
file.txt and the stage 2 and stage 3 versions. So instead of preceding
each line by a single "+" or "-", it now uses two columns: the first
column is used for differences between the first parent and the working
directory copy, and the second for differences between the second parent
and the working directory copy. (See the "COMBINED DIFF FORMAT" section
of gitlink:git-diff-files[1] for a details of the format.)
After resolving the conflict in the obvious way (but before updating the
index), the diff will look like:
-------------------------------------------------
$ git diff
@@ -1220,26 +1271,37 @@ This shows that our resolved version deleted "Hello world" from the
first parent, deleted "Goodbye" from the second parent, and added
"Goodbye world", which was previously absent from both.
The gitlink:git-log[1] command also provides special help for merges:
Some special diff options allow diffing the working directory against
any of these stages:
-------------------------------------------------
$ git diff -1 file.txt # diff against stage 1
$ git diff --base file.txt # same as the above
$ git diff -2 file.txt # diff against stage 2
$ git diff --ours file.txt # same as the above
$ git diff -3 file.txt # diff against stage 3
$ git diff --theirs file.txt # same as the above.
-------------------------------------------------
The gitlink:git-log[1] and gitk[1] commands also provide special help
for merges:
-------------------------------------------------
$ git log --merge
$ gitk --merge
-------------------------------------------------
This will list all commits which exist only on HEAD or on MERGE_HEAD,
and which touch an unmerged file.
These will display all commits which exist only on HEAD or on
MERGE_HEAD, and which touch an unmerged file.
We can now add the resolved version to the index and commit:
Each time you resolve the conflicts in a file and update the index:
-------------------------------------------------
$ git add file.txt
$ git commit
-------------------------------------------------
Note that the commit message will already be filled in for you with
some information about the merge. Normally you can just use this
default message unchanged, but you may add additional commentary of
your own if desired.
the different stages of that file will be "collapsed", after which
git-diff will (by default) no longer show diffs for that file.
[[undoing-a-merge]]
undoing a merge
@@ -1255,7 +1317,7 @@ $ git reset --hard HEAD
Or, if you've already commited the merge that you want to throw away,
-------------------------------------------------
$ git reset --hard HEAD^
$ git reset --hard ORIG_HEAD
-------------------------------------------------
However, this last command can be dangerous in some cases--never
@@ -1328,6 +1390,7 @@ with the changes to be reverted, then you will be asked to fix
conflicts manually, just as in the case of <<resolving-a-merge,
resolving a merge>>.
[[fixing-a-mistake-by-editing-history]]
Fixing a mistake by editing history
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1479,7 +1542,7 @@ Examining dangling objects
In some situations the reflog may not be able to save you. For
example, suppose you delete a branch, then realize you need the history
it pointed you. The reflog is also deleted; however, if you have not
it contained. The reflog is also deleted; however, if you have not
yet pruned the repository, then you may still be able to find
the lost commits; run git-fsck and watch for output that mentions
"dangling commits":
@@ -1505,7 +1568,7 @@ history that is described by all your existing branches and tags. Thus
you get exactly the history reachable from that commit that is lost.
(And notice that it might not be just one commit: we only report the
"tip of the line" as being dangling, but there might be a whole deep
and complex commit history that was gotten dropped.)
and complex commit history that was dropped.)
If you decide you want the history back, you can always create a new
reference pointing to it, for example, a new branch:
@@ -1561,7 +1624,7 @@ repository that you pulled from.
(But note that no such commit will be created in the case of a
<<fast-forwards,fast forward>>; instead, your branch will just be
updated to point to the latest commit from the upstream branch).
updated to point to the latest commit from the upstream branch.)
The git-pull command can also be given "." as the "remote" repository,
in which case it just merges in a branch from the current repository; so
@@ -1638,8 +1701,8 @@ updates with git pull>>".
If you and maintainer both have accounts on the same machine, then
then you can just pull changes from each other's repositories
directly; note that all of the command (gitlink:git-clone[1],
git-fetch[1], git-pull[1], etc.) which accept a URL as an argument
directly; note that all of the commands (gitlink:git-clone[1],
git-fetch[1], git-pull[1], etc.) that accept a URL as an argument
will also accept a local file patch; so, for example, you can
use
@@ -1832,7 +1895,7 @@ that makes it easy for them to read your changes, verify that they are
correct, and understand why you made each change.
If you present all of your changes as a single patch (or commit), they
may find it is too much to digest all at once.
may find that it is too much to digest all at once.
If you present them with the entire history of your work, complete with
mistakes, corrections, and dead ends, they may be overwhelmed.
@@ -1858,11 +1921,8 @@ you are rewriting history.
Keeping a patch series up to date using git-rebase
--------------------------------------------------
Suppose you have a series of commits in a branch "mywork", which
originally branched off from "origin".
Suppose you create a branch "mywork" on a remote-tracking branch
"origin", and created some commits on top of it:
Suppose that you create a branch "mywork" on a remote-tracking branch
"origin", and create some commits on top of it:
-------------------------------------------------
$ git checkout -b mywork origin
@@ -1933,6 +1993,51 @@ return mywork to the state it had before you started the rebase:
$ git rebase --abort
-------------------------------------------------
Modifying a single commit
-------------------------
We saw in <<fixing-a-mistake-by-editing-history>> that you can replace the
most recent commit using
-------------------------------------------------
$ git commit --amend
-------------------------------------------------
which will replace the old commit by a new commit incorporating your
changes, giving you a chance to edit the old commit message first.
You can also use a combination of this and gitlink:git-rebase[1] to edit
commits further back in your history. First, tag the problematic commit with
-------------------------------------------------
$ git tag bad mywork~5
-------------------------------------------------
(Either gitk or git-log may be useful for finding the commit.)
Then check out a new branch at that commit, edit it, and rebase the rest of
the series on top of it:
-------------------------------------------------
$ git checkout -b TMP bad
$ # make changes here and update the index
$ git commit --amend
$ git rebase --onto TMP bad mywork
-------------------------------------------------
When you're done, you'll be left with mywork checked out, with the top patches
on mywork reapplied on top of the modified commit you created in TMP. You can
then clean up with
-------------------------------------------------
$ git branch -d TMP
$ git tag -d bad
-------------------------------------------------
Note that the immutable nature of git history means that you haven't really
"modified" existing commits; instead, you have replaced the old commits with
new commits having new object names.
Reordering or selecting from a patch series
-------------------------------------------
@@ -1966,7 +2071,7 @@ Other tools
-----------
There are numerous other tools, such as stgit, which exist for the
purpose of maintaining a patch series. These are out of the scope of
purpose of maintaining a patch series. These are outside of the scope of
this manual.
Problems with rewriting history
@@ -2088,7 +2193,7 @@ descendant of the old head, you may force the update with:
$ git fetch git://example.com/proj.git +master:refs/remotes/example/master
-------------------------------------------------
Note the addition of the "+" sign. Be aware that commits which the
Note the addition of the "+" sign. Be aware that commits that the
old version of example/master pointed at may be lost, as we saw in
the previous section.
@@ -2096,7 +2201,7 @@ Configuring remote branches
---------------------------
We saw above that "origin" is just a shortcut to refer to the
repository which you originally cloned from. This information is
repository that you originally cloned from. This information is
stored in git configuration variables, which you can see using
gitlink:git-config[1]:
@@ -2158,6 +2263,7 @@ See gitlink:git-config[1] for more details on the configuration
options mentioned above.
[[git-internals]]
Git internals
=============
@@ -2407,7 +2513,7 @@ conflicts between different tree objects, allowing each pathname to be
associated with sufficient information about the trees involved that
you can create a three-way merge between them.'
Those are the three ONLY things that the directory cache does. It's a
Those are the ONLY three things that the directory cache does. It's a
cache, and the normal operation is to re-generate it completely from a
known tree object, or update/compare it with a live tree that is being
developed. If you blow the directory cache away entirely, you generally
@@ -2939,11 +3045,6 @@ provides.
Simplify beginning by suggesting disconnected head instead of
temporary branch creation?
Explain how to refer to file stages in the "how to resolve a merge"
section: diff -1, -2, -3, --ours, --theirs :1:/path notation. The
"git ls-files --unmerged --stage" thing is sorta useful too,
actually. And note gitk --merge.
Add more good examples. Entire sections of just cookbook examples
might be a good idea; maybe make an "advanced examples" section a
standard end-of-chapter section?

View File

@@ -89,6 +89,9 @@ all::
#
# Define NO_ICONV if your libc does not properly support iconv.
#
# Define OLD_ICONV if your library has an old iconv(), where the second
# (input buffer pointer) parameter is declared with type (const char **).
#
# Define NO_R_TO_GCC if your gcc does not like "-R/path/lib" that
# tells runtime paths to dynamic libraries; "-Wl,-rpath=/path/lib"
# is used instead.
@@ -277,6 +280,7 @@ BUILTIN_OBJS = \
builtin-archive.o \
builtin-blame.o \
builtin-branch.o \
builtin-bundle.o \
builtin-cat-file.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
@@ -377,7 +381,6 @@ ifeq ($(uname_O),Cygwin)
NO_STRCASESTR = YesPlease
NO_SYMLINK_HEAD = YesPlease
NEEDS_LIBICONV = YesPlease
NO_C99_FORMAT = YesPlease
NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
# There are conflicting reports about this.
@@ -601,6 +604,10 @@ ifdef NO_ICONV
BASIC_CFLAGS += -DNO_ICONV
endif
ifdef OLD_ICONV
BASIC_CFLAGS += -DOLD_ICONV
endif
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
@@ -805,7 +812,7 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
$(DIFF_OBJS): diffcore.h

View File

@@ -262,7 +262,7 @@ static int write_tar_entry(const unsigned char *sha1,
static struct strbuf path;
int filenamelen = strlen(filename);
void *buffer;
char type[20];
enum object_type type;
unsigned long size;
if (!path.alloc) {
@@ -283,7 +283,7 @@ static int write_tar_entry(const unsigned char *sha1,
buffer = NULL;
size = 0;
} else {
buffer = read_sha1_file(sha1, type, &size);
buffer = read_sha1_file(sha1, &type, &size);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
}

View File

@@ -167,7 +167,7 @@ static int write_zip_entry(const unsigned char *sha1,
int pathlen;
unsigned char *out;
char *path;
char type[20];
enum object_type type;
void *buffer = NULL;
void *deflated = NULL;
@@ -195,7 +195,7 @@ static int write_zip_entry(const unsigned char *sha1,
if (S_ISREG(mode) && zlib_compression_level != 0)
method = 8;
result = 0;
buffer = read_sha1_file(sha1, type, &size);
buffer = read_sha1_file(sha1, &type, &size);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
crc = crc32(crc, buffer, size);

6
blob.c
View File

@@ -30,18 +30,18 @@ int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
int parse_blob(struct blob *item)
{
char type[20];
enum object_type type;
void *buffer;
unsigned long size;
int ret;
if (item->object.parsed)
return 0;
buffer = read_sha1_file(item->object.sha1, type, &size);
buffer = read_sha1_file(item->object.sha1, &type, &size);
if (!buffer)
return error("Could not read %s",
sha1_to_hex(item->object.sha1));
if (strcmp(type, blob_type))
if (type != OBJ_BLOB)
return error("Object %s not a blob",
sha1_to_hex(item->object.sha1));
ret = parse_blob_buffer(item, buffer, size);

View File

@@ -1607,7 +1607,8 @@ static int apply_line(char *output, const char *patch, int plen)
int need_fix_leading_space = 0;
char *buf;
if ((new_whitespace != strip_whitespace) || !whitespace_error) {
if ((new_whitespace != strip_whitespace) || !whitespace_error ||
*patch != '+') {
memcpy(output, patch + 1, plen);
return plen;
}
@@ -1911,11 +1912,11 @@ static int apply_binary(struct buffer_desc *desc, struct patch *patch)
if (has_sha1_file(sha1)) {
/* We already have the postimage */
char type[10];
enum object_type type;
unsigned long size;
free(desc->buffer);
desc->buffer = read_sha1_file(sha1, type, &size);
desc->buffer = read_sha1_file(sha1, &type, &size);
if (!desc->buffer)
return error("the necessary postimage %s for "
"'%s' cannot be read",
@@ -1971,8 +1972,8 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
buf = NULL;
if (cached) {
if (ce) {
char type[20];
buf = read_sha1_file(ce->sha1, type, &size);
enum object_type type;
buf = read_sha1_file(ce->sha1, &type, &size);
if (!buf)
return error("read of %s failed",
patch->old_name);
@@ -2358,7 +2359,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
char *nbuf;
unsigned long nsize;
if (S_ISLNK(mode))
if (has_symlinks && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL
* terminated.
*/

View File

@@ -252,8 +252,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
memset(&ar, 0, sizeof(ar));
tree_idx = parse_archive_args(argc, argv, &ar);
if (prefix == NULL)
prefix = setup_git_directory();
argv += tree_idx;
parse_treeish_arg(argv, &ar.args, prefix);

View File

@@ -87,9 +87,9 @@ struct origin {
static char *fill_origin_blob(struct origin *o, mmfile_t *file)
{
if (!o->file.ptr) {
char type[10];
enum object_type type;
num_read_blob++;
file->ptr = read_sha1_file(o->blob_sha1, type,
file->ptr = read_sha1_file(o->blob_sha1, &type,
(unsigned long *)(&(file->size)));
o->file = *file;
}
@@ -263,7 +263,6 @@ static struct origin *get_origin(struct scoreboard *sb,
static int fill_blob_sha1(struct origin *origin)
{
unsigned mode;
char type[10];
if (!is_null_sha1(origin->blob_sha1))
return 0;
@@ -271,8 +270,7 @@ static int fill_blob_sha1(struct origin *origin)
origin->path,
origin->blob_sha1, &mode))
goto error_out;
if (sha1_object_info(origin->blob_sha1, type, NULL) ||
strcmp(type, blob_type))
if (sha1_object_info(origin->blob_sha1, NULL) != OBJ_BLOB)
goto error_out;
return 0;
error_out:
@@ -1322,10 +1320,10 @@ static void get_commit_info(struct commit *commit,
* we now need to populate them for output.
*/
if (!commit->buffer) {
char type[20];
enum object_type type;
unsigned long size;
commit->buffer =
read_sha1_file(commit->object.sha1, type, &size);
read_sha1_file(commit->object.sha1, &type, &size);
}
ret->author = author_buf;
get_ac_line(commit->buffer, "\nauthor ",
@@ -2006,7 +2004,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
buf[fin_size] = 0;
origin->file.ptr = buf;
origin->file.size = fin_size;
pretend_sha1_file(buf, fin_size, blob_type, origin->blob_sha1);
pretend_sha1_file(buf, fin_size, OBJ_BLOB, origin->blob_sha1);
commit->util = origin;
/*
@@ -2068,7 +2066,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
int show_stats = 0;
const char *revs_file = NULL;
const char *final_commit_name = NULL;
char type[10];
enum object_type type;
const char *bottomtop = NULL;
const char *contents_from = NULL;
@@ -2302,7 +2300,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
if (fill_blob_sha1(o))
die("no such path %s in %s", path, final_commit_name);
sb.final_buf = read_sha1_file(o->blob_sha1, type,
sb.final_buf = read_sha1_file(o->blob_sha1, &type,
&sb.final_buf_size);
}
num_read_blob++;

View File

@@ -12,7 +12,7 @@
#include "builtin.h"
static const char builtin_branch_usage[] =
"git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length>]]";
"git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [--color | --no-color] [-r | -a] [-v [--abbrev=<length> | --no-abbrev]]";
#define REF_UNKNOWN_TYPE 0x00
#define REF_LOCAL_BRANCH 0x01
@@ -446,8 +446,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
reflog = 1;
continue;
}
if (!prefixcmp(arg, "--no-abbrev")) {
abbrev = 0;
continue;
}
if (!prefixcmp(arg, "--abbrev=")) {
abbrev = atoi(arg+9);
abbrev = strtoul(arg + 9, NULL, 10);
if (abbrev < MINIMUM_ABBREV)
abbrev = MINIMUM_ABBREV;
else if (abbrev > 40)
abbrev = 40;
continue;
}
if (!strcmp(arg, "-v")) {

424
builtin-bundle.c Normal file
View File

@@ -0,0 +1,424 @@
#include "cache.h"
#include "object.h"
#include "commit.h"
#include "diff.h"
#include "revision.h"
#include "list-objects.h"
#include "exec_cmd.h"
/*
* Basic handler for bundle files to connect repositories via sneakernet.
* Invocation must include action.
* This function can create a bundle or provide information on an existing
* bundle supporting git-fetch, git-pull, and git-ls-remote
*/
static const char *bundle_usage="git-bundle (create <bundle> <git-rev-list args> | verify <bundle> | list-heads <bundle> [refname]... | unbundle <bundle> [refname]... )";
static const char bundle_signature[] = "# v2 git bundle\n";
struct ref_list {
unsigned int nr, alloc;
struct ref_list_entry {
unsigned char sha1[20];
char *name;
} *list;
};
static void add_to_ref_list(const unsigned char *sha1, const char *name,
struct ref_list *list)
{
if (list->nr + 1 >= list->alloc) {
list->alloc = alloc_nr(list->nr + 1);
list->list = xrealloc(list->list,
list->alloc * sizeof(list->list[0]));
}
memcpy(list->list[list->nr].sha1, sha1, 20);
list->list[list->nr].name = xstrdup(name);
list->nr++;
}
struct bundle_header {
struct ref_list prerequisites;
struct ref_list references;
};
/* this function returns the length of the string */
static int read_string(int fd, char *buffer, int size)
{
int i;
for (i = 0; i < size - 1; i++) {
int count = xread(fd, buffer + i, 1);
if (count < 0)
return error("Read error: %s", strerror(errno));
if (count == 0) {
i--;
break;
}
if (buffer[i] == '\n')
break;
}
buffer[i + 1] = '\0';
return i + 1;
}
/* returns an fd */
static int read_header(const char *path, struct bundle_header *header) {
char buffer[1024];
int fd = open(path, O_RDONLY);
if (fd < 0)
return error("could not open '%s'", path);
if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
strcmp(buffer, bundle_signature)) {
close(fd);
return error("'%s' does not look like a v2 bundle file", path);
}
while (read_string(fd, buffer, sizeof(buffer)) > 0
&& buffer[0] != '\n') {
int is_prereq = buffer[0] == '-';
int offset = is_prereq ? 1 : 0;
int len = strlen(buffer);
unsigned char sha1[20];
struct ref_list *list = is_prereq ? &header->prerequisites
: &header->references;
char delim;
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if (get_sha1_hex(buffer + offset, sha1)) {
warn("unrecognized header: %s", buffer);
continue;
}
delim = buffer[40 + offset];
if (!isspace(delim) && (delim != '\0' || !is_prereq))
die ("invalid header: %s", buffer);
add_to_ref_list(sha1, isspace(delim) ?
buffer + 41 + offset : "", list);
}
return fd;
}
/* if in && *in >= 0, take that as input file descriptor instead */
static int fork_with_pipe(const char **argv, int *in, int *out)
{
int needs_in, needs_out;
int fdin[2], fdout[2], pid;
needs_in = in && *in < 0;
if (needs_in) {
if (pipe(fdin) < 0)
return error("could not setup pipe");
*in = fdin[1];
}
needs_out = out && *out < 0;
if (needs_out) {
if (pipe(fdout) < 0)
return error("could not setup pipe");
*out = fdout[0];
}
if ((pid = fork()) < 0) {
if (needs_in) {
close(fdin[0]);
close(fdin[1]);
}
if (needs_out) {
close(fdout[0]);
close(fdout[1]);
}
return error("could not fork");
}
if (!pid) {
if (needs_in) {
dup2(fdin[0], 0);
close(fdin[0]);
close(fdin[1]);
} else if (in) {
dup2(*in, 0);
close(*in);
}
if (needs_out) {
dup2(fdout[1], 1);
close(fdout[0]);
close(fdout[1]);
} else if (out) {
dup2(*out, 1);
close(*out);
}
exit(execv_git_cmd(argv));
}
if (needs_in)
close(fdin[0]);
else if (in)
close(*in);
if (needs_out)
close(fdout[1]);
else if (out)
close(*out);
return pid;
}
static int verify_bundle(struct bundle_header *header)
{
/*
* Do fast check, then if any prereqs are missing then go line by line
* to be verbose about the errors
*/
struct ref_list *p = &header->prerequisites;
struct rev_info revs;
const char *argv[] = {NULL, "--all"};
struct object_array refs;
struct commit *commit;
int i, ret = 0, req_nr;
const char *message = "Repository lacks these prerequisite commits:";
init_revisions(&revs, NULL);
for (i = 0; i < p->nr; i++) {
struct ref_list_entry *e = p->list + i;
struct object *o = parse_object(e->sha1);
if (o) {
o->flags |= BOUNDARY_SHOW;
add_pending_object(&revs, o, e->name);
continue;
}
if (++ret == 1)
error(message);
error("%s %s", sha1_to_hex(e->sha1), e->name);
}
if (revs.pending.nr == 0)
return ret;
req_nr = revs.pending.nr;
setup_revisions(2, argv, &revs, NULL);
memset(&refs, 0, sizeof(struct object_array));
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
add_object_array(e->item, e->name, &refs);
}
prepare_revision_walk(&revs);
i = req_nr;
while (i && (commit = get_revision(&revs)))
if (commit->object.flags & BOUNDARY_SHOW)
i--;
for (i = 0; i < req_nr; i++)
if (!(refs.objects[i].item->flags & SHOWN)) {
if (++ret == 1)
error(message);
error("%s %s", sha1_to_hex(refs.objects[i].item->sha1),
refs.objects[i].name);
}
for (i = 0; i < refs.nr; i++)
clear_commit_marks((struct commit *)refs.objects[i].item, -1);
return ret;
}
static int list_heads(struct bundle_header *header, int argc, const char **argv)
{
int i;
struct ref_list *r = &header->references;
for (i = 0; i < r->nr; i++) {
if (argc > 1) {
int j;
for (j = 1; j < argc; j++)
if (!strcmp(r->list[i].name, argv[j]))
break;
if (j == argc)
continue;
}
printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
r->list[i].name);
}
return 0;
}
static void show_commit(struct commit *commit)
{
write_or_die(1, sha1_to_hex(commit->object.sha1), 40);
write_or_die(1, "\n", 1);
if (commit->parents) {
free_commit_list(commit->parents);
commit->parents = NULL;
}
}
static void show_object(struct object_array_entry *p)
{
/* An object with name "foo\n0000000..." can be used to
* confuse downstream git-pack-objects very badly.
*/
const char *ep = strchr(p->name, '\n');
int len = ep ? ep - p->name : strlen(p->name);
write_or_die(1, sha1_to_hex(p->item->sha1), 40);
write_or_die(1, " ", 1);
if (len)
write_or_die(1, p->name, len);
write_or_die(1, "\n", 1);
}
static int create_bundle(struct bundle_header *header, const char *path,
int argc, const char **argv)
{
int bundle_fd = -1;
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
const char **argv_pack = xmalloc(4 * sizeof(const char *));
int pid, in, out, i, status;
char buffer[1024];
struct rev_info revs;
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_WRONLY, 0666));
if (bundle_fd < 0)
return error("Could not write to '%s'", path);
/* write signature */
write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
/* write prerequisites */
memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *));
argv_boundary[0] = "rev-list";
argv_boundary[1] = "--boundary";
argv_boundary[2] = "--pretty=oneline";
argv_boundary[argc + 2] = NULL;
out = -1;
pid = fork_with_pipe(argv_boundary, NULL, &out);
if (pid < 0)
return -1;
while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
if (buffer[0] == '-')
write_or_die(bundle_fd, buffer, i);
while ((i = waitpid(pid, &status, 0)) < 0)
if (errno != EINTR)
return error("rev-list died");
if (!WIFEXITED(status) || WEXITSTATUS(status))
return error("rev-list died %d", WEXITSTATUS(status));
/* write references */
save_commit_buffer = 0;
init_revisions(&revs, NULL);
revs.tag_objects = 1;
revs.tree_objects = 1;
revs.blob_objects = 1;
argc = setup_revisions(argc, argv, &revs, NULL);
if (argc > 1)
return error("unrecognized argument: %s'", argv[1]);
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
if (!(e->item->flags & UNINTERESTING)) {
unsigned char sha1[20];
char *ref;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, ref, strlen(ref));
write_or_die(bundle_fd, "\n", 1);
free(ref);
}
}
/* end header */
write_or_die(bundle_fd, "\n", 1);
/* write pack */
argv_pack[0] = "pack-objects";
argv_pack[1] = "--all-progress";
argv_pack[2] = "--stdout";
argv_pack[3] = NULL;
in = -1;
out = bundle_fd;
pid = fork_with_pipe(argv_pack, &in, &out);
if (pid < 0)
return error("Could not spawn pack-objects");
close(1);
dup2(in, 1);
close(in);
prepare_revision_walk(&revs);
traverse_commit_list(&revs, show_commit, show_object);
close(1);
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return -1;
if (!WIFEXITED(status) || WEXITSTATUS(status))
return error ("pack-objects died");
return 0;
}
static int unbundle(struct bundle_header *header, int bundle_fd,
int argc, const char **argv)
{
const char *argv_index_pack[] = {"index-pack", "--stdin", NULL};
int pid, status, dev_null;
if (verify_bundle(header))
return -1;
dev_null = open("/dev/null", O_WRONLY);
pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
if (pid < 0)
return error("Could not spawn index-pack");
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return error("index-pack died");
if (!WIFEXITED(status) || WEXITSTATUS(status))
return error("index-pack exited with status %d",
WEXITSTATUS(status));
return list_heads(header, argc, argv);
}
int cmd_bundle(int argc, const char **argv, const char *prefix)
{
struct bundle_header header;
int nongit = 0;
const char *cmd, *bundle_file;
int bundle_fd = -1;
char buffer[PATH_MAX];
if (argc < 3)
usage(bundle_usage);
cmd = argv[1];
bundle_file = argv[2];
argc -= 2;
argv += 2;
prefix = setup_git_directory_gently(&nongit);
if (prefix && bundle_file[0] != '/') {
snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
bundle_file = buffer;
}
memset(&header, 0, sizeof(header));
if (strcmp(cmd, "create") &&
!(bundle_fd = read_header(bundle_file, &header)))
return 1;
if (!strcmp(cmd, "verify")) {
close(bundle_fd);
if (verify_bundle(&header))
return 1;
fprintf(stderr, "%s is okay\n", bundle_file);
return 0;
}
if (!strcmp(cmd, "list-heads")) {
close(bundle_fd);
return !!list_heads(&header, argc, argv);
}
if (!strcmp(cmd, "create")) {
if (nongit)
die("Need a repository to create a bundle.");
return !!create_bundle(&header, bundle_file, argc, argv);
} else if (!strcmp(cmd, "unbundle")) {
if (nongit)
die("Need a repository to unbundle.");
return !!unbundle(&header, bundle_fd, argc, argv);
} else
usage(bundle_usage);
}

View File

@@ -79,7 +79,7 @@ static void pprint_tag(const unsigned char *sha1, const char *buf, unsigned long
int cmd_cat_file(int argc, const char **argv, const char *prefix)
{
unsigned char sha1[20];
char type[20];
enum object_type type;
void *buf;
unsigned long size;
int opt;
@@ -100,14 +100,16 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
buf = NULL;
switch (opt) {
case 't':
if (!sha1_object_info(sha1, type, NULL)) {
printf("%s\n", type);
type = sha1_object_info(sha1, NULL);
if (type > 0) {
printf("%s\n", typename(type));
return 0;
}
break;
case 's':
if (!sha1_object_info(sha1, type, &size)) {
type = sha1_object_info(sha1, &size);
if (type > 0) {
printf("%lu\n", size);
return 0;
}
@@ -117,17 +119,18 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix)
return !has_sha1_file(sha1);
case 'p':
if (sha1_object_info(sha1, type, NULL))
type = sha1_object_info(sha1, NULL);
if (type < 0)
die("Not a valid object name %s", argv[2]);
/* custom pretty-print here */
if (!strcmp(type, tree_type))
if (type == OBJ_TREE)
return cmd_ls_tree(2, argv + 1, NULL);
buf = read_sha1_file(sha1, type, &size);
buf = read_sha1_file(sha1, &type, &size);
if (!buf)
die("Cannot read object %s", argv[2]);
if (!strcmp(type, tag_type)) {
if (type == OBJ_TAG) {
pprint_tag(sha1, buf, size);
return 0;
}

View File

@@ -45,15 +45,14 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
memcpy(buf + size, one_line, len);
}
static void check_valid(unsigned char *sha1, const char *expect)
static void check_valid(unsigned char *sha1, enum object_type expect)
{
char type[20];
if (sha1_object_info(sha1, type, NULL))
enum object_type type = sha1_object_info(sha1, NULL);
if (type < 0)
die("%s is not a valid object", sha1_to_hex(sha1));
if (expect && strcmp(type, expect))
if (type != expect)
die("%s is not a valid '%s' object", sha1_to_hex(sha1),
expect);
typename(expect));
}
/*
@@ -101,7 +100,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (get_sha1(argv[1], tree_sha1))
die("Not a valid object name %s", argv[1]);
check_valid(tree_sha1, tree_type);
check_valid(tree_sha1, OBJ_TREE);
for (i = 2; i < argc; i += 2) {
const char *a, *b;
a = argv[i]; b = argv[i+1];
@@ -112,7 +111,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
die("Too many parents (%d max)", MAXPARENT);
if (get_sha1(b, parent_sha1[parents]))
die("Not a valid object name %s", b);
check_valid(parent_sha1[parents], commit_type);
check_valid(parent_sha1[parents], OBJ_COMMIT);
if (new_parent(parents))
parents++;
}

View File

@@ -10,42 +10,24 @@
#include "builtin.h"
static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc|-n|--no-index] [<common diff options>] [<path>...]"
COMMON_DIFF_OPTIONS_HELP;
int cmd_diff_files(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
int silent = 0;
int nongit = 0;
prefix = setup_git_directory_gently(&nongit);
init_revisions(&rev, prefix);
git_config(git_default_config); /* no "diff" UI options */
rev.abbrev = 0;
argc = setup_revisions(argc, argv, &rev, NULL);
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "--base"))
rev.max_count = 1;
else if (!strcmp(argv[1], "--ours"))
rev.max_count = 2;
else if (!strcmp(argv[1], "--theirs"))
rev.max_count = 3;
else if (!strcmp(argv[1], "-q"))
silent = 1;
else
usage(diff_files_usage);
argv++; argc--;
}
if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix))
argc = 0;
else
argc = setup_revisions(argc, argv, &rev, NULL);
if (!rev.diffopt.output_format)
rev.diffopt.output_format = DIFF_FORMAT_RAW;
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* rev.max_count is reasonable (0 <= n <= 3),
* there is no other revision filtering parameters.
*/
if (rev.pending.nr ||
rev.min_age != -1 || rev.max_age != -1)
usage(diff_files_usage);
return run_diff_files(&rev, silent);
return run_diff_files_cmd(&rev, argc, argv);
}

View File

@@ -38,5 +38,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
if (rev.pending.nr != 1 ||
rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
usage(diff_cache_usage);
if (read_cache() < 0) {
perror("read_cache");
return -1;
}
return run_diff_index(&rev, cached);
}

View File

@@ -25,40 +25,6 @@ struct blobinfo {
static const char builtin_diff_usage[] =
"git-diff <options> <rev>{0,2} -- <path>*";
static int builtin_diff_files(struct rev_info *revs,
int argc, const char **argv)
{
int silent = 0;
while (1 < argc) {
const char *arg = argv[1];
if (!strcmp(arg, "--base"))
revs->max_count = 1;
else if (!strcmp(arg, "--ours"))
revs->max_count = 2;
else if (!strcmp(arg, "--theirs"))
revs->max_count = 3;
else if (!strcmp(arg, "-q"))
silent = 1;
else
usage(builtin_diff_usage);
argv++; argc--;
}
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* specified rev.max_count is reasonable (0 <= n <= 3), and
* there is no other revision filtering parameter.
*/
if (revs->pending.nr ||
revs->min_age != -1 ||
revs->max_age != -1 ||
3 < revs->max_count)
usage(builtin_diff_usage);
if (revs->max_count < 0 &&
(revs->diffopt.output_format & DIFF_FORMAT_PATCH))
revs->combine_merges = revs->dense_combined_merges = 1;
return run_diff_files(revs, silent);
}
static void stuff_change(struct diff_options *opt,
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
@@ -151,6 +117,10 @@ static int builtin_diff_index(struct rev_info *revs,
revs->max_count != -1 || revs->min_age != -1 ||
revs->max_age != -1)
usage(builtin_diff_usage);
if (read_cache() < 0) {
perror("read_cache");
return -1;
}
return run_diff_index(revs, cached);
}
@@ -219,6 +189,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
int ents = 0, blobs = 0, paths = 0;
const char *path = NULL;
struct blobinfo blob[2];
int nongit = 0;
/*
* We could get N tree-ish in the rev.pending_objects list.
@@ -240,10 +211,14 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
* Other cases are errors.
*/
prefix = setup_git_directory_gently(&nongit);
git_config(git_diff_ui_config);
init_revisions(&rev, prefix);
argc = setup_revisions(argc, argv, &rev, NULL);
if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix))
argc = 0;
else
argc = setup_revisions(argc, argv, &rev, NULL);
if (!rev.diffopt.output_format) {
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
if (diff_setup_done(&rev.diffopt) < 0)
@@ -317,7 +292,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
if (!ents) {
switch (blobs) {
case 0:
return builtin_diff_files(&rev, argc, argv);
return run_diff_files_cmd(&rev, argc, argv);
break;
case 1:
if (paths != 1)

View File

@@ -261,13 +261,15 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
else if (!strcmp(argv[1], "--no-summary"))
merge_summary = 0;
else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
if (argc < 2)
if (argc < 3)
die ("Which file?");
if (!strcmp(argv[2], "-"))
in = stdin;
else {
fclose(in);
in = fopen(argv[2], "r");
if (!in)
die("cannot open %s", argv[2]);
}
argc--; argv++;
} else

View File

@@ -173,8 +173,8 @@ static void verify_format(const char *format)
*/
static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
{
char type[20];
void *buf = read_sha1_file(sha1, type, sz);
enum object_type type;
void *buf = read_sha1_file(sha1, &type, sz);
if (buf)
*obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
@@ -196,7 +196,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
if (deref)
name++;
if (!strcmp(name, "objecttype"))
v->s = type_names[obj->type];
v->s = typename(obj->type);
else if (!strcmp(name, "objectsize")) {
char *s = xmalloc(40);
sprintf(s, "%lu", sz);

View File

@@ -84,11 +84,11 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char
{
unsigned long size;
char *data;
char type[20];
enum object_type type;
char *to_free = NULL;
int hit;
data = read_sha1_file(sha1, type, &size);
data = read_sha1_file(sha1, &type, &size);
if (!data) {
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
return 0;
@@ -380,10 +380,10 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
else if (S_ISREG(entry.mode))
hit |= grep_sha1(opt, entry.sha1, path_buf, tn_len);
else if (S_ISDIR(entry.mode)) {
char type[20];
enum object_type type;
struct tree_desc sub;
void *data;
data = read_sha1_file(entry.sha1, type, &sub.size);
data = read_sha1_file(entry.sha1, &type, &sub.size);
if (!data)
die("unable to read tree (%s)",
sha1_to_hex(entry.sha1));

View File

@@ -89,8 +89,8 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
static int show_object(const unsigned char *sha1, int suppress_header)
{
unsigned long size;
char type[20];
char *buf = read_sha1_file(sha1, type, &size);
enum object_type type;
char *buf = read_sha1_file(sha1, &type, &size);
int offset = 0;
if (!buf)

View File

@@ -406,6 +406,11 @@ static int is_rfc2822_header(char *line)
*/
int ch;
char *cp = line;
/* Count mbox From headers as headers */
if (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6))
return 1;
while ((ch = *cp++)) {
if (ch == ':')
return cp != line;
@@ -417,30 +422,61 @@ static int is_rfc2822_header(char *line)
return 0;
}
/*
* sz is size of 'line' buffer in bytes. Must be reasonably
* long enough to hold one physical real-world e-mail line.
*/
static int read_one_header_line(char *line, int sz, FILE *in)
{
int ofs = 0;
while (ofs < sz) {
int peek, len;
if (fgets(line + ofs, sz - ofs, in) == NULL)
break;
len = eatspace(line + ofs);
if ((len == 0) || !is_rfc2822_header(line)) {
/* Re-add the newline */
line[ofs + len] = '\n';
line[ofs + len + 1] = '\0';
break;
}
ofs += len;
/* Yuck, 2822 header "folding" */
int len;
/*
* We will read at most (sz-1) bytes and then potentially
* re-add NUL after it. Accessing line[sz] after this is safe
* and we can allow len to grow up to and including sz.
*/
sz--;
/* Get the first part of the line. */
if (!fgets(line, sz, in))
return 0;
/*
* Is it an empty line or not a valid rfc2822 header?
* If so, stop here, and return false ("not a header")
*/
len = eatspace(line);
if (!len || !is_rfc2822_header(line)) {
/* Re-add the newline */
line[len] = '\n';
line[len + 1] = '\0';
return 0;
}
/*
* Now we need to eat all the continuation lines..
* Yuck, 2822 header "folding"
*/
for (;;) {
int peek, addlen;
static char continuation[1000];
peek = fgetc(in); ungetc(peek, in);
if (peek != ' ' && peek != '\t')
break;
if (!fgets(continuation, sizeof(continuation), in))
break;
addlen = eatspace(continuation);
if (len < sz - 1) {
if (addlen >= sz - len)
addlen = sz - len - 1;
memcpy(line + len, continuation, addlen);
len += addlen;
}
}
/* Count mbox From headers as headers */
if (!ofs && (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6)))
ofs = 1;
return ofs;
line[len] = 0;
return 1;
}
static int decode_q_segment(char *in, char *ot, char *ep, int rfc2047)

View File

@@ -230,8 +230,8 @@ static unsigned char *find_packed_object_name(struct packed_git *p,
static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
{
unsigned long othersize, delta_size;
char type[10];
void *otherbuf = read_sha1_file(entry->delta->sha1, type, &othersize);
enum object_type type;
void *otherbuf = read_sha1_file(entry->delta->sha1, &type, &othersize);
void *delta_buf;
if (!otherbuf)
@@ -375,7 +375,7 @@ static unsigned long write_object(struct sha1file *f,
struct object_entry *entry)
{
unsigned long size;
char type[10];
enum object_type type;
void *buf;
unsigned char header[10];
unsigned hdrlen, datalen;
@@ -416,7 +416,7 @@ static unsigned long write_object(struct sha1file *f,
}
if (!to_reuse) {
buf = read_sha1_file(entry->sha1, type, &size);
buf = read_sha1_file(entry->sha1, &type, &size);
if (!buf)
die("unable to read %s", sha1_to_hex(entry->sha1));
if (size != entry->size)
@@ -765,7 +765,7 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
struct pbase_tree_cache *ent, *nent;
void *data;
unsigned long size;
char type[20];
enum object_type type;
int neigh;
int my_ix = pbase_tree_cache_ix(sha1);
int available_ix = -1;
@@ -792,10 +792,10 @@ static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
/* Did not find one. Either we got a bogus request or
* we need to read and perhaps cache.
*/
data = read_sha1_file(sha1, type, &size);
data = read_sha1_file(sha1, &type, &size);
if (!data)
return NULL;
if (strcmp(type, tree_type)) {
if (type != OBJ_TREE) {
free(data);
return NULL;
}
@@ -854,19 +854,19 @@ static void add_pbase_object(struct tree_desc *tree,
while (tree_entry(tree,&entry)) {
unsigned long size;
char type[20];
enum object_type type;
if (entry.pathlen != cmplen ||
memcmp(entry.path, name, cmplen) ||
!has_sha1_file(entry.sha1) ||
sha1_object_info(entry.sha1, type, &size))
(type = sha1_object_info(entry.sha1, &size)) < 0)
continue;
if (name[cmplen] != '/') {
unsigned hash = name_hash(fullname);
add_object_entry(entry.sha1, hash, 1);
return;
}
if (!strcmp(type, tree_type)) {
if (type == OBJ_TREE) {
struct tree_desc sub;
struct pbase_tree_cache *tree;
const char *down = name+cmplen+1;
@@ -978,8 +978,6 @@ static void add_preferred_base(unsigned char *sha1)
static void check_object(struct object_entry *entry)
{
char type[20];
if (entry->in_pack && !entry->preferred_base) {
struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
@@ -1062,21 +1060,10 @@ static void check_object(struct object_entry *entry)
/* Otherwise we would do the usual */
}
if (sha1_object_info(entry->sha1, type, &entry->size))
entry->type = sha1_object_info(entry->sha1, &entry->size);
if (entry->type < 0)
die("unable to get type of object %s",
sha1_to_hex(entry->sha1));
if (!strcmp(type, commit_type)) {
entry->type = OBJ_COMMIT;
} else if (!strcmp(type, tree_type)) {
entry->type = OBJ_TREE;
} else if (!strcmp(type, blob_type)) {
entry->type = OBJ_BLOB;
} else if (!strcmp(type, tag_type)) {
entry->type = OBJ_TAG;
} else
die("unable to pack object %s of type %s",
sha1_to_hex(entry->sha1), type);
}
static unsigned int check_delta_limit(struct object_entry *me, unsigned int n)
@@ -1206,7 +1193,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
struct object_entry *trg_entry = trg->entry;
struct object_entry *src_entry = src->entry;
unsigned long trg_size, src_size, delta_size, sizediff, max_size, sz;
char type[10];
enum object_type type;
void *delta_buf;
/* Don't bother doing diffs between different types */
@@ -1257,13 +1244,13 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
/* Load data if not already done */
if (!trg->data) {
trg->data = read_sha1_file(trg_entry->sha1, type, &sz);
trg->data = read_sha1_file(trg_entry->sha1, &type, &sz);
if (sz != trg_size)
die("object %s inconsistent object length (%lu vs %lu)",
sha1_to_hex(trg_entry->sha1), sz, trg_size);
}
if (!src->data) {
src->data = read_sha1_file(src_entry->sha1, type, &sz);
src->data = read_sha1_file(src_entry->sha1, &type, &sz);
if (sz != src_size)
die("object %s inconsistent object length (%lu vs %lu)",
sha1_to_hex(src_entry->sha1), sz, src_size);

View File

@@ -10,15 +10,10 @@ static int show_only;
static int prune_object(char *path, const char *filename, const unsigned char *sha1)
{
char buf[20];
const char *type;
if (show_only) {
if (sha1_object_info(sha1, buf, NULL))
type = "unknown";
else
type = buf;
printf("%s %s\n", sha1_to_hex(sha1), type);
enum object_type type = sha1_object_info(sha1, NULL);
printf("%s %s\n", sha1_to_hex(sha1),
(type > 0) ? typename(type) : "unknown");
return 0;
}
unlink(mkpath("%s/%s", path, filename));

View File

@@ -55,8 +55,8 @@ static int tree_is_complete(const unsigned char *sha1)
desc.buf = tree->buffer;
desc.size = tree->size;
if (!desc.buf) {
char type[20];
void *data = read_sha1_file(sha1, type, &desc.size);
enum object_type type;
void *data = read_sha1_file(sha1, &type, &desc.size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;

View File

@@ -119,18 +119,18 @@ struct obj_info {
static struct obj_info *obj_list;
static void added_object(unsigned nr, const char *type, void *data,
unsigned long size);
static void added_object(unsigned nr, enum object_type type,
void *data, unsigned long size);
static void write_object(unsigned nr, void *buf, unsigned long size,
const char *type)
static void write_object(unsigned nr, enum object_type type,
void *buf, unsigned long size)
{
if (write_sha1_file(buf, size, type, obj_list[nr].sha1) < 0)
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
}
static void resolve_delta(unsigned nr, const char *type,
static void resolve_delta(unsigned nr, enum object_type type,
void *base, unsigned long base_size,
void *delta, unsigned long delta_size)
{
@@ -143,12 +143,12 @@ static void resolve_delta(unsigned nr, const char *type,
if (!result)
die("failed to apply delta");
free(delta);
write_object(nr, result, result_size, type);
write_object(nr, type, result, result_size);
free(result);
}
static void added_object(unsigned nr, const char *type, void *data,
unsigned long size)
static void added_object(unsigned nr, enum object_type type,
void *data, unsigned long size)
{
struct delta_info **p = &delta_list;
struct delta_info *info;
@@ -167,33 +167,24 @@ static void added_object(unsigned nr, const char *type, void *data,
}
}
static void unpack_non_delta_entry(enum object_type kind, unsigned long size,
static void unpack_non_delta_entry(enum object_type type, unsigned long size,
unsigned nr)
{
void *buf = get_data(size);
const char *type;
switch (kind) {
case OBJ_COMMIT: type = commit_type; break;
case OBJ_TREE: type = tree_type; break;
case OBJ_BLOB: type = blob_type; break;
case OBJ_TAG: type = tag_type; break;
default: die("bad type %d", kind);
}
if (!dry_run && buf)
write_object(nr, buf, size, type);
write_object(nr, type, buf, size);
free(buf);
}
static void unpack_delta_entry(enum object_type kind, unsigned long delta_size,
static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
unsigned nr)
{
void *delta_data, *base;
unsigned long base_size;
char type[20];
unsigned char base_sha1[20];
if (kind == OBJ_REF_DELTA) {
if (type == OBJ_REF_DELTA) {
hashcpy(base_sha1, fill(20));
use(20);
delta_data = get_data(delta_size);
@@ -255,7 +246,7 @@ static void unpack_delta_entry(enum object_type kind, unsigned long delta_size,
}
}
base = read_sha1_file(base_sha1, type, &base_size);
base = read_sha1_file(base_sha1, &type, &base_size);
if (!base) {
error("failed to read delta-pack base object %s",
sha1_to_hex(base_sha1));

View File

@@ -109,11 +109,11 @@ static int add_file_to_cache(const char *path)
ce->ce_flags = htons(namelen);
fill_stat_cache_info(ce, &st);
if (trust_executable_bit)
if (trust_executable_bit && has_symlinks)
ce->ce_mode = create_ce_mode(st.st_mode);
else {
/* If there is an existing entry, pick the mode bits
* from it, otherwise assume unexecutable.
/* If there is an existing entry, pick the mode bits and type
* from it, otherwise assume unexecutable regular file.
*/
struct cache_entry *ent;
int pos = cache_name_pos(path, namelen);
@@ -487,6 +487,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
int prefix_length = prefix ? strlen(prefix) : 0;
char set_executable_bit = 0;
unsigned int refresh_flags = 0;
int lock_error = 0;
struct lock_file *lock_file;
git_config(git_default_config);
@@ -494,7 +495,9 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
/* We can't free this memory, it becomes part of a linked list parsed atexit() */
lock_file = xcalloc(1, sizeof(struct lock_file));
newfd = hold_lock_file_for_update(lock_file, get_index_file(), 1);
newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0);
if (newfd < 0)
lock_error = errno;
entries = read_cache();
if (entries < 0)
@@ -651,6 +654,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
finish:
if (active_cache_changed) {
if (newfd < 0) {
if (refresh_flags & REFRESH_QUIET)
exit(128);
die("unable to create '%s.lock': %s",
get_index_file(), strerror(lock_error));
}
if (write_cache(newfd, active_cache, active_nr) ||
close(newfd) || commit_lock_file(lock_file))
die("Unable to write new index file");

View File

@@ -19,6 +19,7 @@ extern int cmd_apply(int argc, const char **argv, const char *prefix);
extern int cmd_archive(int argc, const char **argv, const char *prefix);
extern int cmd_blame(int argc, const char **argv, const char *prefix);
extern int cmd_branch(int argc, const char **argv, const char *prefix);
extern int cmd_bundle(int argc, const char **argv, const char *prefix);
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);

48
cache.h
View File

@@ -108,7 +108,10 @@ static inline unsigned int create_ce_mode(unsigned int mode)
}
static inline unsigned int ce_mode_from_stat(struct cache_entry *ce, unsigned int mode)
{
extern int trust_executable_bit;
extern int trust_executable_bit, has_symlinks;
if (!has_symlinks && S_ISREG(mode) &&
ce && S_ISLNK(ntohl(ce->ce_mode)))
return ce->ce_mode;
if (!trust_executable_bit && S_ISREG(mode)) {
if (ce && S_ISREG(ntohl(ce->ce_mode)))
return ce->ce_mode;
@@ -127,6 +130,19 @@ extern unsigned int active_nr, active_alloc, active_cache_changed;
extern struct cache_tree *active_cache_tree;
extern int cache_errno;
enum object_type {
OBJ_BAD = -1,
OBJ_NONE = 0,
OBJ_COMMIT = 1,
OBJ_TREE = 2,
OBJ_BLOB = 3,
OBJ_TAG = 4,
/* 5 for future expansion */
OBJ_OFS_DELTA = 6,
OBJ_REF_DELTA = 7,
OBJ_MAX,
};
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
@@ -177,7 +193,7 @@ 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 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, const char *type);
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);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
@@ -203,6 +219,7 @@ extern int delete_ref(const char *, unsigned char *sha1);
/* Environment bits from configuration mechanism */
extern int use_legacy_headers;
extern int trust_executable_bit;
extern int has_symlinks;
extern int assume_unchanged;
extern int prefer_symlink_refs;
extern int log_all_ref_updates;
@@ -264,12 +281,12 @@ int safe_create_leading_directories(char *path);
char *enter_repo(char *path, int strict);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
extern int sha1_object_info(const unsigned char *, unsigned long *);
extern void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size);
extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, const char *, unsigned char *);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
@@ -286,18 +303,6 @@ extern int legacy_loose_object(unsigned char *);
extern int has_pack_file(const unsigned char *sha1);
extern int has_pack_index(const unsigned char *sha1);
enum object_type {
OBJ_NONE = 0,
OBJ_COMMIT = 1,
OBJ_TREE = 2,
OBJ_BLOB = 3,
OBJ_TAG = 4,
/* 5 for future expansion */
OBJ_OFS_DELTA = 6,
OBJ_REF_DELTA = 7,
OBJ_BAD,
};
extern signed char hexval_table[256];
static inline unsigned int hexval(unsigned int c)
{
@@ -327,7 +332,8 @@ extern void *read_object_with_reference(const unsigned char *sha1,
unsigned long *size,
unsigned char *sha1_ret);
const char *show_date(unsigned long time, int timezone, int relative);
enum date_mode { DATE_NORMAL = 0, DATE_RELATIVE, DATE_SHORT };
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);
void datestamp(char *buf, int bufsize);
@@ -423,9 +429,9 @@ extern struct packed_git *add_packed_git(char *, int, int);
extern int num_packed_objects(const struct packed_git *p);
extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
extern void *unpack_entry(struct packed_git *, unsigned long, char *, unsigned long *);
extern void *unpack_entry(struct packed_git *, unsigned long, enum object_type *, unsigned long *);
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
extern const char *packed_object_info_detail(struct packed_git *, unsigned long, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
/* Dumb servers support */
extern int update_server_info(int);

View File

@@ -92,14 +92,14 @@ struct sline {
static char *grab_blob(const unsigned char *sha1, unsigned long *size)
{
char *blob;
char type[20];
enum object_type type;
if (is_null_sha1(sha1)) {
/* deleted blob */
*size = 0;
return xcalloc(1, 1);
}
blob = read_sha1_file(sha1, type, size);
if (strcmp(type, blob_type))
blob = read_sha1_file(sha1, &type, size);
if (type != OBJ_BLOB)
die("object '%s' is not a blob!", sha1_to_hex(sha1));
return blob;
}
@@ -684,7 +684,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
goto deleted_file;
if (S_ISLNK(st.st_mode)) {
int len = st.st_size;
size_t len = st.st_size;
result_size = len;
result = xmalloc(len + 1);
if (result_size != readlink(elem->path, result, len)) {
@@ -697,10 +697,20 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
}
else if (0 <= (fd = open(elem->path, O_RDONLY)) &&
!fstat(fd, &st)) {
int len = st.st_size;
int sz = 0;
size_t len = st.st_size;
size_t sz = 0;
int is_file, i;
elem->mode = canon_mode(st.st_mode);
/* if symlinks don't work, assume symlink if all parents
* are symlinks
*/
is_file = has_symlinks;
for (i = 0; !is_file && i < num_parent; i++)
is_file = !S_ISLNK(elem->parent[i].mode);
if (!is_file)
elem->mode = canon_mode(S_IFLNK);
result_size = len;
result = xmalloc(len + 1);
while (sz < len) {

201
commit.c
View File

@@ -3,6 +3,7 @@
#include "commit.h"
#include "pkt-line.h"
#include "utf8.h"
#include "interpolate.h"
int save_commit_buffer = 1;
@@ -36,8 +37,11 @@ struct cmt_fmt_map {
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
{ "format:", 7, CMIT_FMT_USERFORMAT},
};
static char *user_format;
enum cmit_fmt get_commit_format(const char *arg)
{
int i;
@@ -46,6 +50,12 @@ enum cmit_fmt get_commit_format(const char *arg)
return CMIT_FMT_DEFAULT;
if (*arg == '=')
arg++;
if (!prefixcmp(arg, "format:")) {
if (user_format)
free(user_format);
user_format = xstrdup(arg + 7);
return CMIT_FMT_USERFORMAT;
}
for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
!strncmp(arg, cmt_fmts[i].n, strlen(arg)))
@@ -342,18 +352,18 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
int parse_commit(struct commit *item)
{
char type[20];
enum object_type type;
void *buffer;
unsigned long size;
int ret;
if (item->object.parsed)
return 0;
buffer = read_sha1_file(item->object.sha1, type, &size);
buffer = read_sha1_file(item->object.sha1, &type, &size);
if (!buffer)
return error("Could not read %s",
sha1_to_hex(item->object.sha1));
if (strcmp(type, commit_type)) {
if (type != OBJ_COMMIT) {
free(buffer);
return error("Object %s not a commit",
sha1_to_hex(item->object.sha1));
@@ -710,6 +720,188 @@ static char *logmsg_reencode(const struct commit *commit,
return out;
}
static char *xstrndup(const char *text, int len)
{
char *result = xmalloc(len + 1);
memcpy(result, text, len);
result[len] = '\0';
return result;
}
static void fill_person(struct interp *table, const char *msg, int len)
{
int start, end, tz = 0;
unsigned long date;
char *ep;
/* parse name */
for (end = 0; end < len && msg[end] != '<'; end++)
; /* do nothing */
start = end + 1;
while (end > 0 && isspace(msg[end - 1]))
end--;
table[0].value = xstrndup(msg, end);
if (start >= len)
return;
/* parse email */
for (end = start + 1; end < len && msg[end] != '>'; end++)
; /* do nothing */
if (end >= len)
return;
table[1].value = xstrndup(msg + start, end - start);
/* parse date */
for (start = end + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start >= len)
return;
date = strtoul(msg + start, &ep, 10);
if (msg + start == ep)
return;
table[5].value = xstrndup(msg + start, ep - msg + start);
/* parse tz */
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
; /* do nothing */
if (start + 1 < len) {
tz = strtoul(msg + start + 1, NULL, 10);
if (msg[start] == '-')
tz = -tz;
}
interp_set_entry(table, 2, show_date(date, tz, 0));
interp_set_entry(table, 3, show_rfc2822_date(date, tz));
interp_set_entry(table, 4, show_date(date, tz, 1));
}
static long format_commit_message(const struct commit *commit,
const char *msg, char *buf, unsigned long space)
{
struct interp table[] = {
{ "%H" }, /* commit hash */
{ "%h" }, /* abbreviated commit hash */
{ "%T" }, /* tree hash */
{ "%t" }, /* abbreviated tree hash */
{ "%P" }, /* parent hashes */
{ "%p" }, /* abbreviated parent hashes */
{ "%an" }, /* author name */
{ "%ae" }, /* author email */
{ "%ad" }, /* author date */
{ "%aD" }, /* author date, RFC2822 style */
{ "%ar" }, /* author date, relative */
{ "%at" }, /* author date, UNIX timestamp */
{ "%cn" }, /* committer name */
{ "%ce" }, /* committer email */
{ "%cd" }, /* committer date */
{ "%cD" }, /* committer date, RFC2822 style */
{ "%cr" }, /* committer date, relative */
{ "%ct" }, /* committer date, UNIX timestamp */
{ "%e" }, /* encoding */
{ "%s" }, /* subject */
{ "%b" }, /* body */
{ "%Cred" }, /* red */
{ "%Cgreen" }, /* green */
{ "%Cblue" }, /* blue */
{ "%Creset" }, /* reset color */
{ "%n" } /* newline */
};
enum interp_index {
IHASH = 0, IHASH_ABBREV,
ITREE, ITREE_ABBREV,
IPARENTS, IPARENTS_ABBREV,
IAUTHOR_NAME, IAUTHOR_EMAIL,
IAUTHOR_DATE, IAUTHOR_DATE_RFC2822, IAUTHOR_DATE_RELATIVE,
IAUTHOR_TIMESTAMP,
ICOMMITTER_NAME, ICOMMITTER_EMAIL,
ICOMMITTER_DATE, ICOMMITTER_DATE_RFC2822,
ICOMMITTER_DATE_RELATIVE, ICOMMITTER_TIMESTAMP,
IENCODING,
ISUBJECT,
IBODY,
IRED, IGREEN, IBLUE, IRESET_COLOR,
INEWLINE
};
struct commit_list *p;
char parents[1024];
int i;
enum { HEADER, SUBJECT, BODY } state;
if (INEWLINE + 1 != ARRAY_SIZE(table))
die("invalid interp table!");
/* these are independent of the commit */
interp_set_entry(table, IRED, "\033[31m");
interp_set_entry(table, IGREEN, "\033[32m");
interp_set_entry(table, IBLUE, "\033[34m");
interp_set_entry(table, IRESET_COLOR, "\033[m");
interp_set_entry(table, INEWLINE, "\n");
/* these depend on the commit */
if (!commit->object.parsed)
parse_object(commit->object.sha1);
interp_set_entry(table, IHASH, sha1_to_hex(commit->object.sha1));
interp_set_entry(table, IHASH_ABBREV,
find_unique_abbrev(commit->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, ITREE, sha1_to_hex(commit->tree->object.sha1));
interp_set_entry(table, ITREE_ABBREV,
find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
sha1_to_hex(p->item->object.sha1));
interp_set_entry(table, IPARENTS, parents);
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
find_unique_abbrev(p->item->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, IPARENTS_ABBREV, parents);
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
int eol;
for (eol = i; msg[eol] && msg[eol] != '\n'; eol++)
; /* do nothing */
if (state == SUBJECT) {
table[ISUBJECT].value = xstrndup(msg + i, eol - i);
i = eol;
}
if (i == eol) {
state++;
/* strip empty lines */
while (msg[eol + 1] == '\n')
eol++;
} else if (!prefixcmp(msg + i, "author "))
fill_person(table + IAUTHOR_NAME,
msg + i + 7, eol - i - 7);
else if (!prefixcmp(msg + i, "committer "))
fill_person(table + ICOMMITTER_NAME,
msg + i + 10, eol - i - 10);
else if (!prefixcmp(msg + i, "encoding "))
table[IENCODING].value = xstrndup(msg + i, eol - i);
i = eol;
}
if (msg[i])
table[IBODY].value = xstrdup(msg + i);
for (i = 0; i < ARRAY_SIZE(table); i++)
if (!table[i].value)
interp_set_entry(table, i, "<unknown>");
interpolate(buf, space, user_format, table, ARRAY_SIZE(table));
interp_clear_table(table, ARRAY_SIZE(table));
return strlen(buf);
}
unsigned long pretty_print_commit(enum cmit_fmt fmt,
const struct commit *commit,
unsigned long len,
@@ -727,6 +919,9 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
char *reencoded;
char *encoding;
if (fmt == CMIT_FMT_USERFORMAT)
return format_commit_message(commit, msg, buf, space);
encoding = (git_log_output_encoding
? git_log_output_encoding
: git_commit_encoding);

View File

@@ -47,6 +47,7 @@ enum cmit_fmt {
CMIT_FMT_FULLER,
CMIT_FMT_ONELINE,
CMIT_FMT_EMAIL,
CMIT_FMT_USERFORMAT,
CMIT_FMT_UNSPECIFIED,
};

View File

@@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.symlinks")) {
has_symlinks = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
return 0;

View File

@@ -41,7 +41,7 @@ $fileview->set_rules_hint(1);
$fileview->signal_connect (row_activated => sub {
my ($sl, $path, $column) = @_;
my $row_ref = $sl->get_row_data_from_path ($path);
system("blameview @$row_ref[0] $fn &");
system("blameview @$row_ref[0]~1 $fn &");
});
my $commitwindow = Gtk2::ScrolledWindow->new();

View File

@@ -2,7 +2,7 @@
EMACS = emacs
ELC = git.elc vc-git.elc
ELC = git.elc vc-git.elc git-blame.elc
INSTALL ?= install
INSTALL_ELC = $(INSTALL) -m 644
prefix ?= $(HOME)
@@ -15,6 +15,6 @@ install: all
$(INSTALL_ELC) $(ELC) $(emacsdir)
%.elc: %.el
$(EMACS) --batch --eval '(byte-compile-file "$<")'
$(EMACS) -batch -f batch-byte-compile $<
clean:; rm -f $(ELC)

View File

@@ -75,10 +75,11 @@ then to `add-log-mailing-address' and then to `user-mail-address'."
:type '(choice (const :tag "Default" nil)
(string :tag "Email")))
(defcustom git-commits-coding-system 'utf-8
(defcustom git-commits-coding-system nil
"Default coding system for the log message of git commits."
:group 'git
:type 'coding-system)
:type '(choice (const :tag "From repository config" nil)
(coding-system)))
(defcustom git-append-signed-off-by nil
"Whether to append a Signed-off-by line to the commit message before editing."
@@ -236,6 +237,15 @@ and returns the process output as a string."
(and (fboundp 'user-mail-address) (user-mail-address))
(and (boundp 'user-mail-address) user-mail-address)))
(defun git-get-commits-coding-system ()
"Return the coding system to use for commits."
(let ((repo-config (git-config "i18n.commitencoding")))
(or git-commits-coding-system
(and repo-config
(fboundp 'locale-charset-to-coding-system)
(locale-charset-to-coding-system repo-config))
'utf-8)))
(defun git-escape-file-name (name)
"Escape a file name if necessary."
(if (string-match "[\n\t\"\\]" name)
@@ -327,7 +337,7 @@ and returns the process output as a string."
"Call git-commit-tree with buffer as input and return the resulting commit SHA1."
(let ((author-name (git-get-committer-name))
(author-email (git-get-committer-email))
author-date log-start log-end args)
author-date log-start log-end args coding-system-for-write)
(when head
(push "-p" args)
(push head args))
@@ -350,12 +360,12 @@ and returns the process output as a string."
(push "-p" args)
(push (match-string 1) args))))
(setq log-start (point-min)))
(setq log-end (point-max)))
(setq log-end (point-max))
(setq coding-system-for-write buffer-file-coding-system))
(git-get-string-sha1
(with-output-to-string
(with-current-buffer standard-output
(let ((coding-system-for-write git-commits-coding-system)
(env `(("GIT_AUTHOR_NAME" . ,author-name)
(let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
("GIT_AUTHOR_EMAIL" . ,author-email)
("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
@@ -888,6 +898,7 @@ and returns the process output as a string."
(let ((buffer (get-buffer-create "*git-commit*"))
(merge-heads (git-get-merge-heads))
(dir default-directory)
(coding-system (git-get-commits-coding-system))
(sign-off git-append-signed-off-by))
(with-current-buffer buffer
(when (eq 0 (buffer-size))
@@ -912,6 +923,7 @@ and returns the process output as a string."
(git-get-committer-name) (git-get-committer-email)))))))
(log-edit #'git-do-commit nil #'git-log-edit-files buffer)
(setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
(setq buffer-file-coding-system coding-system)
(re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t)))
(defun git-find-file ()

View File

@@ -284,27 +284,27 @@ static void convert_commit(void *buffer, unsigned long size, unsigned char *resu
static struct entry * convert_entry(unsigned char *sha1)
{
struct entry *entry = lookup_entry(sha1);
char type[20];
enum object_type type;
void *buffer, *data;
unsigned long size;
if (entry->converted)
return entry;
data = read_sha1_file(sha1, type, &size);
data = read_sha1_file(sha1, &type, &size);
if (!data)
die("unable to read object %s", sha1_to_hex(sha1));
buffer = xmalloc(size);
memcpy(buffer, data, size);
if (!strcmp(type, blob_type)) {
if (type == OBJ_BLOB) {
write_sha1_file(buffer, size, blob_type, entry->new_sha1);
} else if (!strcmp(type, tree_type))
} else if (type == OBJ_TREE)
convert_tree(buffer, size, entry->new_sha1);
else if (!strcmp(type, commit_type))
else if (type == OBJ_COMMIT)
convert_commit(buffer, size, entry->new_sha1);
else
die("unknown object type '%s' in %s", type, sha1_to_hex(sha1));
die("unknown object type %d in %s", type, sha1_to_hex(sha1));
entry->converted = 1;
free(buffer);
free(data);

20
date.c
View File

@@ -55,12 +55,12 @@ static struct tm *time_to_tm(unsigned long time, int tz)
return gmtime(&t);
}
const char *show_date(unsigned long time, int tz, int relative)
const char *show_date(unsigned long time, int tz, enum date_mode mode)
{
struct tm *tm;
static char timebuf[200];
if (relative) {
if (mode == DATE_RELATIVE) {
unsigned long diff;
struct timeval now;
gettimeofday(&now, NULL);
@@ -105,12 +105,16 @@ const char *show_date(unsigned long time, int tz, int relative)
tm = time_to_tm(time, tz);
if (!tm)
return NULL;
sprintf(timebuf, "%.3s %.3s %d %02d:%02d:%02d %d %+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);
if (mode == DATE_SHORT)
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",
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);
return timebuf;
}

View File

@@ -8,11 +8,293 @@
#include "diffcore.h"
#include "revision.h"
#include "cache-tree.h"
#include "path-list.h"
/*
* diff-files
*/
static int read_directory(const char *path, struct path_list *list)
{
DIR *dir;
struct dirent *e;
if (!(dir = opendir(path)))
return error("Could not open directory %s", path);
while ((e = readdir(dir)))
if (strcmp(".", e->d_name) && strcmp("..", e->d_name))
path_list_insert(xstrdup(e->d_name), list);
closedir(dir);
return 0;
}
static int queue_diff(struct diff_options *o,
const char *name1, const char *name2)
{
struct stat st;
int mode1 = 0, mode2 = 0;
if (name1) {
if (stat(name1, &st))
return error("Could not access '%s'", name1);
mode1 = st.st_mode;
}
if (name2) {
if (stat(name2, &st))
return error("Could not access '%s'", name2);
mode2 = st.st_mode;
}
if (mode1 && mode2 && S_ISDIR(mode1) != S_ISDIR(mode2))
return error("file/directory conflict: %s, %s", name1, name2);
if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
char buffer1[PATH_MAX], buffer2[PATH_MAX];
struct path_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
int len1 = 0, len2 = 0, i1, i2, ret = 0;
if (name1 && read_directory(name1, &p1))
return -1;
if (name2 && read_directory(name2, &p2)) {
path_list_clear(&p1, 0);
return -1;
}
if (name1) {
len1 = strlen(name1);
if (len1 > 0 && name1[len1 - 1] == '/')
len1--;
memcpy(buffer1, name1, len1);
buffer1[len1++] = '/';
}
if (name2) {
len2 = strlen(name2);
if (len2 > 0 && name2[len2 - 1] == '/')
len2--;
memcpy(buffer2, name2, len2);
buffer2[len2++] = '/';
}
for (i1 = i2 = 0; !ret && (i1 < p1.nr || i2 < p2.nr); ) {
const char *n1, *n2;
int comp;
if (i1 == p1.nr)
comp = 1;
else if (i2 == p2.nr)
comp = -1;
else
comp = strcmp(p1.items[i1].path,
p2.items[i2].path);
if (comp > 0)
n1 = NULL;
else {
n1 = buffer1;
strncpy(buffer1 + len1, p1.items[i1++].path,
PATH_MAX - len1);
}
if (comp < 0)
n2 = NULL;
else {
n2 = buffer2;
strncpy(buffer2 + len2, p2.items[i2++].path,
PATH_MAX - len2);
}
ret = queue_diff(o, n1, n2);
}
path_list_clear(&p1, 0);
path_list_clear(&p2, 0);
return ret;
} else {
struct diff_filespec *d1, *d2;
if (o->reverse_diff) {
unsigned tmp;
const char *tmp_c;
tmp = mode1; mode1 = mode2; mode2 = tmp;
tmp_c = name1; name1 = name2; name2 = tmp_c;
}
if (!name1)
name1 = "/dev/null";
if (!name2)
name2 = "/dev/null";
d1 = alloc_filespec(name1);
d2 = alloc_filespec(name2);
fill_filespec(d1, null_sha1, mode1);
fill_filespec(d2, null_sha1, mode2);
diff_queue(&diff_queued_diff, d1, d2);
return 0;
}
}
static int is_in_index(const char *path)
{
int len = strlen(path);
int pos = cache_name_pos(path, len);
char c;
if (pos < 0)
return 0;
if (strncmp(active_cache[pos]->name, path, len))
return 0;
c = active_cache[pos]->name[len];
return c == '\0' || c == '/';
}
static int handle_diff_files_args(struct rev_info *revs,
int argc, const char **argv, int *silent)
{
*silent = 0;
/* revs->max_count == -2 means --no-index */
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "--base"))
revs->max_count = 1;
else if (!strcmp(argv[1], "--ours"))
revs->max_count = 2;
else if (!strcmp(argv[1], "--theirs"))
revs->max_count = 3;
else if (!strcmp(argv[1], "-n") ||
!strcmp(argv[1], "--no-index"))
revs->max_count = -2;
else if (!strcmp(argv[1], "-q"))
*silent = 1;
else
return error("invalid option: %s", argv[1]);
argv++; argc--;
}
if (revs->max_count == -1 && revs->diffopt.nr_paths == 2) {
/*
* If two files are specified, and at least one is untracked,
* default to no-index.
*/
read_cache();
if (!is_in_index(revs->diffopt.paths[0]) ||
!is_in_index(revs->diffopt.paths[1]))
revs->max_count = -2;
}
/*
* Make sure there are NO revision (i.e. pending object) parameter,
* rev.max_count is reasonable (0 <= n <= 3),
* there is no other revision filtering parameters.
*/
if (revs->pending.nr || revs->max_count > 3 ||
revs->min_age != -1 || revs->max_age != -1)
return error("no revision allowed with diff-files");
if (revs->max_count == -1 &&
(revs->diffopt.output_format & DIFF_FORMAT_PATCH))
revs->combine_merges = revs->dense_combined_merges = 1;
return 0;
}
static int is_outside_repo(const char *path, int nongit, const char *prefix)
{
int i;
if (nongit || !strcmp(path, "-") || path[0] == '/')
return 1;
if (prefixcmp(path, "../"))
return 0;
if (!prefix)
return 1;
for (i = strlen(prefix); !prefixcmp(path, "../"); ) {
while (i > 0 && prefix[i - 1] != '/')
i--;
if (--i < 0)
return 1;
path += 3;
}
return 0;
}
int setup_diff_no_index(struct rev_info *revs,
int argc, const char ** argv, int nongit, const char *prefix)
{
int i;
for (i = 1; i < argc; i++)
if (argv[i][0] != '-')
break;
else if (!strcmp(argv[i], "--")) {
i++;
break;
} else if (i < argc - 3 && !strcmp(argv[i], "--no-index")) {
i = argc - 3;
break;
}
if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
!is_outside_repo(argv[i], nongit, prefix)))
return -1;
diff_setup(&revs->diffopt);
for (i = 1; i < argc - 2; )
if (!strcmp(argv[i], "--no-index"))
i++;
else {
int j = diff_opt_parse(&revs->diffopt,
argv + i, argc - i);
if (!j)
die("invalid diff option/value: %s", argv[i]);
i += j;
}
if (prefix) {
int len = strlen(prefix);
revs->diffopt.paths = xcalloc(2, sizeof(char*));
for (i = 0; i < 2; i++) {
const char *p;
p = prefix_filename(prefix, len, argv[argc - 2 + i]);
revs->diffopt.paths[i] = xstrdup(p);
}
}
else
revs->diffopt.paths = argv + argc - 2;
revs->diffopt.nr_paths = 2;
revs->max_count = -2;
return 0;
}
int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
{
int silent_on_removed;
if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
return -1;
if (revs->max_count == -2) {
if (revs->diffopt.nr_paths != 2)
return error("need two files/directories with --no-index");
if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
revs->diffopt.paths[1]))
return -1;
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
/*
* The return code for --no-index imitates diff(1):
* 0 = no changes, 1 = changes, else error
*/
return revs->diffopt.found_changes;
}
if (read_cache() < 0) {
perror("read_cache");
return -1;
}
return run_diff_files(revs, silent_on_removed);
}
int run_diff_files(struct rev_info *revs, int silent_on_removed)
{
int entries, i;
@@ -20,11 +302,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
if (diff_unmerged_stage < 0)
diff_unmerged_stage = 2;
entries = read_cache();
if (entries < 0) {
perror("read_cache");
return -1;
}
entries = active_nr;
for (i = 0; i < entries; i++) {
struct stat st;
unsigned int oldmode, newmode;
@@ -134,6 +412,9 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
S_ISREG(newmode) && S_ISREG(oldmode) &&
((newmode ^ oldmode) == 0111))
newmode = oldmode;
else if (!has_symlinks &&
S_ISREG(newmode) && S_ISLNK(oldmode))
newmode = oldmode;
diff_change(&revs->diffopt, oldmode, newmode,
ce->sha1, (changed ? null_sha1 : ce->sha1),
ce->name, NULL);
@@ -362,10 +643,6 @@ int run_diff_index(struct rev_info *revs, int cached)
if (!revs->ignore_merges)
match_missing = 1;
if (read_cache() < 0) {
perror("read_cache");
return -1;
}
mark_merge_entries();
ent = revs->pending.objects[0].item;

16
diff.c
View File

@@ -383,6 +383,7 @@ struct emit_callback {
int nparents, color_diff;
const char **label_path;
struct diff_words_data *diff_words;
int *found_changesp;
};
static void free_diff_words_data(struct emit_callback *ecbdata)
@@ -502,6 +503,8 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
*(ecbdata->found_changesp) = 1;
if (ecbdata->label_path[0]) {
const char *name_a_tab, *name_b_tab;
@@ -1099,6 +1102,7 @@ static void builtin_diff(const char *name_a,
if (complete_rewrite) {
emit_rewrite_diff(name_a, name_b, one, two,
o->color_diff);
o->found_changes = 1;
goto free_ab_and_return;
}
}
@@ -1116,6 +1120,7 @@ static void builtin_diff(const char *name_a,
else
printf("Binary files %s and %s differ\n",
lbl[0], lbl[1]);
o->found_changes = 1;
}
else {
/* Crazy xdl interfaces.. */
@@ -1128,6 +1133,7 @@ static void builtin_diff(const char *name_a,
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.label_path = lbl;
ecbdata.color_diff = o->color_diff;
ecbdata.found_changesp = &o->found_changes;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -1431,7 +1437,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
}
}
else {
char type[20];
enum object_type type;
struct sha1_size_cache *e;
if (size_only) {
@@ -1440,11 +1446,12 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
s->size = e->size;
return 0;
}
if (!sha1_object_info(s->sha1, type, &s->size))
type = sha1_object_info(s->sha1, &s->size);
if (type < 0)
locate_size_cache(s->sha1, 0, s->size);
}
else {
s->data = read_sha1_file(s->sha1, type, &s->size);
s->data = read_sha1_file(s->sha1, &type, &s->size);
s->should_free = 1;
}
}
@@ -2450,7 +2457,8 @@ static void diff_resolve_rename_copy(void)
p->status = DIFF_STATUS_RENAMED;
}
else if (hashcmp(p->one->sha1, p->two->sha1) ||
p->one->mode != p->two->mode)
p->one->mode != p->two->mode ||
is_null_sha1(p->one->sha1))
p->status = DIFF_STATUS_MODIFIED;
else {
/* This is a "no-change" entry and should not

6
diff.h
View File

@@ -75,6 +75,9 @@ struct diff_options {
int stat_width;
int stat_name_width;
/* this is set by diffcore for DIFF_FORMAT_PATCH */
int found_changes;
int nr_paths;
const char **paths;
int *pathlens;
@@ -219,6 +222,9 @@ extern void diff_flush(struct diff_options*);
extern const char *diff_unique_abbrev(const unsigned char *, int);
extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
extern int setup_diff_no_index(struct rev_info *revs,
int argc, const char ** argv, int nongit, const char *prefix);
extern int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv);
extern int run_diff_index(struct rev_info *revs, int cached);

15
entry.c
View File

@@ -68,10 +68,10 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
void *new;
unsigned long size;
long wrote;
char type[20];
enum object_type type;
new = read_sha1_file(ce->sha1, type, &size);
if (!new || strcmp(type, blob_type)) {
new = read_sha1_file(ce->sha1, &type, &size);
if (!new || type != OBJ_BLOB) {
if (new)
free(new);
return error("git-checkout-index: unable to read sha1 file of %s (%s)",
@@ -111,9 +111,12 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
return error("git-checkout-index: unable to write file %s", path);
break;
case S_IFLNK:
if (to_tempfile) {
strcpy(path, ".merge_link_XXXXXX");
fd = mkstemp(path);
if (to_tempfile || !has_symlinks) {
if (to_tempfile) {
strcpy(path, ".merge_link_XXXXXX");
fd = mkstemp(path);
} else
fd = create_file(path, 0666);
if (fd < 0) {
free(new);
return error("git-checkout-index: unable to create "

View File

@@ -13,6 +13,7 @@ char git_default_email[MAX_GITNAME];
char git_default_name[MAX_GITNAME];
int use_legacy_headers = 1;
int trust_executable_bit = 1;
int has_symlinks = 1;
int assume_unchanged;
int prefer_symlink_refs;
int is_bare_repository_cfg = -1; /* unspecified */

View File

@@ -891,7 +891,7 @@ static int store_object(
SHA_CTX c;
z_stream s;
hdrlen = sprintf((char*)hdr,"%s %lu", type_names[type],
hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
(unsigned long)datlen) + 1;
SHA1_Init(&c);
SHA1_Update(&c, hdr, hdrlen);
@@ -1008,11 +1008,11 @@ static void *gfi_unpack_entry(
struct object_entry *oe,
unsigned long *sizep)
{
static char type[20];
enum object_type type;
struct packed_git *p = all_packs[oe->pack_id];
if (p == pack_data)
p->pack_size = pack_size + 20;
return unpack_entry(p, oe->offset, type, sizep);
return unpack_entry(p, oe->offset, &type, sizep);
}
static const char *get_mode(const char *str, uint16_t *modep)
@@ -1049,9 +1049,9 @@ static void load_tree(struct tree_entry *root)
t->delta_depth = 0;
buf = gfi_unpack_entry(myoe, &size);
} else {
char type[20];
buf = read_sha1_file(sha1, type, &size);
if (!buf || strcmp(type, tree_type))
enum object_type type;
buf = read_sha1_file(sha1, &type, &size);
if (!buf || type != OBJ_TREE)
die("Can't load tree %s", sha1_to_hex(sha1));
}
@@ -1573,7 +1573,6 @@ static void file_change_m(struct branch *b)
struct object_entry *oe = oe;
unsigned char sha1[20];
uint16_t mode, inline_data = 0;
char type[20];
p = get_mode(p, &mode);
if (!p)
@@ -1626,13 +1625,14 @@ static void file_change_m(struct branch *b)
} else if (oe) {
if (oe->type != OBJ_BLOB)
die("Not a blob (actually a %s): %s",
command_buf.buf, type_names[oe->type]);
command_buf.buf, typename(oe->type));
} else {
if (sha1_object_info(sha1, type, NULL))
enum object_type type = sha1_object_info(sha1, NULL);
if (type < 0)
die("Blob not found: %s", command_buf.buf);
if (strcmp(blob_type, type))
if (type != OBJ_BLOB)
die("Not a blob (actually a %s): %s",
command_buf.buf, type);
typename(type), command_buf.buf);
}
tree_content_set(&b->branch_tree, p, sha1, S_IFREG | mode);
@@ -1711,7 +1711,7 @@ static void cmd_from(struct branch *b)
char *buf;
buf = read_object_with_reference(b->sha1,
type_names[OBJ_COMMIT], &size, b->sha1);
commit_type, &size, b->sha1);
if (!buf || size < 46)
die("Not a valid commit: %s", from);
if (memcmp("tree ", buf, 5)
@@ -1895,7 +1895,7 @@ static void cmd_new_tag(void)
char *buf;
buf = read_object_with_reference(sha1,
type_names[OBJ_COMMIT], &size, sha1);
commit_type, &size, sha1);
if (!buf || size < 46)
die("Not a valid commit: %s", from);
free(buf);
@@ -1916,7 +1916,7 @@ static void cmd_new_tag(void)
size_dbuf(&new_data, 67+strlen(t->name)+strlen(tagger)+msglen);
sp = new_data.buffer;
sp += sprintf(sp, "object %s\n", sha1_to_hex(sha1));
sp += sprintf(sp, "type %s\n", type_names[OBJ_COMMIT]);
sp += sprintf(sp, "type %s\n", commit_type);
sp += sprintf(sp, "tag %s\n", t->name);
sp += sprintf(sp, "tagger %s\n", tagger);
*sp++ = '\n';

View File

@@ -15,8 +15,9 @@ static int quiet;
static int verbose;
static int fetch_all;
static int depth;
static int no_progress;
static const char fetch_pack_usage[] =
"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [-v] [<host>:]<directory> [<refs>...]";
"git-fetch-pack [--all] [--quiet|-q] [--keep|-k] [--thin] [--upload-pack=<git-upload-pack>] [--depth=<n>] [--no-progress] [-v] [<host>:]<directory> [<refs>...]";
static const char *uploadpack = "git-upload-pack";
#define COMPLETE (1U << 0)
@@ -173,12 +174,13 @@ static int find_common(int fd[2], unsigned char *result_sha1,
}
if (!fetching)
packet_write(fd[1], "want %s%s%s%s%s%s\n",
packet_write(fd[1], "want %s%s%s%s%s%s%s\n",
sha1_to_hex(remote),
(multi_ack ? " multi_ack" : ""),
(use_sideband == 2 ? " side-band-64k" : ""),
(use_sideband == 1 ? " side-band" : ""),
(use_thin_pack ? " thin-pack" : ""),
(no_progress ? " no-progress" : ""),
" ofs-delta");
else
packet_write(fd[1], "want %s\n", sha1_to_hex(remote));
@@ -521,7 +523,7 @@ static int get_pack(int xd[2])
if (do_keep) {
*av++ = "index-pack";
*av++ = "--stdin";
if (!quiet)
if (!quiet && !no_progress)
*av++ = "-v";
if (use_thin_pack)
*av++ = "--fix-thin";
@@ -712,6 +714,10 @@ int main(int argc, char **argv)
st.st_mtime = 0;
continue;
}
if (!strcmp("--no-progress", arg)) {
no_progress = 1;
continue;
}
usage(fetch_pack_usage);
}
dest = arg;

View File

@@ -553,7 +553,7 @@ foreach my $ps (@psets) {
my $pid = open2(*READER, *WRITER,'git-commit-tree',$tree,@par)
or die $!;
print WRITER $ps->{summary},"\n";
print WRITER $ps->{summary},"\n\n";
print WRITER $ps->{message},"\n";
# make it easy to backtrack and figure out which Arch revision this was:
@@ -755,7 +755,8 @@ sub parselog {
$ps->{tag} = $1;
$key = undef;
} elsif (/^Summary:\s*(.*)$/ ) {
# summary can be multiline as long as it has a leading space
# summary can be multiline as long as it has a leading space.
# we squeeze it onto a single line, though.
$ps->{summary} = [ $1 ];
$key = 'summary';
} elsif (/^Creator: (.*)\s*<([^\>]+)>/) {
@@ -787,8 +788,18 @@ sub parselog {
}
}
# post-processing:
$ps->{summary} = join("\n",@{$ps->{summary}})."\n";
# drop leading empty lines from the log message
while (@$log && $log->[0] eq '') {
shift @$log;
}
if (exists $ps->{summary} && @{$ps->{summary}}) {
$ps->{summary} = join(' ', @{$ps->{summary}});
}
elsif (@$log == 0) {
$ps->{summary} = 'empty commit message';
} else {
$ps->{summary} = $log->[0] . '...';
}
$ps->{message} = join("\n",@$log);
# skip Arch control files, unescape pika-escaped files

View File

@@ -79,6 +79,8 @@ origin=
origin_override=
use_separate_remote=t
depth=
no_progress=
test -t 1 || no_progress=--no-progress
while
case "$#,$1" in
0,*) break ;;
@@ -290,8 +292,8 @@ yes,yes)
;;
*)
case "$upload_pack" in
'') git-fetch-pack --all -k $quiet $depth "$repo" ;;
*) git-fetch-pack --all -k $quiet "$upload_pack" $depth "$repo" ;;
'') git-fetch-pack --all -k $quiet $depth $no_progress "$repo";;
*) git-fetch-pack --all -k $quiet "$upload_pack" $depth $no_progress "$repo" ;;
esac >"$GIT_DIR/CLONE_HEAD" ||
die "fetch-pack from '$repo' failed."
;;
@@ -393,7 +395,7 @@ then
case "$no_checkout" in
'')
test "z$quiet" = z && v=-v || v=
test "z$quiet" = z -a "z$no_progress" = z && v=-v || v=
git-read-tree -m -u $v HEAD HEAD
esac
fi

View File

@@ -13,10 +13,10 @@ git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
case "$0" in
*status)
status_only=t
unmerged_ok_if_status=--unmerged ;;
;;
*commit)
status_only=
unmerged_ok_if_status= ;;
;;
esac
refuse_partial () {
@@ -393,16 +393,17 @@ else
USE_INDEX="$THIS_INDEX"
fi
GIT_INDEX_FILE="$USE_INDEX" \
git-update-index -q $unmerged_ok_if_status --refresh || exit
################################################################
# If the request is status, just show it and exit.
case "$0" in
*status)
case "$status_only" in
t)
# This will silently fail in a read-only repository, which is
# what we want.
GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --unmerged --refresh
run_status
exit $?
;;
'')
GIT_INDEX_FILE="$USE_INDEX" git-update-index -q --refresh || exit
;;
esac
################################################################

View File

@@ -51,10 +51,16 @@
#include <netdb.h>
#include <pwd.h>
#include <inttypes.h>
#if defined(__CYGWIN__)
#undef _XOPEN_SOURCE
#include <grp.h>
#define _XOPEN_SOURCE 600
#else
#undef _ALL_SOURCE /* AIX 5.3L defines a struct list with _ALL_SOURCE. */
#include <grp.h>
#define _ALL_SOURCE 1
#endif
#endif
#ifndef NO_ICONV
#include <iconv.h>

View File

@@ -248,13 +248,14 @@ if ($opt_c) {
die "Exiting: The commit did not succeed";
}
print "Committed successfully to CVS\n";
# clean up
unlink(".msg");
} else {
print "Ready for you to commit, just run:\n\n $cmd\n";
}
# clean up
unlink(".cvsexportcommit.diff");
unlink(".msg");
sub usage {
print STDERR <<END;

View File

@@ -374,7 +374,8 @@ sub req_add
print "Checked-in $dirpart\n";
print "$filename\n";
print "/$filepart/0///\n";
my $kopts = kopts_from_path($filepart);
print "/$filepart/0//$kopts/\n";
$addcount++;
}
@@ -455,7 +456,8 @@ sub req_remove
print "Checked-in $dirpart\n";
print "$filename\n";
print "/$filepart/-1.$wrev///\n";
my $kopts = kopts_from_path($filepart);
print "/$filepart/-1.$wrev//$kopts/\n";
$rmcount++;
}
@@ -726,7 +728,8 @@ sub req_co
print $state->{CVSROOT} . "/$module/" . ( defined ( $git->{dir} ) and $git->{dir} ne "./" ? $git->{dir} . "/" : "" ) . "$git->{name}\n";
# this is an "entries" line
print "/$git->{name}/1.$git->{revision}///\n";
my $kopts = kopts_from_path($git->{name});
print "/$git->{name}/1.$git->{revision}//$kopts/\n";
# permissions
print "u=$git->{mode},g=$git->{mode},o=$git->{mode}\n";
@@ -917,8 +920,9 @@ sub req_update
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
# this is an "entries" line
$log->debug("/$filepart/1.$meta->{revision}///");
print "/$filepart/1.$meta->{revision}///\n";
my $kopts = kopts_from_path($filepart);
$log->debug("/$filepart/1.$meta->{revision}//$kopts/");
print "/$filepart/1.$meta->{revision}//$kopts/\n";
# permissions
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
@@ -961,8 +965,9 @@ sub req_update
print "Update-existing $dirpart\n";
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
$log->debug("/$filepart/1.$meta->{revision}///");
print "/$filepart/1.$meta->{revision}///\n";
my $kopts = kopts_from_path($filepart);
$log->debug("/$filepart/1.$meta->{revision}//$kopts/");
print "/$filepart/1.$meta->{revision}//$kopts/\n";
}
}
elsif ( $return == 1 )
@@ -975,7 +980,8 @@ sub req_update
{
print "Update-existing $dirpart\n";
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
print "/$filepart/1.$meta->{revision}/+//\n";
my $kopts = kopts_from_path($filepart);
print "/$filepart/1.$meta->{revision}/+/$kopts/\n";
}
}
else
@@ -1031,36 +1037,35 @@ sub req_ci
exit;
}
my $lockfile = "$state->{CVSROOT}/refs/heads/$state->{module}.lock";
unless ( sysopen(LOCKFILE,$lockfile,O_EXCL|O_CREAT|O_WRONLY) )
{
$log->warn("lockfile '$lockfile' already exists, please try again");
print "error 1 Lock file '$lockfile' already exists, please try again\n";
exit;
}
# Grab a handle to the SQLite db and do any necessary updates
my $updater = GITCVS::updater->new($state->{CVSROOT}, $state->{module}, $log);
$updater->update();
my $tmpdir = tempdir ( DIR => $TEMP_DIR );
my ( undef, $file_index ) = tempfile ( DIR => $TEMP_DIR, OPEN => 0 );
$log->info("Lock successful, basing commit on '$tmpdir', index file is '$file_index'");
$log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
$ENV{GIT_INDEX_FILE} = $file_index;
# Remember where the head was at the beginning.
my $parenthash = `git show-ref -s refs/heads/$state->{module}`;
chomp $parenthash;
if ($parenthash !~ /^[0-9a-f]{40}$/) {
print "error 1 pserver cannot find the current HEAD of module";
exit;
}
chdir $tmpdir;
# populate the temporary index based
system("git-read-tree", $state->{module});
system("git-read-tree", $parenthash);
unless ($? == 0)
{
die "Error running git-read-tree $state->{module} $file_index $!";
}
$log->info("Created index '$file_index' with for head $state->{module} - exit status $?");
my @committedfiles = ();
# foreach file specified on the command line ...
@@ -1095,8 +1100,6 @@ sub req_ci
{
# fail everything if an up to date check fails
print "error 1 Up to date check failed for $filename\n";
close LOCKFILE;
unlink($lockfile);
chdir "/";
exit;
}
@@ -1139,16 +1142,12 @@ sub req_ci
{
print "E No files to commit\n";
print "ok\n";
close LOCKFILE;
unlink($lockfile);
chdir "/";
return;
}
my $treehash = `git-write-tree`;
my $parenthash = `cat $ENV{GIT_DIR}refs/heads/$state->{module}`;
chomp $treehash;
chomp $parenthash;
$log->debug("Treehash : $treehash, Parenthash : $parenthash");
@@ -1159,14 +1158,13 @@ sub req_ci
close $msg_fh;
my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
chomp($commithash);
$log->info("Commit hash : $commithash");
unless ( $commithash =~ /[a-zA-Z0-9]{40}/ )
{
$log->warn("Commit failed (Invalid commit hash)");
print "error 1 Commit failed (unknown reason)\n";
close LOCKFILE;
unlink($lockfile);
chdir "/";
exit;
}
@@ -1179,14 +1177,17 @@ sub req_ci
{
$log->warn("Commit failed (update hook declined to update ref)");
print "error 1 Commit failed (update hook declined)\n";
close LOCKFILE;
unlink($lockfile);
chdir "/";
exit;
}
}
print LOCKFILE $commithash;
if (system(qw(git update-ref -m), "cvsserver ci",
"refs/heads/$state->{module}", $commithash, $parenthash)) {
$log->warn("update-ref for $state->{module} failed.");
print "error 1 Cannot commit -- update first\n";
exit;
}
$updater->update();
@@ -1211,16 +1212,12 @@ sub req_ci
} else {
print "Checked-in $dirpart\n";
print "$filename\n";
print "/$filepart/1.$meta->{revision}///\n";
my $kopts = kopts_from_path($filepart);
print "/$filepart/1.$meta->{revision}//$kopts/\n";
}
}
close LOCKFILE;
my $reffile = "$ENV{GIT_DIR}refs/heads/$state->{module}";
unlink($reffile);
rename($lockfile, $reffile);
chdir "/";
print "ok\n";
}
@@ -1897,6 +1894,28 @@ sub filecleanup
return $filename;
}
# Given a path, this function returns a string containing the kopts
# that should go into that path's Entries line. For example, a binary
# file should get -kb.
sub kopts_from_path
{
my ($path) = @_;
# Once it exists, the git attributes system should be used to look up
# what attributes apply to this path.
# Until then, take the setting from the config file
unless ( defined ( $cfg->{gitcvs}{allbinary} ) and $cfg->{gitcvs}{allbinary} =~ /^\s*(1|true|yes)\s*$/i )
{
# Return "" to give no special treatment to any path
return "";
} else {
# Alternatively, to have all files treated as if they are binary (which
# is more like git itself), always return the "-kb" option
return "-kb";
}
}
package GITCVS::log;
####

View File

@@ -24,6 +24,8 @@ update_head_ok=
exec=
keep=
shallow_depth=
no_progress=
test -t 1 || no_progress=--no-progress
while case "$#" in 0) break ;; esac
do
case "$1" in
@@ -386,8 +388,16 @@ fetch_main () {
( : subshell because we muck with IFS
IFS=" $LF"
(
git-fetch-pack --thin $exec $keep $shallow_depth "$remote" $rref ||
if test -f "$remote" ; then
test -n "$shallow_depth" &&
die "shallow clone with bundle is not supported"
git-bundle unbundle "$remote" $rref ||
echo failed "$remote"
else
git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
"$remote" $rref ||
echo failed "$remote"
fi
) |
(
trap '

View File

@@ -4,7 +4,7 @@
#
# Cleanup unreachable files and optimize the repository.
USAGE='git-gc [--prune]'
USAGE='[--prune]'
SUBDIRECTORY_OK=Yes
. git-sh-setup

View File

@@ -89,8 +89,13 @@ rsync://* )
;;
* )
git-peek-remote $exec "$peek_repo" ||
if test -f "$peek_repo" ; then
git bundle list-heads "$peek_repo" ||
echo "failed slurping"
else
git-peek-remote $exec "$peek_repo" ||
echo "failed slurping"
fi
;;
esac |
sort -t ' ' -k 2 |

View File

@@ -339,7 +339,7 @@ f,*)
git-update-index --refresh 2>/dev/null
new_head=$(git-rev-parse --verify "$1^0") &&
merge_local_changes $head $new_head &&
finish "$new_head" "Fast forward"
finish "$new_head" "Fast forward" || exit
dropsave
exit 0
;;

View File

@@ -34,6 +34,53 @@ sub readline {
}
package main;
sub usage {
print <<EOT;
git-send-email [options] <file | directory>...
Options:
--from Specify the "From:" line of the email to be sent.
--to Specify the primary "To:" line of the email.
--cc Specify an initial "Cc:" list for the entire series
of emails.
--bcc Specify a list of email addresses that should be Bcc:
on all the emails.
--compose Use \$EDITOR to edit an introductory message for the
patch series.
--subject Specify the initial "Subject:" line.
Only necessary if --compose is also set. If --compose
is not set, this will be prompted for.
--in-reply-to Specify the first "In-Reply-To:" header line.
Only used if --compose is also set. If --compose is not
set, this will be prompted for.
--chain-reply-to If set, the replies will all be to the previous
email sent, rather than to the first email sent.
Defaults to on.
--no-signed-off-cc Suppress the automatic addition of email addresses
that appear in a Signed-off-by: line, to the cc: list.
Note: Using this option is not recommended.
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
--suppress-from Suppress sending emails to yourself if your address
appears in a From: line.
--quiet Make git-send-email less verbose. One line per email
should be all that is output.
EOT
exit(1);
}
# most mail servers generate the Date: header, but not all...
sub format_2822_time {
my ($time) = @_;
@@ -120,6 +167,10 @@ my $rc = GetOptions("from=s" => \$from,
"dry-run" => \$dry_run,
);
unless ($rc) {
usage();
}
# Verify the user input
foreach my $entry (@to) {
@@ -311,50 +362,8 @@ if (@files) {
print $_,"\n" for (@files);
}
} else {
print <<EOT;
git-send-email [options] <file | directory> [... file | directory ]
Options:
--from Specify the "From:" line of the email to be sent.
--to Specify the primary "To:" line of the email.
--cc Specify an initial "Cc:" list for the entire series
of emails.
--bcc Specify a list of email addresses that should be Bcc:
on all the emails.
--compose Use \$EDITOR to edit an introductory message for the
patch series.
--subject Specify the initial "Subject:" line.
Only necessary if --compose is also set. If --compose
is not set, this will be prompted for.
--in-reply-to Specify the first "In-Reply-To:" header line.
Only used if --compose is also set. If --compose is not
set, this will be prompted for.
--chain-reply-to If set, the replies will all be to the previous
email sent, rather than to the first email sent.
Defaults to on.
--no-signed-off-cc Suppress the automatic addition of email addresses
that appear in a Signed-off-by: line, to the cc: list.
Note: Using this option is not recommended.
--smtp-server If set, specifies the outgoing SMTP server to use.
Defaults to localhost.
--suppress-from Suppress sending emails to yourself if your address
appears in a From: line.
--quiet Make git-send-email less verbose. One line per email should be
all that is output.
Error: Please specify a file or a directory on the command line.
EOT
exit(1);
print STDERR "\nNo patch files specified!\n\n";
usage();
}
# Variables we set as part of the loop over files

View File

@@ -447,7 +447,7 @@ sub cmd_show_ignore {
my $url = (::working_head_info('HEAD'))[0];
my $gs = Git::SVN->find_by_url($url) || Git::SVN->new;
my $r = (defined $_revision ? $_revision : $gs->ra->get_latest_revnum);
$gs->traverse_ignore(\*STDOUT, '', $r);
$gs->traverse_ignore(\*STDOUT, $gs->{path}, $r);
}
sub cmd_multi_init {
@@ -1334,7 +1334,7 @@ sub traverse_ignore {
my $ra = $self->ra;
my ($dirent, undef, $props) = $ra->get_dir($path, $r);
my $p = $path;
$p =~ s#^\Q$ra->{svn_path}\E/##;
$p =~ s#^\Q$self->{path}\E(/|$)##;
print $fh length $p ? "\n# $p\n" : "\n# /\n";
if (my $s = $props->{'svn:ignore'}) {
$s =~ s/[\r\n]+/\n/g;

7
git.c
View File

@@ -230,9 +230,10 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "add", cmd_add, RUN_SETUP | NOT_BARE },
{ "annotate", cmd_annotate, USE_PAGER },
{ "apply", cmd_apply },
{ "archive", cmd_archive },
{ "archive", cmd_archive, RUN_SETUP },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP },
{ "bundle", cmd_bundle },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format },
@@ -241,8 +242,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "config", cmd_config },
{ "count-objects", cmd_count_objects, RUN_SETUP },
{ "describe", cmd_describe, RUN_SETUP },
{ "diff", cmd_diff, RUN_SETUP | USE_PAGER },
{ "diff-files", cmd_diff_files, RUN_SETUP },
{ "diff", cmd_diff, USE_PAGER },
{ "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
{ "fmt-merge-msg", cmd_fmt_merge_msg, RUN_SETUP },

View File

@@ -7,7 +7,7 @@
#include "cache.h"
#include "blob.h"
static void hash_object(const char *path, const char *type, int write_object)
static void hash_object(const char *path, enum object_type type, int write_object)
{
int fd;
struct stat st;
@@ -15,7 +15,7 @@ static void hash_object(const char *path, const char *type, int write_object)
fd = open(path, O_RDONLY);
if (fd < 0 ||
fstat(fd, &st) < 0 ||
index_fd(sha1, fd, &st, write_object, type))
index_fd(sha1, fd, &st, write_object, type, path))
die(write_object
? "Unable to add %s to database"
: "Unable to hash %s", path);
@@ -73,7 +73,7 @@ int main(int argc, char **argv)
if (0 <= prefix_length)
arg = prefix_filename(prefix, prefix_length,
arg);
hash_object(arg, type, write_object);
hash_object(arg, type_from_string(type), write_object);
no_more_flags = 1;
}
}

View File

@@ -479,7 +479,7 @@ static void start_put(struct transfer_request *request)
char *hex = sha1_to_hex(request->obj->sha1);
struct active_request_slot *slot;
char *posn;
char type[20];
enum object_type type;
char hdr[50];
void *unpacked;
unsigned long len;
@@ -487,8 +487,8 @@ static void start_put(struct transfer_request *request)
ssize_t size;
z_stream stream;
unpacked = read_sha1_file(request->obj->sha1, type, &len);
hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
unpacked = read_sha1_file(request->obj->sha1, &type, &len);
hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
/* Set it up */
memset(&stream, 0, sizeof(stream));
@@ -1271,7 +1271,9 @@ xml_cdata(void *userData, const XML_Char *s, int len)
struct xml_ctx *ctx = (struct xml_ctx *)userData;
free(ctx->cdata);
ctx->cdata = xmalloc(len + 1);
strlcpy(ctx->cdata, s, len + 1);
/* NB: 's' is not null-terminated, can not use strlcpy here */
memcpy(ctx->cdata, s, len);
ctx->cdata[len] = '\0';
}
static struct remote_lock *lock_remote(const char *path, long timeout)
@@ -1295,7 +1297,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
sprintf(url, "%s%s", remote->url, path);
/* Make sure leading directories exist for the remote ref */
ep = strchr(url + strlen(remote->url) + 11, '/');
ep = strchr(url + strlen(remote->url) + 1, '/');
while (ep) {
*ep = 0;
slot = get_active_slot();
@@ -1473,7 +1475,8 @@ static void process_ls_object(struct remote_ls_ctx *ls)
return;
path += 8;
obj_hex = xmalloc(strlen(path));
strlcpy(obj_hex, path, 3);
/* NB: path is not null-terminated, can not use strlcpy here */
memcpy(obj_hex, path, 2);
strcpy(obj_hex + 2, path + 3);
one_remote_object(obj_hex);
free(obj_hex);
@@ -2170,7 +2173,8 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
/* If it's a symref, set the refname; otherwise try for a sha1 */
if (!prefixcmp((char *)buffer.buffer, "ref: ")) {
*symref = xmalloc(buffer.posn - 5);
strlcpy(*symref, (char *)buffer.buffer + 5, buffer.posn - 5);
memcpy(*symref, (char *)buffer.buffer + 5, buffer.posn - 6);
(*symref)[buffer.posn - 6] = '\0';
} else {
get_sha1_hex(buffer.buffer, sha1);
}

View File

@@ -277,13 +277,19 @@ static void *get_data_from_pack(struct object_entry *obj)
{
unsigned long from = obj[0].offset + obj[0].hdr_size;
unsigned long len = obj[1].offset - from;
unsigned long rdy = 0;
unsigned char *src, *data;
z_stream stream;
int st;
src = xmalloc(len);
if (pread(pack_fd, src, len, from) != len)
die("cannot pread pack file: %s", strerror(errno));
data = src;
do {
ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
if (n <= 0)
die("cannot pread pack file: %s", strerror(errno));
rdy += n;
} while (rdy < len);
data = xmalloc(obj->size);
memset(&stream, 0, sizeof(stream));
stream.next_out = data;
@@ -457,7 +463,8 @@ static void parse_pack_objects(unsigned char *sha1)
/* If input_fd is a file, we should have reached its end now. */
if (fstat(input_fd, &st))
die("cannot fstat packfile: %s", strerror(errno));
if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
if (S_ISREG(st.st_mode) &&
lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
die("pack has junk at the end");
if (!nr_deltas)
@@ -595,30 +602,23 @@ static void fix_unresolved_deltas(int nr_unresolved)
struct delta_entry *d = sorted_by_pos[i];
void *data;
unsigned long size;
char type[10];
enum object_type obj_type;
enum object_type type;
int j, first, last;
if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
continue;
data = read_sha1_file(d->base.sha1, type, &size);
data = read_sha1_file(d->base.sha1, &type, &size);
if (!data)
continue;
if (!strcmp(type, blob_type)) obj_type = OBJ_BLOB;
else if (!strcmp(type, tree_type)) obj_type = OBJ_TREE;
else if (!strcmp(type, commit_type)) obj_type = OBJ_COMMIT;
else if (!strcmp(type, tag_type)) obj_type = OBJ_TAG;
else die("base object %s is of type '%s'",
sha1_to_hex(d->base.sha1), type);
find_delta_children(&d->base, &first, &last);
for (j = first; j <= last; j++) {
struct object_entry *child = objects + deltas[j].obj_no;
if (child->real_type == OBJ_REF_DELTA)
resolve_delta(child, data, size, obj_type);
resolve_delta(child, data, size, type);
}
append_obj_to_pack(data, size, obj_type);
append_obj_to_pack(data, size, type);
free(data);
if (verbose)
percent = display_progress(nr_resolved_deltas,

View File

@@ -211,7 +211,7 @@ void show_log(struct rev_info *opt, const char *sep)
sha1, sha1);
opt->diffopt.stat_sep = buffer;
}
} else {
} else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
fputs(diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
stdout);
if (opt->commit_format != CMIT_FMT_ONELINE)

View File

@@ -7,12 +7,12 @@ static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
{
void *buf;
unsigned long size;
char type[20];
enum object_type type;
buf = read_sha1_file(obj->object.sha1, type, &size);
buf = read_sha1_file(obj->object.sha1, &type, &size);
if (!buf)
return -1;
if (strcmp(type, blob_type))
if (type != OBJ_BLOB)
return -1;
f->ptr = buf;
f->size = size;
@@ -86,12 +86,12 @@ void *merge_file(struct blob *base, struct blob *our, struct blob *their, unsign
* modified in the other branch!
*/
if (!our || !their) {
char type[20];
enum object_type type;
if (base)
return NULL;
if (!our)
our = their;
return read_sha1_file(our->object.sha1, type, size);
return read_sha1_file(our->object.sha1, &type, size);
}
if (fill_mmfile_blob(&f1, our) < 0)

View File

@@ -560,17 +560,17 @@ static void update_file_flags(const unsigned char *sha,
update_wd = 0;
if (update_wd) {
char type[20];
enum object_type type;
void *buf;
unsigned long size;
buf = read_sha1_file(sha, type, &size);
buf = read_sha1_file(sha, &type, &size);
if (!buf)
die("cannot read object %s '%s'", sha1_to_hex(sha), path);
if (strcmp(type, blob_type) != 0)
if (type != OBJ_BLOB)
die("blob expected for %s '%s'", sha1_to_hex(sha), path);
if (S_ISREG(mode)) {
if (S_ISREG(mode) || (!has_symlinks && S_ISLNK(mode))) {
int fd;
if (mkdir_p(path, 0777))
die("failed to create path %s: %s", path, strerror(errno));
@@ -591,6 +591,7 @@ static void update_file_flags(const unsigned char *sha,
mkdir_p(path, 0777);
unlink(path);
symlink(lnk, path);
free(lnk);
} else
die("do not know what to do with %06o %s '%s'",
mode, sha1_to_hex(sha), path);
@@ -620,7 +621,7 @@ struct merge_file_info
static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
{
unsigned long size;
char type[20];
enum object_type type;
if (!hashcmp(sha1, null_sha1)) {
mm->ptr = xstrdup("");
@@ -628,8 +629,8 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
return;
}
mm->ptr = read_sha1_file(sha1, type, &size);
if (!mm->ptr || strcmp(type, blob_type))
mm->ptr = read_sha1_file(sha1, &type, &size);
if (!mm->ptr || type != OBJ_BLOB)
die("unable to read blob object %s", sha1_to_hex(sha1));
mm->size = size;
}
@@ -1213,7 +1214,7 @@ static int merge(struct commit *h1,
tree->object.parsed = 1;
tree->object.type = OBJ_TREE;
pretend_sha1_file(NULL, 0, tree_type, tree->object.sha1);
pretend_sha1_file(NULL, 0, OBJ_TREE, tree->object.sha1);
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
}

View File

@@ -57,11 +57,11 @@ extern void *merge_file(struct blob *, struct blob *, struct blob *, unsigned lo
static void *result(struct merge_list *entry, unsigned long *size)
{
char type[20];
enum object_type type;
struct blob *base, *our, *their;
if (!entry->stage)
return read_sha1_file(entry->blob->object.sha1, type, size);
return read_sha1_file(entry->blob->object.sha1, &type, size);
base = NULL;
if (entry->stage == 1) {
base = entry->blob;
@@ -80,10 +80,10 @@ static void *result(struct merge_list *entry, unsigned long *size)
static void *origin(struct merge_list *entry, unsigned long *size)
{
char type[20];
enum object_type type;
while (entry) {
if (entry->stage == 2)
return read_sha1_file(entry->blob->object.sha1, type, size);
return read_sha1_file(entry->blob->object.sha1, &type, size);
entry = entry->link;
}
return NULL;

View File

@@ -27,13 +27,13 @@
static int verify_object(unsigned char *sha1, const char *expected_type)
{
int ret = -1;
char type[100];
enum object_type type;
unsigned long size;
void *buffer = read_sha1_file(sha1, type, &size);
void *buffer = read_sha1_file(sha1, &type, &size);
if (buffer) {
if (!strcmp(type, expected_type))
ret = check_sha1_signature(sha1, buffer, size, type);
if (type == type_from_string(expected_type))
ret = check_sha1_signature(sha1, buffer, size, expected_type);
free(buffer);
}
return ret;

View File

@@ -95,7 +95,7 @@ int main(int ac, char **av)
int len;
char *ptr, *ntr;
unsigned mode;
char type[20];
enum object_type type;
char *path;
read_line(&sb, stdin, line_termination);
@@ -115,11 +115,12 @@ int main(int ac, char **av)
ntr[41] != '\t' ||
get_sha1_hex(ntr + 1, sha1))
die("input format error: %s", sb.buf);
if (sha1_object_info(sha1, type, NULL))
type = sha1_object_info(sha1, NULL);
if (type < 0)
die("object %s unavailable", sha1_to_hex(sha1));
*ntr++ = 0; /* now at the beginning of SHA1 */
if (strcmp(ptr, type))
die("object type %s mismatch (%s)", ptr, type);
if (type != type_from_string(ptr))
die("object type %s mismatch (%s)", ptr, typename(type));
ntr += 41; /* at the beginning of name */
if (line_termination && ntr[0] == '"')
path = unquote_c_style(ntr, NULL);

View File

@@ -18,11 +18,31 @@ struct object *get_indexed_object(unsigned int idx)
return obj_hash[idx];
}
const char *type_names[] = {
"none", "commit", "tree", "blob", "tag",
"bad type 5", "bad type 6", "delta", "bad",
static const char *object_type_strings[] = {
NULL, /* OBJ_NONE = 0 */
"commit", /* OBJ_COMMIT = 1 */
"tree", /* OBJ_TREE = 2 */
"blob", /* OBJ_BLOB = 3 */
"tag", /* OBJ_TAG = 4 */
};
const char *typename(unsigned int type)
{
if (type >= ARRAY_SIZE(object_type_strings))
return NULL;
return object_type_strings[type];
}
int type_from_string(const char *str)
{
int i;
for (i = 1; i < ARRAY_SIZE(object_type_strings); i++)
if (!strcmp(str, object_type_strings[i]))
return i;
die("invalid object type \"%s\"", str);
}
static unsigned int hash_obj(struct object *obj, unsigned int n)
{
unsigned int hash = *(unsigned int *)obj->sha1;
@@ -100,24 +120,6 @@ void created_object(const unsigned char *sha1, struct object *obj)
nr_objs++;
}
struct object *lookup_object_type(const unsigned char *sha1, const char *type)
{
if (!type) {
return lookup_unknown_object(sha1);
} else if (!strcmp(type, blob_type)) {
return &lookup_blob(sha1)->object;
} else if (!strcmp(type, tree_type)) {
return &lookup_tree(sha1)->object;
} else if (!strcmp(type, commit_type)) {
return &lookup_commit(sha1)->object;
} else if (!strcmp(type, tag_type)) {
return &lookup_tag(sha1)->object;
} else {
error("Unknown type %s", type);
return NULL;
}
}
union any_object {
struct object object;
struct commit commit;
@@ -138,23 +140,23 @@ struct object *lookup_unknown_object(const unsigned char *sha1)
return obj;
}
struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p)
struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
{
struct object *obj;
int eaten = 0;
if (!strcmp(type, blob_type)) {
if (type == OBJ_BLOB) {
struct blob *blob = lookup_blob(sha1);
parse_blob_buffer(blob, buffer, size);
obj = &blob->object;
} else if (!strcmp(type, tree_type)) {
} else if (type == OBJ_TREE) {
struct tree *tree = lookup_tree(sha1);
obj = &tree->object;
if (!tree->object.parsed) {
parse_tree_buffer(tree, buffer, size);
eaten = 1;
}
} else if (!strcmp(type, commit_type)) {
} else if (type == OBJ_COMMIT) {
struct commit *commit = lookup_commit(sha1);
parse_commit_buffer(commit, buffer, size);
if (!commit->buffer) {
@@ -162,7 +164,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, const char *type,
eaten = 1;
}
obj = &commit->object;
} else if (!strcmp(type, tag_type)) {
} else if (type == OBJ_TAG) {
struct tag *tag = lookup_tag(sha1);
parse_tag_buffer(tag, buffer, size);
obj = &tag->object;
@@ -176,13 +178,13 @@ struct object *parse_object_buffer(const unsigned char *sha1, const char *type,
struct object *parse_object(const unsigned char *sha1)
{
unsigned long size;
char type[20];
enum object_type type;
int eaten;
void *buffer = read_sha1_file(sha1, type, &size);
void *buffer = read_sha1_file(sha1, &type, &size);
if (buffer) {
struct object *obj;
if (check_sha1_signature(sha1, buffer, size, type) < 0)
if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0)
printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
obj = parse_object_buffer(sha1, type, size, buffer, &eaten);

View File

@@ -36,24 +36,17 @@ struct object {
};
extern int track_object_refs;
extern const char *type_names[9];
extern const char *typename(unsigned int type);
extern int type_from_string(const char *str);
extern unsigned int get_max_object_index(void);
extern struct object *get_indexed_object(unsigned int);
static inline const char *typename(unsigned int type)
{
return type_names[type > OBJ_BAD ? OBJ_BAD : type];
}
extern struct object_refs *lookup_object_refs(struct object *);
/** Internal only **/
struct object *lookup_object(const unsigned char *sha1);
/** Returns the object, having looked it up as being the given type. **/
struct object *lookup_object_type(const unsigned char *sha1, const char *type);
void created_object(const unsigned char *sha1, struct object *obj);
/** Returns the object, having parsed it to find out what it is. **/
@@ -63,7 +56,7 @@ struct object *parse_object(const unsigned char *sha1);
* parsing it. eaten_p indicates if the object has a borrowed copy
* of buffer and the caller should not free() it.
*/
struct object *parse_object_buffer(const unsigned char *sha1, const char *type, unsigned long size, void *buffer, int *eaten_p);
struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p);
/** Returns the object, with potentially excess memory allocated. **/
struct object *lookup_unknown_object(const unsigned char *sha1);

View File

@@ -43,7 +43,7 @@ static int verify_packfile(struct packed_git *p,
for (i = err = 0; i < nr_objects; i++) {
unsigned char sha1[20];
void *data;
char type[20];
enum object_type type;
unsigned long size, offset;
if (nth_packed_object_sha1(p, i, sha1))
@@ -51,13 +51,13 @@ static int verify_packfile(struct packed_git *p,
offset = find_pack_entry_one(sha1, p);
if (!offset)
die("internal error pack-check find-pack-entry-one");
data = unpack_entry(p, offset, type, &size);
data = unpack_entry(p, offset, &type, &size);
if (!data) {
err = error("cannot unpack %s from %s",
sha1_to_hex(sha1), p->pack_name);
continue;
}
if (check_sha1_signature(sha1, data, size, type)) {
if (check_sha1_signature(sha1, data, size, typename(type))) {
err = error("packed %s from %s is corrupt",
sha1_to_hex(sha1), p->pack_name);
free(data);
@@ -82,7 +82,7 @@ static void show_pack_info(struct packed_git *p)
for (i = 0; i < nr_objects; i++) {
unsigned char sha1[20], base_sha1[20];
char type[20];
const char *type;
unsigned long size;
unsigned long store_size;
unsigned long offset;
@@ -94,9 +94,9 @@ static void show_pack_info(struct packed_git *p)
if (!offset)
die("internal error pack-check find-pack-entry-one");
packed_object_info_detail(p, offset, type, &size, &store_size,
&delta_chain_length,
base_sha1);
type = packed_object_info_detail(p, offset, &size, &store_size,
&delta_chain_length,
base_sha1);
printf("%s ", sha1_to_hex(sha1));
if (!delta_chain_length)
printf("%-6s %lu %lu\n", type, size, offset);

View File

@@ -59,7 +59,7 @@ static int ce_compare_data(struct cache_entry *ce, struct stat *st)
if (fd >= 0) {
unsigned char sha1[20];
if (!index_fd(sha1, fd, st, 0, NULL))
if (!index_fd(sha1, fd, st, 0, OBJ_BLOB, ce->name))
match = hashcmp(sha1, ce->sha1);
/* index_fd() closed the file descriptor already */
}
@@ -72,7 +72,7 @@ static int ce_compare_link(struct cache_entry *ce, unsigned long expected_size)
char *target;
void *buffer;
unsigned long size;
char type[10];
enum object_type type;
int len;
target = xmalloc(expected_size);
@@ -81,7 +81,7 @@ static int ce_compare_link(struct cache_entry *ce, unsigned long expected_size)
free(target);
return -1;
}
buffer = read_sha1_file(ce->sha1, type, &size);
buffer = read_sha1_file(ce->sha1, &type, &size);
if (!buffer) {
free(target);
return -1;
@@ -125,7 +125,9 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
changed |= MODE_CHANGED;
break;
case S_IFLNK:
changed |= !S_ISLNK(st->st_mode) ? TYPE_CHANGED : 0;
if (!S_ISLNK(st->st_mode) &&
(has_symlinks || !S_ISREG(st->st_mode)))
changed |= TYPE_CHANGED;
break;
default:
die("internal error: ce_mode is %o", ntohl(ce->ce_mode));
@@ -344,11 +346,11 @@ int add_file_to_index(const char *path, int verbose)
ce->ce_flags = htons(namelen);
fill_stat_cache_info(ce, &st);
if (trust_executable_bit)
if (trust_executable_bit && has_symlinks)
ce->ce_mode = create_ce_mode(st.st_mode);
else {
/* If there is an existing entry, pick the mode bits
* from it, otherwise assume unexecutable.
/* If there is an existing entry, pick the mode bits and type
* from it, otherwise assume unexecutable regular file.
*/
struct cache_entry *ent;
int pos = cache_name_pos(path, namelen);

2
refs.c
View File

@@ -1061,7 +1061,9 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
return -1;
}
#ifndef NO_SYMLINK_HEAD
done:
#endif
if (logmsg && !read_ref(refs_heads_master, new_sha1))
log_ref_write(ref_target, old_sha1, new_sha1, logmsg);

View File

@@ -116,6 +116,8 @@ void mark_parents_uninteresting(struct commit *commit)
void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
{
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);
if (revs->reflog_info && obj->type == OBJ_COMMIT)
add_reflog_for_walk(revs->reflog_info,
@@ -480,7 +482,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
struct all_refs_cb *cb = cb_data;
struct object *object = get_reference(cb->all_revs, path, sha1,
cb->all_flags);
add_pending_object(cb->all_revs, object, "");
add_pending_object(cb->all_revs, object, path);
return 0;
}

View File

@@ -962,7 +962,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
/* And generate the fake traditional header */
stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu",
type_names[type], size);
typename(type), size);
return 0;
}
@@ -993,26 +993,27 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
* too permissive for what we want to check. So do an anal
* object header parse by hand.
*/
static int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
static int parse_sha1_header(const char *hdr, unsigned long *sizep)
{
char type[10];
int i;
unsigned long size;
/*
* The type can be at most ten bytes (including the
* terminating '\0' that we add), and is followed by
* a space.
* a space.
*/
i = 10;
i = 0;
for (;;) {
char c = *hdr++;
if (c == ' ')
break;
if (!--i)
type[i++] = c;
if (i >= sizeof(type))
return -1;
*type++ = c;
}
*type = 0;
type[i] = 0;
/*
* The length must follow immediately, and be in canonical
@@ -1035,17 +1036,17 @@ static int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
/*
* The length must be followed by a zero byte
*/
return *hdr ? -1 : 0;
return *hdr ? -1 : type_from_string(type);
}
void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size)
void * unpack_sha1_file(void *map, unsigned long mapsize, enum object_type *type, unsigned long *size)
{
int ret;
z_stream stream;
char hdr[8192];
ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
if (ret < Z_OK || parse_sha1_header(hdr, type, size) < 0)
if (ret < Z_OK || (*type = parse_sha1_header(hdr, size)) < 0)
return NULL;
return unpack_sha1_rest(&stream, hdr, *size);
@@ -1053,12 +1054,11 @@ void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned l
static unsigned long get_delta_base(struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
enum object_type kind,
unsigned long delta_obj_offset,
unsigned long *base_obj_offset)
unsigned long *curpos,
enum object_type type,
unsigned long delta_obj_offset)
{
unsigned char *base_info = use_pack(p, w_curs, offset, NULL);
unsigned char *base_info = use_pack(p, w_curs, *curpos, NULL);
unsigned long base_offset;
/* use_pack() assured us we have [base_info, base_info + 20)
@@ -1067,7 +1067,7 @@ static unsigned long get_delta_base(struct packed_git *p,
* that is assured. An OFS_DELTA longer than the hash size
* is stupid, as then a REF_DELTA would be smaller to store.
*/
if (kind == OBJ_OFS_DELTA) {
if (type == OBJ_OFS_DELTA) {
unsigned used = 0;
unsigned char c = base_info[used++];
base_offset = c & 127;
@@ -1081,49 +1081,43 @@ static unsigned long get_delta_base(struct packed_git *p,
base_offset = delta_obj_offset - base_offset;
if (base_offset >= delta_obj_offset)
die("delta base offset out of bound");
offset += used;
} else if (kind == OBJ_REF_DELTA) {
*curpos += used;
} else if (type == OBJ_REF_DELTA) {
/* The base entry _must_ be in the same pack */
base_offset = find_pack_entry_one(base_info, p);
if (!base_offset)
die("failed to find delta-pack base object %s",
sha1_to_hex(base_info));
offset += 20;
*curpos += 20;
} else
die("I am totally screwed");
*base_obj_offset = base_offset;
return offset;
return base_offset;
}
/* forward declaration for a mutually recursive function */
static int packed_object_info(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep);
unsigned long *sizep);
static int packed_delta_info(struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
enum object_type kind,
unsigned long curpos,
enum object_type type,
unsigned long obj_offset,
char *type,
unsigned long *sizep)
{
unsigned long base_offset;
offset = get_delta_base(p, w_curs, offset, kind,
obj_offset, &base_offset);
base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
type = packed_object_info(p, base_offset, NULL);
/* We choose to only get the type of the base object and
* ignore potentially corrupt pack file that expects the delta
* based on a base with a wrong size. This saves tons of
* inflate() calls.
*/
if (packed_object_info(p, base_offset, type, NULL))
die("cannot get info for delta-pack base");
if (sizep) {
const unsigned char *data;
unsigned char delta_head[20], *in;
unsigned long result_size;
z_stream stream;
int st;
@@ -1133,10 +1127,10 @@ static int packed_delta_info(struct packed_git *p,
inflateInit(&stream);
do {
in = use_pack(p, w_curs, offset, &stream.avail_in);
in = use_pack(p, w_curs, curpos, &stream.avail_in);
stream.next_in = in;
st = inflate(&stream, Z_FINISH);
offset += stream.next_in - in;
curpos += stream.next_in - in;
} while ((st == Z_OK || st == Z_BUF_ERROR)
&& stream.total_out < sizeof(delta_head));
inflateEnd(&stream);
@@ -1153,21 +1147,21 @@ static int packed_delta_info(struct packed_git *p,
get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
/* Read the result size */
result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
*sizep = result_size;
*sizep = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
}
return 0;
return type;
}
static unsigned long unpack_object_header(struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
enum object_type *type,
unsigned long *sizep)
static int unpack_object_header(struct packed_git *p,
struct pack_window **w_curs,
unsigned long *curpos,
unsigned long *sizep)
{
unsigned char *base;
unsigned int left;
unsigned long used;
enum object_type type;
/* use_pack() assures us we have [base, base + 20) available
* as a range that we can look at at. (Its actually the hash
@@ -1175,100 +1169,95 @@ static unsigned long unpack_object_header(struct packed_git *p,
* the maximum deflated object size is 2^137, which is just
* insane, so we know won't exceed what we have been given.
*/
base = use_pack(p, w_curs, offset, &left);
used = unpack_object_header_gently(base, left, type, sizep);
base = use_pack(p, w_curs, *curpos, &left);
used = unpack_object_header_gently(base, left, &type, sizep);
if (!used)
die("object offset outside of pack file");
*curpos += used;
return offset + used;
return type;
}
void packed_object_info_detail(struct packed_git *p,
unsigned long offset,
char *type,
unsigned long *size,
unsigned long *store_size,
unsigned int *delta_chain_length,
unsigned char *base_sha1)
const char *packed_object_info_detail(struct packed_git *p,
unsigned long obj_offset,
unsigned long *size,
unsigned long *store_size,
unsigned int *delta_chain_length,
unsigned char *base_sha1)
{
struct pack_window *w_curs = NULL;
unsigned long obj_offset, val;
unsigned long curpos, dummy;
unsigned char *next_sha1;
enum object_type kind;
enum object_type type;
*delta_chain_length = 0;
obj_offset = offset;
offset = unpack_object_header(p, &w_curs, offset, &kind, size);
curpos = obj_offset;
type = unpack_object_header(p, &w_curs, &curpos, size);
for (;;) {
switch (kind) {
switch (type) {
default:
die("pack %s contains unknown object type %d",
p->pack_name, kind);
p->pack_name, type);
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
strcpy(type, type_names[kind]);
*store_size = 0; /* notyet */
unuse_pack(&w_curs);
return;
return typename(type);
case OBJ_OFS_DELTA:
get_delta_base(p, &w_curs, offset, kind,
obj_offset, &offset);
obj_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset);
if (*delta_chain_length == 0) {
/* TODO: find base_sha1 as pointed by offset */
/* TODO: find base_sha1 as pointed by curpos */
}
break;
case OBJ_REF_DELTA:
next_sha1 = use_pack(p, &w_curs, offset, NULL);
next_sha1 = use_pack(p, &w_curs, curpos, NULL);
if (*delta_chain_length == 0)
hashcpy(base_sha1, next_sha1);
offset = find_pack_entry_one(next_sha1, p);
obj_offset = find_pack_entry_one(next_sha1, p);
break;
}
obj_offset = offset;
offset = unpack_object_header(p, &w_curs, offset, &kind, &val);
(*delta_chain_length)++;
curpos = obj_offset;
type = unpack_object_header(p, &w_curs, &curpos, &dummy);
}
}
static int packed_object_info(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep)
static int packed_object_info(struct packed_git *p, unsigned long obj_offset,
unsigned long *sizep)
{
struct pack_window *w_curs = NULL;
unsigned long size, obj_offset = offset;
enum object_type kind;
int r;
unsigned long size, curpos = obj_offset;
enum object_type type;
offset = unpack_object_header(p, &w_curs, offset, &kind, &size);
type = unpack_object_header(p, &w_curs, &curpos, &size);
switch (kind) {
switch (type) {
case OBJ_OFS_DELTA:
case OBJ_REF_DELTA:
r = packed_delta_info(p, &w_curs, offset, kind,
obj_offset, type, sizep);
unuse_pack(&w_curs);
return r;
type = packed_delta_info(p, &w_curs, curpos,
type, obj_offset, sizep);
break;
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
strcpy(type, type_names[kind]);
unuse_pack(&w_curs);
if (sizep)
*sizep = size;
break;
default:
die("pack %s contains unknown object type %d",
p->pack_name, kind);
p->pack_name, type);
}
if (sizep)
*sizep = size;
return 0;
unuse_pack(&w_curs);
return type;
}
static void *unpack_compressed_entry(struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
unsigned long curpos,
unsigned long size)
{
int st;
@@ -1283,10 +1272,10 @@ static void *unpack_compressed_entry(struct packed_git *p,
inflateInit(&stream);
do {
in = use_pack(p, w_curs, offset, &stream.avail_in);
in = use_pack(p, w_curs, curpos, &stream.avail_in);
stream.next_in = in;
st = inflate(&stream, Z_FINISH);
offset += stream.next_in - in;
curpos += stream.next_in - in;
} while (st == Z_OK || st == Z_BUF_ERROR);
inflateEnd(&stream);
if ((st != Z_STREAM_END) || stream.total_out != size) {
@@ -1299,63 +1288,57 @@ static void *unpack_compressed_entry(struct packed_git *p,
static void *unpack_delta_entry(struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
unsigned long curpos,
unsigned long delta_size,
enum object_type kind,
unsigned long obj_offset,
char *type,
enum object_type *type,
unsigned long *sizep)
{
void *delta_data, *result, *base;
unsigned long result_size, base_size, base_offset;
unsigned long base_size, base_offset;
offset = get_delta_base(p, w_curs, offset, kind,
obj_offset, &base_offset);
base_offset = get_delta_base(p, w_curs, &curpos, *type, obj_offset);
base = unpack_entry(p, base_offset, type, &base_size);
if (!base)
die("failed to read delta base object at %lu from %s",
base_offset, p->pack_name);
delta_data = unpack_compressed_entry(p, w_curs, offset, delta_size);
delta_data = unpack_compressed_entry(p, w_curs, curpos, delta_size);
result = patch_delta(base, base_size,
delta_data, delta_size,
&result_size);
sizep);
if (!result)
die("failed to apply delta");
free(delta_data);
free(base);
*sizep = result_size;
return result;
}
void *unpack_entry(struct packed_git *p, unsigned long offset,
char *type, unsigned long *sizep)
void *unpack_entry(struct packed_git *p, unsigned long obj_offset,
enum object_type *type, unsigned long *sizep)
{
struct pack_window *w_curs = NULL;
unsigned long size, obj_offset = offset;
enum object_type kind;
void *retval;
unsigned long curpos = obj_offset;
void *data;
offset = unpack_object_header(p, &w_curs, offset, &kind, &size);
switch (kind) {
*type = unpack_object_header(p, &w_curs, &curpos, sizep);
switch (*type) {
case OBJ_OFS_DELTA:
case OBJ_REF_DELTA:
retval = unpack_delta_entry(p, &w_curs, offset, size,
kind, obj_offset, type, sizep);
data = unpack_delta_entry(p, &w_curs, curpos, *sizep,
obj_offset, type, sizep);
break;
case OBJ_COMMIT:
case OBJ_TREE:
case OBJ_BLOB:
case OBJ_TAG:
strcpy(type, type_names[kind]);
*sizep = size;
retval = unpack_compressed_entry(p, &w_curs, offset, size);
data = unpack_compressed_entry(p, &w_curs, curpos, *sizep);
break;
default:
die("unknown object type %i in %s", kind, p->pack_name);
die("unknown object type %i in %s", *type, p->pack_name);
}
unuse_pack(&w_curs);
return retval;
return data;
}
int num_packed_objects(const struct packed_git *p)
@@ -1462,16 +1445,16 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
return p;
}
return NULL;
}
static int sha1_loose_object_info(const unsigned char *sha1, char *type, unsigned long *sizep)
static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep)
{
int status;
unsigned long mapsize, size;
void *map;
z_stream stream;
char hdr[128];
char hdr[32];
map = map_sha1_file(sha1, &mapsize);
if (!map)
@@ -1479,31 +1462,29 @@ static int sha1_loose_object_info(const unsigned char *sha1, char *type, unsigne
if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
status = error("unable to unpack %s header",
sha1_to_hex(sha1));
if (parse_sha1_header(hdr, type, &size) < 0)
else if ((status = parse_sha1_header(hdr, &size)) < 0)
status = error("unable to parse %s header", sha1_to_hex(sha1));
else {
status = 0;
if (sizep)
*sizep = size;
}
else if (sizep)
*sizep = size;
inflateEnd(&stream);
munmap(map, mapsize);
return status;
}
int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep)
int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
{
struct pack_entry e;
if (!find_pack_entry(sha1, &e, NULL)) {
reprepare_packed_git();
if (!find_pack_entry(sha1, &e, NULL))
return sha1_loose_object_info(sha1, type, sizep);
return sha1_loose_object_info(sha1, sizep);
}
return packed_object_info(e.p, e.offset, type, sizep);
return packed_object_info(e.p, e.offset, sizep);
}
static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned long *size)
static void *read_packed_sha1(const unsigned char *sha1,
enum object_type *type, unsigned long *size)
{
struct pack_entry e;
@@ -1521,7 +1502,7 @@ static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned lo
*/
static struct cached_object {
unsigned char sha1[20];
const char *type;
enum object_type type;
void *buf;
unsigned long size;
} *cached_objects;
@@ -1539,11 +1520,12 @@ static struct cached_object *find_cached_object(const unsigned char *sha1)
return NULL;
}
int pretend_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1)
int pretend_sha1_file(void *buf, unsigned long len, enum object_type type,
unsigned char *sha1)
{
struct cached_object *co;
hash_sha1_file(buf, len, type, sha1);
hash_sha1_file(buf, len, typename(type), sha1);
if (has_sha1_file(sha1) || find_cached_object(sha1))
return 0;
if (cached_object_alloc <= cached_object_nr) {
@@ -1554,14 +1536,15 @@ int pretend_sha1_file(void *buf, unsigned long len, const char *type, unsigned c
}
co = &cached_objects[cached_object_nr++];
co->size = len;
co->type = strdup(type);
co->type = type;
co->buf = xmalloc(len);
memcpy(co->buf, buf, len);
hashcpy(co->sha1, sha1);
return 0;
}
void *read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
unsigned long *size)
{
unsigned long mapsize;
void *map, *buf;
@@ -1572,7 +1555,7 @@ void *read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
buf = xmalloc(co->size + 1);
memcpy(buf, co->buf, co->size);
((char*)buf)[co->size] = 0;
strcpy(type, co->type);
*type = co->type;
*size = co->size;
return buf;
}
@@ -1591,33 +1574,34 @@ void *read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
}
void *read_object_with_reference(const unsigned char *sha1,
const char *required_type,
const char *required_type_name,
unsigned long *size,
unsigned char *actual_sha1_return)
{
char type[20];
enum object_type type, required_type;
void *buffer;
unsigned long isize;
unsigned char actual_sha1[20];
required_type = type_from_string(required_type_name);
hashcpy(actual_sha1, sha1);
while (1) {
int ref_length = -1;
const char *ref_type = NULL;
buffer = read_sha1_file(actual_sha1, type, &isize);
buffer = read_sha1_file(actual_sha1, &type, &isize);
if (!buffer)
return NULL;
if (!strcmp(type, required_type)) {
if (type == required_type) {
*size = isize;
if (actual_sha1_return)
hashcpy(actual_sha1_return, actual_sha1);
return buffer;
}
/* Handle references */
else if (!strcmp(type, commit_type))
else if (type == OBJ_COMMIT)
ref_type = "tree ";
else if (!strcmp(type, tag_type))
else if (type == OBJ_TAG)
ref_type = "object ";
else {
free(buffer);
@@ -1638,12 +1622,12 @@ void *read_object_with_reference(const unsigned char *sha1,
static void write_sha1_file_prepare(void *buf, unsigned long len,
const char *type, unsigned char *sha1,
unsigned char *hdr, int *hdrlen)
char *hdr, int *hdrlen)
{
SHA_CTX c;
/* Generate the header */
*hdrlen = sprintf((char *)hdr, "%s %lu", type, len)+1;
*hdrlen = sprintf(hdr, "%s %lu", type, len)+1;
/* Sha1.. */
SHA1_Init(&c);
@@ -1750,33 +1734,24 @@ static int write_binary_header(unsigned char *hdr, enum object_type type, unsign
static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
{
int obj_type, hdr;
int obj_type, hdrlen;
if (use_legacy_headers) {
while (deflate(stream, 0) == Z_OK)
/* nothing */;
return;
}
if (!strcmp(type, blob_type))
obj_type = OBJ_BLOB;
else if (!strcmp(type, tree_type))
obj_type = OBJ_TREE;
else if (!strcmp(type, commit_type))
obj_type = OBJ_COMMIT;
else if (!strcmp(type, tag_type))
obj_type = OBJ_TAG;
else
die("trying to generate bogus object of type '%s'", type);
hdr = write_binary_header(stream->next_out, obj_type, len);
stream->total_out = hdr;
stream->next_out += hdr;
stream->avail_out -= hdr;
obj_type = type_from_string(type);
hdrlen = write_binary_header(stream->next_out, obj_type, len);
stream->total_out = hdrlen;
stream->next_out += hdrlen;
stream->avail_out -= hdrlen;
}
int hash_sha1_file(void *buf, unsigned long len, const char *type,
unsigned char *sha1)
{
unsigned char hdr[50];
char hdr[32];
int hdrlen;
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
return 0;
@@ -1790,7 +1765,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
unsigned char sha1[20];
char *filename;
static char tmpfile[PATH_MAX];
unsigned char hdr[50];
char hdr[32];
int fd, hdrlen;
/* Normally if we have it in the pack then we do not bother writing
@@ -1837,7 +1812,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
stream.avail_out = size;
/* First header.. */
stream.next_in = hdr;
stream.next_in = (unsigned char *)hdr;
stream.avail_in = hdrlen;
setup_object_header(&stream, type, len);
@@ -1868,17 +1843,17 @@ static void *repack_object(const unsigned char *sha1, unsigned long *objsize)
z_stream stream;
unsigned char *unpacked;
unsigned long len;
char type[20];
char hdr[50];
enum object_type type;
char hdr[32];
int hdrlen;
void *buf;
/* need to unpack and recompress it by itself */
unpacked = read_packed_sha1(sha1, type, &len);
unpacked = read_packed_sha1(sha1, &type, &len);
if (!unpacked)
error("cannot read sha1_file for %s", sha1_to_hex(sha1));
hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
/* Set it up */
memset(&stream, 0, sizeof(stream));
@@ -2088,7 +2063,8 @@ int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
return ret;
}
int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type)
int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
enum object_type type, const char *path)
{
unsigned long size = st->st_size;
void *buf;
@@ -2100,15 +2076,15 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
close(fd);
if (!type)
type = blob_type;
type = OBJ_BLOB;
/*
* Convert blobs to git internal format
*/
if (!strcmp(type, blob_type)) {
if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
unsigned long nsize = size;
char *nbuf = buf;
if (convert_to_git(NULL, &nbuf, &nsize)) {
if (convert_to_git(path, &nbuf, &nsize)) {
if (size)
munmap(buf, size);
size = nsize;
@@ -2118,9 +2094,9 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, con
}
if (write_object)
ret = write_sha1_file(buf, size, type, sha1);
ret = write_sha1_file(buf, size, typename(type), sha1);
else
ret = hash_sha1_file(buf, size, type, sha1);
ret = hash_sha1_file(buf, size, typename(type), sha1);
if (re_allocated) {
free(buf);
return ret;
@@ -2141,7 +2117,7 @@ int index_path(unsigned char *sha1, const char *path, struct stat *st, int write
if (fd < 0)
return error("open(\"%s\"): %s", path,
strerror(errno));
if (index_fd(sha1, fd, st, write_object, NULL) < 0)
if (index_fd(sha1, fd, st, write_object, OBJ_BLOB, path) < 0)
return error("%s: failed to insert into database",
path);
break;

View File

@@ -577,6 +577,62 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
return get_short_sha1(name, len, sha1, 0);
}
static int handle_one_ref(const char *path,
const unsigned char *sha1, int flag, void *cb_data)
{
struct commit_list **list = cb_data;
struct object *object = parse_object(sha1);
if (!object)
return 0;
if (object->type == OBJ_TAG)
object = deref_tag(object, path, strlen(path));
if (object->type != OBJ_COMMIT)
return 0;
insert_by_date((struct commit *)object, list);
return 0;
}
/*
* This interprets names like ':/Initial revision of "git"' by searching
* through history and returning the first commit whose message starts
* with the given string.
*
* For future extension, ':/!' is reserved. If you want to match a message
* beginning with a '!', you have to repeat the exclamation mark.
*/
#define ONELINE_SEEN (1u<<20)
int get_sha1_oneline(const char *prefix, unsigned char *sha1)
{
struct commit_list *list = NULL, *backup = NULL, *l;
struct commit *commit;
if (prefix[0] == '!') {
if (prefix[1] != '!')
die ("Invalid search pattern: %s", prefix);
prefix++;
}
if (!save_commit_buffer)
return error("Could not expand oneline-name.");
for_each_ref(handle_one_ref, &list);
for (l = list; l; l = l->next)
commit_list_insert(l->item, &backup);
while ((commit = pop_most_recent_commit(&list, ONELINE_SEEN))) {
char *p;
parse_object(commit->object.sha1);
if (!commit->buffer || !(p = strstr(commit->buffer, "\n\n")))
continue;
if (!prefixcmp(p + 2, prefix)) {
hashcpy(sha1, commit->object.sha1);
break;
}
}
free_commit_list(list);
for (l = backup; l; l = l->next)
clear_commit_marks(l->item, ONELINE_SEEN);
return commit == NULL;
}
/*
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
* notably "xyz^" for "parent of xyz"
@@ -600,6 +656,8 @@ int get_sha1(const char *name, unsigned char *sha1)
int stage = 0;
struct cache_entry *ce;
int pos;
if (namelen > 2 && name[1] == '/')
return get_sha1_oneline(name + 2, sha1);
if (namelen < 3 ||
name[2] != ':' ||
name[1] < '0' || '3' < name[1])

View File

@@ -0,0 +1,28 @@
#!/bin/sh
#
# Copyright (c) 2007 Johannes Sixt
#
test_description='git-checkout-index on filesystem w/o symlinks test.
This tests that git-checkout-index creates a symbolic link as a plain
file if core.symlinks is false.'
. ./test-lib.sh
test_expect_success \
'preparation' '
git-config core.symlinks false &&
l=$(echo -n file | git-hash-object -t blob -w --stdin) &&
echo "120000 $l symlink" | git-update-index --index-info'
test_expect_success \
'the checked-out symlink must be a file' '
git-checkout-index symlink &&
test -f symlink'
test_expect_success \
'the file must be the blob we added during the setup' '
test "$(git-hash-object -t blob symlink)" = $l'
test_done

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