mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge with git://repo.or.cz/alt-git.git#master
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,7 @@ git-archive
|
||||
git-bisect
|
||||
git-blame
|
||||
git-branch
|
||||
git-bundle
|
||||
git-cat-file
|
||||
git-check-ref-format
|
||||
git-checkout
|
||||
|
||||
@@ -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 $< | \
|
||||
|
||||
58
Documentation/RelNotes-1.5.0.3.txt
Normal file
58
Documentation/RelNotes-1.5.0.3.txt
Normal 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.
|
||||
@@ -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:
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
139
Documentation/git-bundle.txt
Normal file
139
Documentation/git-bundle.txt
Normal 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
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
------
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
11
Makefile
11
Makefile
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
6
blob.c
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
424
builtin-bundle.c
Normal 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
48
cache.h
@@ -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);
|
||||
|
||||
@@ -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
201
commit.c
@@ -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);
|
||||
|
||||
1
commit.h
1
commit.h
@@ -47,6 +47,7 @@ enum cmit_fmt {
|
||||
CMIT_FMT_FULLER,
|
||||
CMIT_FMT_ONELINE,
|
||||
CMIT_FMT_EMAIL,
|
||||
CMIT_FMT_USERFORMAT,
|
||||
|
||||
CMIT_FMT_UNSPECIFIED,
|
||||
};
|
||||
|
||||
5
config.c
5
config.c
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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
20
date.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
295
diff-lib.c
295
diff-lib.c
@@ -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
16
diff.c
@@ -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
6
diff.h
@@ -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
15
entry.c
@@ -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 "
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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';
|
||||
|
||||
12
fetch-pack.c
12
fetch-pack.c
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
################################################################
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
####
|
||||
|
||||
12
git-fetch.sh
12
git-fetch.sh
@@ -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 '
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Cleanup unreachable files and optimize the repository.
|
||||
|
||||
USAGE='git-gc [--prune]'
|
||||
USAGE='[--prune]'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
;;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
7
git.c
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
18
http-push.c
18
http-push.c
@@ -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);
|
||||
}
|
||||
|
||||
28
index-pack.c
28
index-pack.c
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
10
merge-file.c
10
merge-file.c
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
8
mktag.c
8
mktag.c
@@ -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;
|
||||
|
||||
9
mktree.c
9
mktree.c
@@ -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);
|
||||
|
||||
60
object.c
60
object.c
@@ -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);
|
||||
|
||||
15
object.h
15
object.h
@@ -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);
|
||||
|
||||
14
pack-check.c
14
pack-check.c
@@ -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);
|
||||
|
||||
16
read-cache.c
16
read-cache.c
@@ -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
2
refs.c
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
306
sha1_file.c
306
sha1_file.c
@@ -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;
|
||||
|
||||
58
sha1_name.c
58
sha1_name.c
@@ -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])
|
||||
|
||||
28
t/t2005-checkout-index-symlinks.sh
Executable file
28
t/t2005-checkout-index-symlinks.sh
Executable 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
Reference in New Issue
Block a user