Merge branch 'master' of git://repo.or.cz/alt-git

This commit is contained in:
Johannes Sixt
2007-08-17 12:24:19 +02:00
104 changed files with 2610 additions and 591 deletions

1
.gitignore vendored
View File

@@ -148,6 +148,7 @@ git-write-tree
git-core-*/?*
gitk-wish
gitweb/gitweb.cgi
test-absolute-path
test-chmtime
test-date
test-delta

View File

@@ -44,6 +44,11 @@ INSTALL?=install
RM ?= rm -f
DOC_REF = origin/man
infodir?=$(prefix)/share/info
MAKEINFO=makeinfo
INSTALL_INFO=install-info
DOCBOOK2X_TEXI=docbook2x-texi
-include ../config.mak.autogen
-include ../config.mak
@@ -67,6 +72,8 @@ man1: $(DOC_MAN1)
man5: $(DOC_MAN5)
man7: $(DOC_MAN7)
info: git.info
install: man
$(INSTALL) -d -m755 $(DESTDIR)$(man1dir)
$(INSTALL) -d -m755 $(DESTDIR)$(man5dir)
@@ -75,6 +82,14 @@ install: man
$(INSTALL) -m644 $(DOC_MAN5) $(DESTDIR)$(man5dir)
$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
install-info: info
$(INSTALL) -d -m755 $(DESTDIR)$(infodir)
$(INSTALL) -m644 git.info $(DESTDIR)$(infodir)
if test -r $(DESTDIR)$(infodir)/dir; then \
$(INSTALL_INFO) --info-dir=$(DESTDIR)$(infodir) git.info ;\
else \
echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
fi
../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
$(MAKE) -C ../ GIT-VERSION-FILE
@@ -104,13 +119,14 @@ cmds_txt = cmds-ancillaryinterrogators.txt \
$(cmds_txt): cmd-list.made
cmd-list.made: cmd-list.perl $(MAN1_TXT)
$(RM) $@
perl ./cmd-list.perl
date >$@
git.7 git.html: git.txt core-intro.txt
clean:
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 howto-index.txt howto/*.html doc.dep
$(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7 *.texi *.texi+ howto-index.txt howto/*.html doc.dep
$(RM) $(cmds_txt) *.made
%.html : %.txt
@@ -138,6 +154,13 @@ XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
git.info: user-manual.xml
$(RM) $@ $*.texi $*.texi+
$(DOCBOOK2X_TEXI) user-manual.xml --to-stdout >$*.texi+
perl fix-texi.perl <$*.texi+ >$*.texi
$(MAKEINFO) --no-split $*.texi
$(RM) $*.texi $*.texi+
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
$(RM) $@+ $@
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+

View File

@@ -0,0 +1,30 @@
GIT v1.5.2.5 Release Notes
==========================
Fixes since v1.5.2.4
--------------------
* Bugfixes
- "git add -u" had a serious data corruption problem in one
special case (when the changes to a subdirectory's files
consist only deletion of files).
- "git add -u <path>" did not work from a subdirectory.
- "git apply" left an empty directory after all its files are
renamed away.
- "git $anycmd foo/bar", when there is a file 'foo' in the
working tree, complained that "git $anycmd foo/bar --" form
should be used to disambiguate between revs and files,
which was completely bogus.
- "git checkout-index" and other commands that checks out
files to the work tree tried unlink(2) on directories,
which is a sane thing to do on sane systems, but not on
Solaris when you are root.
* Documentation Fixes and Updates
- A handful documentation fixes.

View File

@@ -9,6 +9,19 @@ Updates since v1.5.2
* The submodule support has Porcelain layer.
Note that the current submodule support is minimal and this is
deliberately so. A design decision we made is that operations
at the supermodule level do not recurse into submodules by
default. The expectation is that later we would add a
mechanism to tell git which submodules the user is interested
in, and this information might be used to determine the
recursive behaviour of certain commands (e.g. "git checkout"
and "git diff"), but currently we haven't agreed on what that
mechanism should look like. Therefore, if you use submodules,
you would probably need "git submodule update" on the
submodules you care about after running a "git checkout" at
the supermodule level.
* There are a handful pack-objects changes to help you cope better
with repositories with pathologically large blobs in them.
@@ -46,21 +59,21 @@ Updates since v1.5.2
- "git log" learned a new option "--follow", to follow
renaming history of a single file.
- "git-filter-branch" lets you rewrite the revision history of
- "git filter-branch" lets you rewrite the revision history of
specified branches. You can specify a number of filters to
modify the commits, files and trees.
- "git-cvsserver" learned new options (--base-path, --export-all,
--strict-paths) inspired by git-daemon.
- "git cvsserver" learned new options (--base-path, --export-all,
--strict-paths) inspired by "git daemon".
- "git daemon --base-path-relaxed" can help migrating a repository URL
that did not use to use --base-path to use --base-path.
- "git-commit" can use "-t templatefile" option and commit.template
- "git commit" can use "-t templatefile" option and commit.template
configuration variable to prime the commit message given to you in the
editor.
- "git-submodule" command helps you manage the projects from
- "git submodule" command helps you manage the projects from
the superproject that contain them.
- In addition to core.compression configuration option,
@@ -68,15 +81,15 @@ Updates since v1.5.2
independently tweak zlib compression levels used for loose
and packed objects.
- "git-ls-tree -l" shows size of blobs pointed at by the
- "git ls-tree -l" shows size of blobs pointed at by the
tree entries, similar to "/bin/ls -l".
- "git-rev-list" learned --regexp-ignore-case and
- "git rev-list" learned --regexp-ignore-case and
--extended-regexp options to tweak its matching logic used
for --grep fitering.
- "git-describe --contains" is a handier way to call more
obscure command "git-name-rev --tags".
- "git describe --contains" is a handier way to call more
obscure command "git name-rev --tags".
- "git gc --aggressive" tells the command to spend more cycles
to optimize the repository harder.
@@ -112,6 +125,9 @@ Updates since v1.5.2
- "git config" learned NUL terminated output format via -z to
help scripts.
- "git add" learned "--refresh <paths>..." option to selectively refresh
the cached stat information.
- "git init -q" makes the command quieter.
* Updated behavior of existing commands.
@@ -126,9 +142,19 @@ Updates since v1.5.2
of the format ('tgz', 'tbz2' or 'zip'). Please update the
your configuration file accordingly.
- "git clone" uses -l (hardlink files under .git) by default when
cloning locally.
- "git bundle create" can now create a bundle without negative refs,
i.e. "everything since the beginning up to certain points".
- "git diff" (but not the plumbing level "git diff-tree") now
recursively descends into trees by default.
- "git diff" does not show differences that come only from
stat-dirtiness in the form of "diff --git" header anymore. When
generating a textual diff, it shows a warning message at the end.
- The editor to use with many interactive commands can be
overridden with GIT_EDITOR environment variable, or if it
does not exist, with core.editor configuration variable. As
@@ -143,8 +169,16 @@ Updates since v1.5.2
given strings now have shorter abbreviations. -i is for ignore case,
and -E is for extended regexp.
- "git log" learned --log-size to show the number of bytes in
the log message part of the output to help qgit.
- "git svn dcommit" retains local merge information.
- "git svnimport" allows an empty string to be specified as the
trunk/ directory. This is necessary to suck data from a SVN
repository that doe not have trunk/ branches/ and tags/ organization
at all.
- "git config" to set values also honors type flags like --bool
and --int.
@@ -167,7 +201,7 @@ Updates since v1.5.2
and the handcrafted ones the old code created was not
properly formed anyway.
- "git-push" pretends that you immediately fetched back from
- "git push" pretends that you immediately fetched back from
the remote by updating corresponding remote tracking
branches if you have any.
@@ -177,10 +211,10 @@ Updates since v1.5.2
- "git commit --amend" is now compatible with various message source
options such as -m/-C/-c/-F.
- "git-apply --whitespace=strip" removes blank lines added at
- "git apply --whitespace=strip" removes blank lines added at
the end of the file.
- "git-fetch" over git native protocols with "-v" option shows
- "git fetch" over git native protocols with "-v" option shows
connection status, and the IP address of the other end, to
help diagnosing problems.
@@ -195,10 +229,10 @@ Updates since v1.5.2
- "--find-copies-harder" option to diff family can now be
spelled as "-C -C" for brevity.
- "git-mailsplit" (hence "git-am") can read from Maildir
- "git mailsplit" (hence "git am") can read from Maildir
formatted mailboxes.
- "git-cvsserver" does not barf upon seeing "cvs login"
- "git cvsserver" does not barf upon seeing "cvs login"
request.
- "pack-objects" honors "delta" attribute set in
@@ -208,7 +242,7 @@ Updates since v1.5.2
- "new-workdir" script (in contrib) can now be used with a
bare repository.
- "git-mergetool" learned to use gvimdiff.
- "git mergetool" learned to use gvimdiff.
- "gitview" (in contrib) has a better blame interface.
@@ -223,8 +257,8 @@ Updates since v1.5.2
"oneline".
- "git p4import" has been demoted to contrib status. For
a superior option, checkout the git-p4 front end to
git-fast-import (also in contrib). The man page and p4
a superior option, checkout the "git p4" front end to
"git fast-import" (also in contrib). The man page and p4
rpm have been removed as well.
- "git mailinfo" (hence "am") now tries to see if the message
@@ -237,13 +271,15 @@ Updates since v1.5.2
without parameter defined with "func()", not "func(void)")
have been eradicated.
- "git tag" and "git verify-tag" have been rewritten in C.
* Performance Tweaks
- git-pack-objects avoids re-deltification cost by caching
- "git pack-objects" avoids re-deltification cost by caching
small enough delta results it creates while looking for the
best delta candidates.
- git-pack-objects learned a new heuristcs to prefer delta
- "git pack-objects" learned a new heuristcs to prefer delta
that is shallower in depth over the smallest delta
possible. This improves both overall packfile access
performance and packfile density.
@@ -260,6 +296,13 @@ Updates since v1.5.2
- verifying pack contents done by "git fsck --full" got boost
by carefully choosing the order to verify objects in them.
- "git read-tree -m" to read into an already populated index
has been optimized vastly. The effect of this can be seen
when switching branches that have differences in only a
handful paths.
- "git commit paths..." has also been optimized.
Fixes since v1.5.2
------------------

View File

@@ -64,11 +64,11 @@ of lines before or after the line given by <start>.
assigns blame to the lines that were moved down (i.e. A)
to the child commit. With this option, both groups of lines
are blamed on the parent.
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
within a file for it to associate those lines with the parent
commit.
+
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
within a file for it to associate those lines with the parent
commit.
-C|<num>|::
In addition to `-M`, detect lines copied from other
@@ -77,11 +77,11 @@ of lines before or after the line given by <start>.
around across files. When this option is given twice,
the command looks for copies from all other files in the
parent for the commit that creates the file in addition.
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
between files for it to associate those lines with the parent
commit.
+
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
between files for it to associate those lines with the parent
commit.
-h, --help::
Show help message.

View File

@@ -68,6 +68,8 @@ for my $cat (qw(ancillaryinterrogators
}
}
# The following list is sorted with "sort -d" to make it easier
# to find entry in the resulting git.html manual page.
__DATA__
git-add mainporcelain
git-am mainporcelain
@@ -80,9 +82,9 @@ git-blame ancillaryinterrogators
git-branch mainporcelain
git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-checkout-index plumbingmanipulators
git-checkout mainporcelain
git-check-attr purehelpers
git-checkout mainporcelain
git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
git-cherry ancillaryinterrogators
git-cherry-pick mainporcelain
@@ -91,6 +93,7 @@ git-clean mainporcelain
git-clone mainporcelain
git-commit mainporcelain
git-commit-tree plumbingmanipulators
git-config ancillarymanipulators
git-convert-objects ancillarymanipulators
git-count-objects ancillaryinterrogators
git-cvsexportcommit foreignscminterface
@@ -98,9 +101,9 @@ git-cvsimport foreignscminterface
git-cvsserver foreignscminterface
git-daemon synchingrepositories
git-describe mainporcelain
git-diff mainporcelain
git-diff-files plumbinginterrogators
git-diff-index plumbinginterrogators
git-diff mainporcelain
git-diff-tree plumbinginterrogators
git-fast-import ancillarymanipulators
git-fetch mainporcelain
@@ -130,13 +133,13 @@ git-ls-remote plumbinginterrogators
git-ls-tree plumbinginterrogators
git-mailinfo purehelpers
git-mailsplit purehelpers
git-merge mainporcelain
git-merge-base plumbinginterrogators
git-merge-file plumbingmanipulators
git-merge-index plumbingmanipulators
git-merge mainporcelain
git-merge-one-file purehelpers
git-merge-tree ancillaryinterrogators
git-mergetool ancillarymanipulators
git-merge-tree ancillaryinterrogators
git-mktag plumbingmanipulators
git-mktree plumbingmanipulators
git-mv mainporcelain
@@ -157,9 +160,8 @@ git-rebase mainporcelain
git-receive-pack synchelpers
git-reflog ancillarymanipulators
git-relink ancillarymanipulators
git-repack ancillarymanipulators
git-config ancillarymanipulators
git-remote ancillarymanipulators
git-repack ancillarymanipulators
git-request-pull foreignscminterface
git-rerere ancillaryinterrogators
git-reset mainporcelain

15
Documentation/fix-texi.perl Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/perl -w
while (<>) {
if (/^\@setfilename/) {
$_ = "\@setfilename git.info\n";
} elsif (/^\@direntry/) {
print '@dircategory Development
@direntry
* Git: (git). A fast distributed revision control system
@end direntry
'; }
unless (/^\@direntry/../^\@end direntry/) {
print;
}
}

View File

@@ -3,32 +3,37 @@ git-add(1)
NAME
----
git-add - Add file contents to the changeset to be committed next
git-add - Add file contents to the index
SYNOPSIS
--------
'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--] <file>...
'git-add' [-n] [-v] [-f] [--interactive | -i] [-u] [--refresh] [--] <file>...
DESCRIPTION
-----------
All the changed file contents to be committed together in a single set
of changes must be "added" with the 'add' command before using the
'commit' command. This is not only for adding new files. Even modified
files must be added to the set of changes about to be committed.
This command adds the current content of new or modified files to the
index, thus staging that content for inclusion in the next commit.
This command can be performed multiple times before a commit. The added
content corresponds to the state of specified file(s) at the time the
'add' command is used. This means the 'commit' command will not consider
subsequent changes to already added content if it is not added again before
the commit.
The "index" holds a snapshot of the content of the working tree, and it
is this snapshot that is taken as the contents of the next commit. Thus
after making any changes to the working directory, and before running
the commit command, you must use the 'add' command to add any new or
modified files to the index.
The 'git status' command can be used to obtain a summary of what is included
for the next commit.
This command can be performed multiple times before a commit. It only
adds the content of the specified file(s) at the time the add command is
run; if you want subsequent changes included in the next commit, then
you must run 'git add' again to add the new content to the index.
This command can be used to add ignored files with `-f` (force)
option, but they have to be
explicitly and exactly specified from the command line. File globbing
and recursive behaviour do not add ignored files.
The 'git status' command can be used to obtain a summary of which
files have changes that are staged for the next commit.
The 'git add' command will not add ignored files by default. If any
ignored files were explicitly specified on the command line, 'git add'
will fail with a list of ignored files. Ignored files reached by
directory recursion or filename globbing will be silently ignored.
The 'add' command can be used to add ignored files with the `-f`
(force) option.
Please see gitlink:git-commit[1] for alternative ways to add content to a
commit.
@@ -63,6 +68,10 @@ OPTIONS
command line. If no paths are specified, all tracked files are
updated.
\--refresh::
Don't add the file(s), but only refresh their stat()
information in the index.
\--::
This option can be used to separate command-line options from
the list of files, (useful when filenames might be mistaken

View File

@@ -171,6 +171,20 @@ apply.whitespace::
When no `--whitespace` flag is given from the command
line, this configuration item is used as the default.
Submodules
----------
If the patch contains any changes to submodules then gitlink:git-apply[1]
treats these changes as follows.
If --index is specified (explicitly or implicitly), then the submodule
commits must match the index exactly for the patch to apply. If any
of the submodules are checked-out, then these check-outs are completely
ignored, i.e., they are not required to be up-to-date or clean and they
are not updated.
If --index is not specified, then the submodule commits in the patch
are ignored and only the absence of presence of the corresponding
subdirectory is checked and (if possible) updated.
Author
------

View File

@@ -9,7 +9,8 @@ git-clone - Clone a repository into a new directory
SYNOPSIS
--------
[verse]
'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
'git-clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
[--depth <depth>] <repository> [<directory>]
@@ -40,8 +41,19 @@ OPTIONS
this flag bypasses normal "git aware" transport
mechanism and clones the repository by making a copy of
HEAD and everything under objects and refs directories.
The files under .git/objects/ directory are hardlinked
to save space when possible.
The files under `.git/objects/` directory are hardlinked
to save space when possible. This is now the default when
the source repository is specified with `/path/to/repo`
syntax, so it essentially is a no-op option. To force
copying instead of hardlinking (which may be desirable
if you are trying to make a back-up of your repository),
but still avoid the usual "git aware" transport
mechanism, `--no-hardlinks` can be used.
--no-hardlinks::
Optimize the cloning process from a repository on a
local filesystem by copying files under `.git/objects`
directory.
--shared::
-s::

View File

@@ -15,26 +15,27 @@ SYNOPSIS
DESCRIPTION
-----------
Use 'git commit' when you want to record your changes into the repository
along with a log message describing what the commit is about. All changes
to be committed must be explicitly identified using one of the following
methods:
Use 'git commit' to store the current contents of the index in a new
commit along with a log message describing the changes you have made.
The content to be added can be specified in several ways:
1. by using gitlink:git-add[1] to incrementally "add" changes to the
next commit before using the 'commit' command (Note: even modified
index before using the 'commit' command (Note: even modified
files must be "added");
2. by using gitlink:git-rm[1] to identify content removal for the next
commit, again before using the 'commit' command;
2. by using gitlink:git-rm[1] to remove files from the working tree
and the index, again before using the 'commit' command;
3. by directly listing files containing changes to be committed as arguments
to the 'commit' command, in which cases only those files alone will be
considered for the commit;
3. by listing files as arguments to the 'commit' command, in which
case the commit will ignore changes staged in the index, and instead
record the current content of the listed files;
4. by using the -a switch with the 'commit' command to automatically "add"
changes from all known files i.e. files that have already been committed
before, and to automatically "rm" files that have been
removed from the working tree, and perform the actual commit.
4. by using the -a switch with the 'commit' command to automatically
"add" changes from all known files (i.e. all files that are already
listed in the index) and to automatically "rm" files in the index
that have been removed from the working tree, and then perform the
actual commit;
5. by using the --interactive switch with the 'commit' command to decide one
by one which files should be part of the commit, before finalizing the

View File

@@ -64,6 +64,13 @@ include::pretty-options.txt[]
--follow::
Continue listing the history of a file beyond renames.
--log-size::
Before the log message print out its size in bytes. Intended
mainly for porcelain tools consumption. If git is unable to
produce a valid value size is set to zero.
Note that only message is considered, if also a diff is shown
its size is not included.
<paths>...::
Show only commits that affect the specified paths.

View File

@@ -79,7 +79,7 @@ the remote repository.
-f, \--force::
Usually, the command refuses to update a remote ref that is
not a descendant of the local ref used to overwrite it.
not an ancestor of the local ref used to overwrite it.
This flag disables the check. This can cause the
remote repository to lose commits; use it with care.

View File

@@ -130,7 +130,7 @@ the following situation:
then the command
git-rebase --onto topicA~5 topicA~2 topicA
git-rebase --onto topicA~5 topicA~3 topicA
would result in the removal of commits F and G:

View File

@@ -224,7 +224,7 @@ left-to-right.
G H I J
\ / \ /
D E F
\ | / \
\ | / \
\ | / |
\|/ |
B C

View File

@@ -29,8 +29,8 @@ you create one.
The latest stash you created is stored in `$GIT_DIR/refs/stash`; older
stashes are found in the reflog of this reference and can be named using
the usual reflog syntax (e.g. `stash@\{1}` is the most recently
created stash, `stash@\{2}` is the one before it, `stash@\{2.hours.ago}`
the usual reflog syntax (e.g. `stash@\{0}` is the most recently
created stash, `stash@\{1}` is the one before it, `stash@\{2.hours.ago}`
is also possible).
OPTIONS
@@ -45,7 +45,7 @@ save::
list::
List the stashes that you currently have. Each 'stash' is listed
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1} is
with its name (e.g. `stash@\{0}` is the latest stash, `stash@\{1}` is
the one before, etc.), the name of the branch that was current when the
stash was made, and a short description of the commit the stash was
based on.
@@ -61,7 +61,7 @@ show [<stash>]::
stashed state and its original parent. When no `<stash>` is given,
shows the latest one. By default, the command shows the diffstat, but
it will accept any format known to `git-diff` (e.g., `git-stash show
-p stash@\{2}` to view the second most recent stash in patch form).
-p stash@\{1}` to view the second most recent stash in patch form).
apply [<stash>]::

View File

@@ -27,6 +27,13 @@ The command takes the same set of options as `git-commit`; it
shows what would be committed if the same options are given to
`git-commit`.
If any paths have been touched in the working tree (that is,
their modification times have changed) but their contents and
permissions are identical to those in the index file, the command
updates the index file. Running `git-status` can thus speed up
subsequent operations such as `git-diff` if the working tree
contains many paths that have been touched but not modified.
OUTPUT
------

View File

@@ -8,7 +8,7 @@ git-stripspace - Filter out empty lines
SYNOPSIS
--------
'git-stripspace' < <stream>
'git-stripspace' [-s | --strip-comments] < <stream>
DESCRIPTION
-----------
@@ -16,6 +16,9 @@ Remove multiple empty lines, and empty lines at beginning and end.
OPTIONS
-------
-s\|--strip-comments::
In addition to empty lines, also strip lines starting with '#'.
<stream>::
Byte stream to act on.

View File

@@ -435,6 +435,26 @@ Tracking and contributing to an entire Subversion-managed project
# of dcommit/rebase/show-ignore should be the same as above.
------------------------------------------------------------------------
The initial 'git-svn clone' can be quite time-consuming
(especially for large Subversion repositories). If multiple
people (or one person with multiple machines) want to use
git-svn to interact with the same Subversion repository, you can
do the initial 'git-svn clone' to a repository on a server and
have each person clone that repository with 'git clone':
------------------------------------------------------------------------
# Do the initial import on a server
ssh server "cd /pub && git-svn clone http://svn.foo.org/project
# Clone locally
git clone server:/pub/project
# Tell git-svn which branch contains the Subversion commits
git update-ref refs/remotes/git-svn origin/master
# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
git-svn init http://svn.foo.org/project
# Pull the latest changes from Subversion
git-svn rebase
------------------------------------------------------------------------
REBASE VS. PULL/MERGE
---------------------

View File

@@ -12,7 +12,7 @@ SYNOPSIS
'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <name> [<head>]
'git-tag' -d <name>...
'git-tag' [-n [<num>]] -l [<pattern>]
'git-tag' -v <name>
'git-tag' -v <name>...
DESCRIPTION
-----------
@@ -23,7 +23,7 @@ Unless `-f` is given, the tag must not yet exist in
If one of `-a`, `-s`, or `-u <key-id>` is passed, the command
creates a 'tag' object, and requires the tag message. Unless
`-m <msg>` is given, an editor is started for the user to type
`-m <msg>` or `-F <file>` is given, an editor is started for the user to type
in the tag message.
Otherwise just the SHA1 object name of the commit object is
@@ -59,15 +59,17 @@ OPTIONS
Delete existing tags with the given names.
-v::
Verify the gpg signature of given the tag
Verify the gpg signature of the given tag names.
-n <num>::
<num> specifies how many lines from the annotation, if any,
are printed when using -l.
The default is not to print any annotation lines.
If no number is given to `-n`, only the first line is printed.
-l <pattern>::
List tags with names that match the given pattern (or all if no pattern is given).
Typing "git tag" without arguments, also lists all tags.
-m <msg>::
Use the given tag message (instead of prompting)

View File

@@ -3,11 +3,11 @@ git-verify-tag(1)
NAME
----
git-verify-tag - Check the GPG signature of tag
git-verify-tag - Check the GPG signature of tags
SYNOPSIS
--------
'git-verify-tag' <tag>
'git-verify-tag' <tag>...
DESCRIPTION
-----------

View File

@@ -42,9 +42,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
* link:v1.5.2.4/git.html[documentation for release 1.5.2.4]
* link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
* release notes for
link:RelNotes-1.5.2.5.txt[1.5.2.5],
link:RelNotes-1.5.2.4.txt[1.5.2.4],
link:RelNotes-1.5.2.3.txt[1.5.2.3],
link:RelNotes-1.5.2.2.txt[1.5.2.2],
@@ -421,6 +422,22 @@ other
to an empty string or to the value "cat", git will not launch
a pager.
'GIT_SSH'::
If this environment variable is set then gitlink:git-fetch[1]
and gitlink:git-push[1] will use this command instead
of `ssh` when they need to connect to a remote system.
The 'GIT_SSH' command will be given exactly two arguments:
the 'username@host' (or just 'host') from the URL and the
shell command to execute on that remote system.
+
To pass options to the program that you want to list in GIT_SSH
you will need to wrap the program and options into a shell script,
then set GIT_SSH to refer to the shell script.
+
Usually it is easier to configure any desired options through your
personal `.ssh/config` file. Please consult your ssh documentation
for further details.
'GIT_FLUSH'::
If this environment variable is set to "1", then commands such
as git-blame (in incremental mode), git-rev-list, git-log,

View File

@@ -7,7 +7,7 @@ mandir="$2"
SUBDIRECTORY_OK=t
USAGE='<refname> <target directory>'
. git-sh-setup
export GIT_DIR
cd_to_toplevel
test -z "$mandir" && usage
if ! git rev-parse --verify "$head^0" >/dev/null; then
@@ -18,14 +18,14 @@ fi
GIT_INDEX_FILE=`pwd`/.quick-doc.index
export GIT_INDEX_FILE
rm -f "$GIT_INDEX_FILE"
trap 'rm -f "$GIT_INDEX_FILE"' 0
git read-tree $head
git checkout-index -a -f --prefix="$mandir"/
if test -n "$GZ"; then
cd "$mandir"
for i in `git ls-tree -r --name-only $head`
do
gzip < $i > $i.gz && rm $i
done
git ls-tree -r --name-only $head |
xargs printf "$mandir/%s\n" |
xargs gzip -f
fi
rm -f "$GIT_INDEX_FILE"

View File

@@ -15,11 +15,11 @@ to name the remote repository:
- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
===============================================================
SSH is the default transport protocol. You can optionally specify
which user to log-in as, and an alternate, scp-like syntax is also
supported. Both syntaxes support username expansion,
as does the native git protocol. The following three are
identical to the last three above, respectively:
SSH is the default transport protocol over the network. You can
optionally specify which user to log-in as, and an alternate,
scp-like syntax is also supported. Both syntaxes support
username expansion, as does the native git protocol. The following
three are identical to the last three above, respectively:
===============================================================
- {startsb}user@{endsb}host.xz:/path/to/repo.git/
@@ -27,8 +27,12 @@ identical to the last three above, respectively:
- {startsb}user@{endsb}host.xz:path/to/repo.git
===============================================================
To sync with a local directory, use:
To sync with a local directory, you can use:
===============================================================
- /path/to/repo.git/
- file:///path/to/repo.git/
===============================================================
They are mostly equivalent, except when cloning. See
gitlink:git-clone[1] for details.

View File

@@ -1,4 +1,4 @@
Git User's Manual (for version 1.5.1 or newer)
Git User's Manual (for version 1.5.3 or newer)
______________________________________________
@@ -1079,6 +1079,11 @@ $ git diff HEAD # difference between HEAD and working tree; what
$ git status # a brief per-file summary of the above.
-------------------------------------------------
You can also use gitlink:git-gui[1] to create commits, view changes in
the index and the working tree files, and individually select diff hunks
for inclusion in the index (by right-clicking on the diff hunk and
choosing "Stage Hunk For Commit").
[[creating-good-commit-messages]]
Creating good commit messages
-----------------------------
@@ -1484,6 +1489,38 @@ $ git show HEAD^:path/to/file
which will display the given version of the file.
[[interrupted-work]]
Temporarily setting aside work in progress
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While you are in the middle of working on something complicated, you
find an unrelated but obvious and trivial bug. You would like to fix it
before continuing. You can use gitlink:git-stash[1] to save the current
state of your work, and after fixing the bug (or, optionally after doing
so on a different branch and then coming back), unstash the
work-in-progress changes.
------------------------------------------------
$ git stash "work in progress for foo feature"
------------------------------------------------
This command will save your changes away to the `stash`, and
reset your working tree and the index to match the tip of your
current branch. Then you can make your fix as usual.
------------------------------------------------
... edit and test ...
$ git commit -a -m "blorpl: typofix"
------------------------------------------------
After that, you can go back to what you were working on with
`git stash apply`:
------------------------------------------------
$ git stash apply
------------------------------------------------
[[ensuring-good-performance]]
Ensuring good performance
-------------------------
@@ -1667,24 +1704,19 @@ one step:
$ git pull origin master
-------------------------------------------------
In fact, "origin" is normally the default repository to pull from,
and the default branch is normally the HEAD of the remote repository,
so often you can accomplish the above with just
In fact, if you have "master" checked out, then by default "git pull"
merges from the HEAD branch of the origin repository. So often you can
accomplish the above with just a simple
-------------------------------------------------
$ git pull
-------------------------------------------------
See the descriptions of the branch.<name>.remote and branch.<name>.merge
options in gitlink:git-config[1] to learn how to control these defaults
depending on the current branch. Also note that the --track option to
gitlink:git-branch[1] and gitlink:git-checkout[1] can be used to
automatically set the default remote branch to pull from at the time
that a branch is created:
-------------------------------------------------
$ git checkout --track -b maint origin/maint
-------------------------------------------------
More generally, a branch that is created from a remote branch will pull
by default from that branch. See the descriptions of the
branch.<name>.remote and branch.<name>.merge options in
gitlink:git-config[1], and the discussion of the --track option in
gitlink:git-checkout[1], to learn how to control these defaults.
In addition to saving you keystrokes, "git pull" also helps you by
producing a default commit message documenting the branch and
@@ -2479,8 +2511,10 @@ $ gitk origin..mywork &
And browse through the list of patches in the mywork branch using gitk,
applying them (possibly in a different order) to mywork-new using
cherry-pick, and possibly modifying them as you go using commit
--amend.
cherry-pick, and possibly modifying them as you go using commit --amend.
The git-gui[1] command may also help as it allows you to individually
select diff hunks for inclusion in the index (by right-clicking on the
diff hunk and choosing "Stage Hunk for Commit").
Another technique is to use git-format-patch to create a series of
patches, then reset the state to before the patches:

18
INSTALL
View File

@@ -5,8 +5,8 @@ Normally you can just do "make" followed by "make install", and that
will install the git programs in your own ~/bin/ directory. If you want
to do a global install, you can do
$ make prefix=/usr all doc ;# as yourself
# make prefix=/usr install install-doc ;# as root
$ make prefix=/usr all doc info ;# as yourself
# make prefix=/usr install install-doc install-info ;# as root
(or prefix=/usr/local, of course). Just like any program suite
that uses $prefix, the built results have some paths encoded,
@@ -91,9 +91,13 @@ Issues of note:
- To build and install documentation suite, you need to have
the asciidoc/xmlto toolchain. Because not many people are
inclined to install the tools, the default build target
("make all") does _not_ build them. The documentation is
written for AsciiDoc 7, but "make ASCIIDOC8=YesPlease doc"
will let you format with AsciiDoc 8.
("make all") does _not_ build them.
Building and installing the info file additionally requires
makeinfo and docbook2X. Version 0.8.3 is known to work.
The documentation is written for AsciiDoc 7, but "make
ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
Alternatively, pre-formatted documentation are available in
"html" and "man" branches of the git repository itself. For
@@ -116,3 +120,7 @@ Issues of note:
would instead give you a copy of what you see at:
http://www.kernel.org/pub/software/scm/git/docs/
It has been reported that docbook-xsl version 1.72 and 1.73 are
buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
the patch in contrib/patches/docbook-xsl-manpages-charmap.patch

View File

@@ -208,7 +208,6 @@ SCRIPT_SH = \
git-pull.sh git-rebase.sh git-rebase--interactive.sh \
git-repack.sh git-request-pull.sh git-reset.sh \
git-sh-setup.sh \
git-tag.sh git-verify-tag.sh \
git-am.sh \
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
git-merge-resolve.sh git-merge-ours.sh \
@@ -364,12 +363,14 @@ BUILTIN_OBJS = \
builtin-show-branch.o \
builtin-stripspace.o \
builtin-symbolic-ref.o \
builtin-tag.o \
builtin-tar-tree.o \
builtin-unpack-objects.o \
builtin-update-index.o \
builtin-update-ref.o \
builtin-upload-archive.o \
builtin-verify-pack.o \
builtin-verify-tag.o \
builtin-write-tree.o \
builtin-show-ref.o \
builtin-pack-refs.o
@@ -941,6 +942,9 @@ perl/Makefile: perl/Git.pm perl/Makefile.PL GIT-CFLAGS
doc:
$(MAKE) -C Documentation all
info:
$(MAKE) -C Documentation info
TAGS:
$(RM) TAGS
$(FIND) . -name '*.[hcS]' -print | xargs etags -a
@@ -1030,6 +1034,9 @@ endif
install-doc:
$(MAKE) -C Documentation install
install-info:
$(MAKE) -C Documentation install-info
quick-install-doc:
$(MAKE) -C Documentation quick-install

120
attr.c
View File

@@ -257,6 +257,7 @@ static struct attr_stack {
struct attr_stack *prev;
char *origin;
unsigned num_matches;
unsigned alloc;
struct match_attr **attrs;
} *attr_stack;
@@ -287,6 +288,26 @@ static const char *builtin_attr[] = {
NULL,
};
static void handle_attr_line(struct attr_stack *res,
const char *line,
const char *src,
int lineno,
int macro_ok)
{
struct match_attr *a;
a = parse_attr_line(line, src, lineno, macro_ok);
if (!a)
return;
if (res->alloc <= res->num_matches) {
res->alloc = alloc_nr(res->num_matches);
res->attrs = xrealloc(res->attrs,
sizeof(struct match_attr *) *
res->alloc);
}
res->attrs[res->num_matches++] = a;
}
static struct attr_stack *read_attr_from_array(const char **list)
{
struct attr_stack *res;
@@ -294,42 +315,91 @@ static struct attr_stack *read_attr_from_array(const char **list)
int lineno = 0;
res = xcalloc(1, sizeof(*res));
while ((line = *(list++)) != NULL) {
struct match_attr *a;
a = parse_attr_line(line, "[builtin]", ++lineno, 1);
if (!a)
continue;
res->attrs = xrealloc(res->attrs,
sizeof(struct match_attr *) * (res->num_matches + 1));
res->attrs[res->num_matches++] = a;
}
while ((line = *(list++)) != NULL)
handle_attr_line(res, line, "[builtin]", ++lineno, 1);
return res;
}
static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
{
FILE *fp;
FILE *fp = fopen(path, "r");
struct attr_stack *res;
char buf[2048];
int lineno = 0;
res = xcalloc(1, sizeof(*res));
fp = fopen(path, "r");
if (!fp)
return NULL;
res = xcalloc(1, sizeof(*res));
while (fgets(buf, sizeof(buf), fp))
handle_attr_line(res, buf, path, ++lineno, macro_ok);
fclose(fp);
return res;
}
static void *read_index_data(const char *path)
{
int pos, len;
unsigned long sz;
enum object_type type;
void *data;
len = strlen(path);
pos = cache_name_pos(path, len);
if (pos < 0) {
/*
* We might be in the middle of a merge, in which
* case we would read stage #2 (ours).
*/
int i;
for (i = -pos - 1;
(pos < 0 && i < active_nr &&
!strcmp(active_cache[i]->name, path));
i++)
if (ce_stage(active_cache[i]) == 2)
pos = i;
}
if (pos < 0)
return NULL;
data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
if (!data || type != OBJ_BLOB) {
free(data);
return NULL;
}
return data;
}
static struct attr_stack *read_attr(const char *path, int macro_ok)
{
struct attr_stack *res;
char *buf, *sp;
int lineno = 0;
res = read_attr_from_file(path, macro_ok);
if (res)
return res;
while (fgets(buf, sizeof(buf), fp)) {
struct match_attr *a;
res = xcalloc(1, sizeof(*res));
a = parse_attr_line(buf, path, ++lineno, macro_ok);
if (!a)
continue;
res->attrs = xrealloc(res->attrs,
sizeof(struct match_attr *) * (res->num_matches + 1));
res->attrs[res->num_matches++] = a;
/*
* There is no checked out .gitattributes file there, but
* we might have it in the index. We allow operation in a
* sparsely checked out work tree, so read from it.
*/
buf = read_index_data(path);
if (!buf)
return res;
for (sp = buf; *sp; ) {
char *ep;
int more;
for (ep = sp; *ep && *ep != '\n'; ep++)
;
more = (*ep == '\n');
*ep = '\0';
handle_attr_line(res, sp, path, ++lineno, macro_ok);
sp = ep + more;
}
fclose(fp);
free(buf);
return res;
}
@@ -370,13 +440,15 @@ static void bootstrap_attr_stack(void)
elem->prev = attr_stack;
attr_stack = elem;
elem = read_attr_from_file(GITATTRIBUTES_FILE, 1);
elem = read_attr(GITATTRIBUTES_FILE, 1);
elem->origin = strdup("");
elem->prev = attr_stack;
attr_stack = elem;
debug_push(elem);
elem = read_attr_from_file(git_path(INFOATTRIBUTES_FILE), 1);
if (!elem)
elem = xcalloc(1, sizeof(*elem));
elem->origin = NULL;
elem->prev = attr_stack;
attr_stack = elem;
@@ -441,7 +513,7 @@ static void prepare_attr_stack(const char *path, int dirlen)
memcpy(pathbuf + dirlen, "/", 2);
cp = strchr(pathbuf + len + 1, '/');
strcpy(cp + 1, GITATTRIBUTES_FILE);
elem = read_attr_from_file(pathbuf, 0);
elem = read_attr(pathbuf, 0);
*cp = '\0';
elem->origin = strdup(pathbuf);
elem->prev = attr_stack;

View File

@@ -102,6 +102,7 @@ static void update_callback(struct diff_queue_struct *q,
break;
case DIFF_STATUS_DELETED:
remove_file_from_cache(path);
cache_tree_invalidate_path(active_cache_tree, path);
if (verbose)
printf("remove '%s'\n", path);
break;
@@ -109,12 +110,12 @@ static void update_callback(struct diff_queue_struct *q,
}
}
static void update(int verbose, const char **files)
static void update(int verbose, const char *prefix, const char **files)
{
struct rev_info rev;
init_revisions(&rev, "");
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
rev.prune_data = get_pathspec(rev.prefix, files);
rev.prune_data = get_pathspec(prefix, files);
rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = update_callback;
rev.diffopt.format_callback_data = &verbose;
@@ -123,6 +124,23 @@ static void update(int verbose, const char **files)
run_diff_files(&rev, 0);
}
static void refresh(int verbose, const char **pathspec)
{
char *seen;
int i, specs;
for (specs = 0; pathspec[specs]; specs++)
/* nothing */;
seen = xcalloc(specs, 1);
if (read_cache() < 0)
die("index file corrupt");
refresh_index(&the_index, verbose ? 0 : REFRESH_QUIET, pathspec, seen);
for (i = 0; i < specs; i++) {
if (!seen[i])
die("pathspec '%s' did not match any files", pathspec[i]);
}
}
static int git_add_config(const char *var, const char *value)
{
if (!strcmp(var, "core.excludesfile")) {
@@ -143,7 +161,7 @@ static const char ignore_warning[] =
int cmd_add(int argc, const char **argv, const char *prefix)
{
int i, newfd;
int verbose = 0, show_only = 0, ignored_too = 0;
int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
const char **pathspec;
struct dir_struct dir;
int add_interactive = 0;
@@ -191,11 +209,15 @@ int cmd_add(int argc, const char **argv, const char *prefix)
take_worktree_changes = 1;
continue;
}
if (!strcmp(arg, "--refresh")) {
refresh_only = 1;
continue;
}
usage(builtin_add_usage);
}
if (take_worktree_changes) {
update(verbose, argv + i);
update(verbose, prefix, argv + i);
goto finish;
}
@@ -206,6 +228,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
pathspec = get_pathspec(prefix, argv + i);
if (refresh_only) {
refresh(verbose, pathspec);
goto finish;
}
fill_directory(&dir, pathspec, ignored_too);
if (show_only) {

View File

@@ -1984,6 +1984,25 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
return 0;
}
static int read_file_or_gitlink(struct cache_entry *ce, char **buf_p,
unsigned long *size_p)
{
if (!ce)
return 0;
if (S_ISGITLINK(ntohl(ce->ce_mode))) {
*buf_p = xmalloc(100);
*size_p = snprintf(*buf_p, 100,
"Subproject commit %s\n", sha1_to_hex(ce->sha1));
} else {
enum object_type type;
*buf_p = read_sha1_file(ce->sha1, &type, size_p);
if (!*buf_p)
return -1;
}
return 0;
}
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
{
char *buf;
@@ -1994,22 +2013,32 @@ static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *
alloc = 0;
buf = NULL;
if (cached) {
if (ce) {
enum object_type type;
buf = read_sha1_file(ce->sha1, &type, &size);
if (!buf)
if (read_file_or_gitlink(ce, &buf, &size))
return error("read of %s failed", patch->old_name);
alloc = size;
} else if (patch->old_name) {
if (S_ISGITLINK(patch->old_mode)) {
if (ce)
read_file_or_gitlink(ce, &buf, &size);
else {
/*
* There is no way to apply subproject
* patch without looking at the index.
*/
patch->fragments = NULL;
size = 0;
}
}
else {
size = xsize_t(st->st_size);
alloc = size + 8192;
buf = xmalloc(alloc);
if (read_old_data(st, patch->old_name,
&buf, &alloc, &size))
return error("read of %s failed",
patch->old_name);
alloc = size;
}
}
else if (patch->old_name) {
size = xsize_t(st->st_size);
alloc = size + 8192;
buf = xmalloc(alloc);
if (read_old_data(st, patch->old_name, &buf, &alloc, &size))
return error("read of %s failed", patch->old_name);
}
desc.size = size;
desc.alloc = alloc;
@@ -2055,6 +2084,16 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
return 0;
}
static int verify_index_match(struct cache_entry *ce, struct stat *st)
{
if (S_ISGITLINK(ntohl(ce->ce_mode))) {
if (!S_ISDIR(st->st_mode))
return -1;
return 0;
}
return ce_match_stat(ce, st, 1);
}
static int check_patch(struct patch *patch, struct patch *prev_patch)
{
struct stat st;
@@ -2065,8 +2104,14 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
int ok_if_exists;
patch->rejected = 1; /* we will drop this after we succeed */
/*
* Make sure that we do not have local modifications from the
* index when we are looking at the index. Also make sure
* we have the preimage file to be patched in the work tree,
* unless --cached, which tells git to apply only in the index.
*/
if (old_name) {
int changed = 0;
int stat_ret = 0;
unsigned st_mode = 0;
@@ -2096,15 +2141,12 @@ static int check_patch(struct patch *patch, struct patch *prev_patch)
lstat(old_name, &st))
return -1;
}
if (!cached)
changed = ce_match_stat(ce, &st, 1);
if (changed)
if (!cached && verify_index_match(ce, &st))
return error("%s: does not match index",
old_name);
if (cached)
st_mode = ntohl(ce->ce_mode);
}
else if (stat_ret < 0)
} else if (stat_ret < 0)
return error("%s: %s", old_name, strerror(errno));
if (!cached)
@@ -2354,7 +2396,11 @@ static void remove_file(struct patch *patch, int rmdir_empty)
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
if (!cached) {
if (!unlink(patch->old_name) && rmdir_empty) {
if (S_ISGITLINK(patch->old_mode)) {
if (rmdir(patch->old_name))
warning("unable to remove submodule %s",
patch->old_name);
} else if (!unlink(patch->old_name) && rmdir_empty) {
char *name = xstrdup(patch->old_name);
char *end = strrchr(name, '/');
while (end) {
@@ -2382,13 +2428,21 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
memcpy(ce->name, path, namelen);
ce->ce_mode = create_ce_mode(mode);
ce->ce_flags = htons(namelen);
if (!cached) {
if (lstat(path, &st) < 0)
die("unable to stat newly created file %s", path);
fill_stat_cache_info(ce, &st);
if (S_ISGITLINK(mode)) {
const char *s = buf;
if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
die("corrupt patch for subproject %s", path);
} else {
if (!cached) {
if (lstat(path, &st) < 0)
die("unable to stat newly created file %s",
path);
fill_stat_cache_info(ce, &st);
}
if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
die("unable to create backing store for newly created file %s", path);
}
if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
die("unable to create backing store for newly created file %s", path);
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
die("unable to add cache entry for %s", path);
}
@@ -2398,6 +2452,13 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
int fd;
char *nbuf;
if (S_ISGITLINK(mode)) {
struct stat st;
if (!lstat(path, &st) && S_ISDIR(st.st_mode))
return 0;
return mkdir(path, 0777);
}
if (has_symlinks && S_ISLNK(mode))
/* Although buf:size is counted string, it also is NUL
* terminated.
@@ -2508,7 +2569,7 @@ static void write_out_one_result(struct patch *patch, int phase)
* thing: remove the old, write the new
*/
if (phase == 0)
remove_file(patch, 0);
remove_file(patch, patch->is_rename);
if (phase == 1)
create_file(patch);
}

View File

@@ -44,38 +44,21 @@ struct bundle_header {
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++) {
ssize_t 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);
int fd;
long fpos;
FILE *ffd = fopen(path, "rb");
if (fd < 0)
if (!ffd)
return error("could not open '%s'", path);
if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
if (!fgets(buffer, sizeof(buffer), ffd) ||
strcmp(buffer, bundle_signature)) {
close(fd);
fclose(ffd);
return error("'%s' does not look like a v2 bundle file", path);
}
while (read_string(fd, buffer, sizeof(buffer)) > 0
while (fgets(buffer, sizeof(buffer), ffd)
&& buffer[0] != '\n') {
int is_prereq = buffer[0] == '-';
int offset = is_prereq ? 1 : 0;
@@ -97,6 +80,12 @@ static int read_header(const char *path, struct bundle_header *header) {
add_to_ref_list(sha1, isspace(delim) ?
buffer + 41 + offset : "", list);
}
fpos = ftell(ffd);
fclose(ffd);
fd = open(path, O_RDONLY);
if (fd < 0)
return error("could not open '%s'", path);
lseek(fd, fpos, SEEK_SET);
return fd;
}
@@ -200,18 +189,22 @@ static int list_heads(struct bundle_header *header, int argc, const char **argv)
static int create_bundle(struct bundle_header *header, const char *path,
int argc, const char **argv)
{
static struct lock_file lock;
int bundle_fd = -1;
int bundle_to_stdout;
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
const char **argv_pack = xmalloc(5 * sizeof(const char *));
int i, ref_count = 0;
char buffer[1024];
struct rev_info revs;
struct child_process rls;
FILE *rls_fout;
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
if (bundle_fd < 0)
return error("Could not create '%s': %s", path, strerror(errno));
bundle_to_stdout = !strcmp(path, "-");
if (bundle_to_stdout)
bundle_fd = 1;
else
bundle_fd = hold_lock_file_for_update(&lock, path, 1);
/* write signature */
write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
@@ -232,10 +225,11 @@ static int create_bundle(struct bundle_header *header, const char *path,
rls.git_cmd = 1;
if (start_command(&rls))
return -1;
while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) {
rls_fout = fdopen(rls.out, "r");
while (fgets(buffer, sizeof(buffer), rls_fout)) {
unsigned char sha1[20];
if (buffer[0] == '-') {
write_or_die(bundle_fd, buffer, i);
write_or_die(bundle_fd, buffer, strlen(buffer));
if (!get_sha1_hex(buffer + 1, sha1)) {
struct object *object = parse_object(sha1);
object->flags |= UNINTERESTING;
@@ -246,6 +240,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
object->flags |= SHOWN;
}
}
fclose(rls_fout);
if (finish_command(&rls))
return error("rev-list died");
@@ -267,12 +262,49 @@ static int create_bundle(struct bundle_header *header, const char *path,
* Make sure the refs we wrote out is correct; --max-count and
* other limiting options could have prevented all the tips
* from getting output.
*
* Non commit objects such as tags and blobs do not have
* this issue as they are not affected by those extra
* constraints.
*/
if (!(e->item->flags & SHOWN)) {
if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
warning("ref '%s' is excluded by the rev-list options",
e->name);
free(ref);
continue;
}
/*
* If you run "git bundle create bndl v1.0..v2.0", the
* name of the positive ref is "v2.0" but that is the
* commit that is referenced by the tag, and not the tag
* itself.
*/
if (hashcmp(sha1, e->item->sha1)) {
/*
* Is this the positive end of a range expressed
* in terms of a tag (e.g. v2.0 from the range
* "v1.0..v2.0")?
*/
struct commit *one = lookup_commit_reference(sha1);
struct object *obj;
if (e->item == &(one->object)) {
/*
* Need to include e->name as an
* independent ref to the pack-objects
* input, so that the tag is included
* in the output; otherwise we would
* end up triggering "empty bundle"
* error.
*/
obj = parse_object(sha1);
obj->flags |= SHOWN;
add_pending_object(&revs, obj, e->name);
}
free(ref);
continue;
}
ref_count++;
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
write_or_die(bundle_fd, " ", 1);
@@ -308,6 +340,9 @@ static int create_bundle(struct bundle_header *header, const char *path,
}
if (finish_command(&rls))
return error ("pack-objects died");
close(bundle_fd);
if (!bundle_to_stdout)
commit_lock_file(&lock);
return 0;
}

View File

@@ -1,4 +1,5 @@
#include "builtin.h"
#include "cache.h"
#include "attr.h"
#include "quote.h"
@@ -10,6 +11,10 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
struct git_attr_check *check;
int cnt, i, doubledash;
if (read_cache() < 0) {
die("invalid cache");
}
doubledash = -1;
for (i = 1; doubledash < 0 && i < argc; i++) {
if (!strcmp(argv[i], "--"))

View File

@@ -222,6 +222,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
prefix = setup_git_directory_gently(&nongit);
git_config(git_diff_ui_config);
init_revisions(&rev, prefix);
rev.diffopt.skip_stat_unmatch = 1;
if (!setup_diff_no_index(&rev, argc, argv, nongit, prefix))
argc = 0;
@@ -235,6 +236,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
rev.diffopt.allow_external = 1;
rev.diffopt.recursive = 1;
/* If the user asked for our exit code then don't start a
* pager or we would end up reporting its exit code instead.
*/
if (!rev.diffopt.exit_with_status)
setup_pager();
/* Do we have --cached and not have a pending object, then
* default to HEAD by hand. Eek.
*/
@@ -338,5 +345,12 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
ent, ents);
if (rev.diffopt.exit_with_status)
result = rev.diffopt.has_changes;
if ((rev.diffopt.output_format & DIFF_FORMAT_PATCH)
&& (1 < rev.diffopt.skip_stat_unmatch))
printf("Warning: %d path%s touched but unmodified. "
"Consider running git-status.\n",
rev.diffopt.skip_stat_unmatch - 1,
rev.diffopt.skip_stat_unmatch == 2 ? "" : "s");
return result;
}

View File

@@ -586,7 +586,7 @@ static off_t write_one(struct sha1file *f,
static int open_object_dir_tmp(const char *path)
{
snprintf(tmpname, sizeof(tmpname), "%s/%s", get_object_directory(), path);
return mkstemp(tmpname);
return xmkstemp(tmpname);
}
/* forward declaration for write_pack_file */
@@ -612,8 +612,6 @@ static void write_pack_file(void)
f = sha1fd(1, "<stdout>");
} else {
int fd = open_object_dir_tmp("tmp_pack_XXXXXX");
if (fd < 0)
die("unable to create %s: %s\n", tmpname, strerror(errno));
pack_tmp_name = xstrdup(tmpname);
f = sha1fd(fd, pack_tmp_name);
}
@@ -1275,9 +1273,8 @@ struct unpacked {
unsigned depth;
};
static int delta_cacheable(struct unpacked *trg, struct unpacked *src,
unsigned long src_size, unsigned long trg_size,
unsigned long delta_size)
static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
unsigned long delta_size)
{
if (max_delta_cache_size && delta_cache_size + delta_size > max_delta_cache_size)
return 0;
@@ -1399,7 +1396,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
trg_entry->delta_size = delta_size;
trg->depth = src->depth + 1;
if (delta_cacheable(src, trg, src_size, trg_size, delta_size)) {
if (delta_cacheable(src_size, trg_size, delta_size)) {
trg_entry->delta_data = xrealloc(delta_buf, delta_size);
delta_cache_size += trg_entry->delta_size;
} else

View File

@@ -13,14 +13,20 @@
#include "dir.h"
#include "builtin.h"
static struct object_list *trees;
#define MAX_TREES 8
static int nr_trees;
static struct tree *trees[MAX_TREES];
static int list_tree(unsigned char *sha1)
{
struct tree *tree = parse_tree_indirect(sha1);
struct tree *tree;
if (nr_trees >= MAX_TREES)
die("I cannot read more than %d trees", MAX_TREES);
tree = parse_tree_indirect(sha1);
if (!tree)
return -1;
object_list_append(&tree->object, &trees);
trees[nr_trees++] = tree;
return 0;
}
@@ -76,11 +82,10 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
static void prime_cache_tree(void)
{
struct tree *tree = (struct tree *)trees->item;
if (!tree)
if (!nr_trees)
return;
active_cache_tree = cache_tree();
prime_cache_tree_rec(active_cache_tree, tree);
prime_cache_tree_rec(active_cache_tree, trees[0]);
}
@@ -92,6 +97,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, newfd, stage = 0;
unsigned char sha1[20];
struct tree_desc t[MAX_TREES];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
@@ -258,7 +264,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
opts.head_idx = 1;
}
unpack_trees(trees, &opts);
for (i = 0; i < nr_trees; i++) {
struct tree *tree = trees[i];
parse_tree(tree);
init_tree_desc(t+i, tree->buffer, tree->size);
}
unpack_trees(nr_trees, t, &opts);
/*
* When reading only one tree (either the most basic form,
@@ -266,7 +277,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
* valid cache-tree because the index must match exactly
* what came from the tree.
*/
if (trees && trees->item && !opts.prefix && (!opts.merge || (stage == 2))) {
if (nr_trees && !opts.prefix && (!opts.merge || (stage == 2))) {
cache_tree_free(&active_cache_tree);
prime_cache_tree();
}

View File

@@ -76,6 +76,11 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
{
char *buffer;
unsigned long size;
int strip_comments = 0;
if (argc > 1 && (!strcmp(argv[1], "-s") ||
!strcmp(argv[1], "--strip-comments")))
strip_comments = 1;
size = 1024;
buffer = xmalloc(size);
@@ -84,7 +89,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
die("could not read the input");
}
size = stripspace(buffer, size, 0);
size = stripspace(buffer, size, strip_comments);
write_or_die(1, buffer, size);
if (size)
putc('\n', stdout);

460
builtin-tag.c Normal file
View File

@@ -0,0 +1,460 @@
/*
* Builtin "git tag"
*
* Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,
* Carlos Rica <jasampler@gmail.com>
* Based on git-tag.sh and mktag.c by Linus Torvalds.
*/
#include "cache.h"
#include "builtin.h"
#include "refs.h"
#include "tag.h"
#include "run-command.h"
static const char builtin_tag_usage[] =
"git-tag [-n [<num>]] -l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d | -v] [-m <msg> | -F <file>] <tagname> [<head>]";
static char signingkey[1000];
static void launch_editor(const char *path, char **buffer, unsigned long *len)
{
const char *editor, *terminal;
struct child_process child;
const char *args[3];
int fd;
editor = getenv("GIT_EDITOR");
if (!editor && editor_program)
editor = editor_program;
if (!editor)
editor = getenv("VISUAL");
if (!editor)
editor = getenv("EDITOR");
terminal = getenv("TERM");
if (!editor && (!terminal || !strcmp(terminal, "dumb"))) {
fprintf(stderr,
"Terminal is dumb but no VISUAL nor EDITOR defined.\n"
"Please supply the message using either -m or -F option.\n");
exit(1);
}
if (!editor)
editor = "vi";
memset(&child, 0, sizeof(child));
child.argv = args;
args[0] = editor;
args[1] = path;
args[2] = NULL;
if (run_command(&child))
die("There was a problem with the editor %s.", editor);
fd = open(path, O_RDONLY);
if (fd < 0)
die("could not open '%s': %s", path, strerror(errno));
if (read_fd(fd, buffer, len)) {
free(*buffer);
die("could not read message file '%s': %s",
path, strerror(errno));
}
close(fd);
}
struct tag_filter {
const char *pattern;
int lines;
};
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
static int show_reference(const char *refname, const unsigned char *sha1,
int flag, void *cb_data)
{
struct tag_filter *filter = cb_data;
if (!fnmatch(filter->pattern, refname, 0)) {
int i;
unsigned long size;
enum object_type type;
char *buf, *sp, *eol;
size_t len;
if (!filter->lines) {
printf("%s\n", refname);
return 0;
}
printf("%-15s ", refname);
sp = buf = read_sha1_file(sha1, &type, &size);
if (!buf)
return 0;
if (!size) {
free(buf);
return 0;
}
/* skip header */
while (sp + 1 < buf + size &&
!(sp[0] == '\n' && sp[1] == '\n'))
sp++;
/* only take up to "lines" lines, and strip the signature */
for (i = 0, sp += 2;
i < filter->lines && sp < buf + size &&
prefixcmp(sp, PGP_SIGNATURE "\n");
i++) {
if (i)
printf("\n ");
eol = memchr(sp, '\n', size - (sp - buf));
len = eol ? eol - sp : size - (sp - buf);
fwrite(sp, len, 1, stdout);
if (!eol)
break;
sp = eol + 1;
}
putchar('\n');
free(buf);
}
return 0;
}
static int list_tags(const char *pattern, int lines)
{
struct tag_filter filter;
char *newpattern;
if (pattern == NULL)
pattern = "";
/* prepend/append * to the shell pattern: */
newpattern = xmalloc(strlen(pattern) + 3);
sprintf(newpattern, "*%s*", pattern);
filter.pattern = newpattern;
filter.lines = lines;
for_each_tag_ref(show_reference, (void *) &filter);
free(newpattern);
return 0;
}
typedef int (*each_tag_name_fn)(const char *name, const char *ref,
const unsigned char *sha1);
static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
{
const char **p;
char ref[PATH_MAX];
int had_error = 0;
unsigned char sha1[20];
for (p = argv; *p; p++) {
if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
>= sizeof(ref)) {
error("tag name too long: %.*s...", 50, *p);
had_error = 1;
continue;
}
if (!resolve_ref(ref, sha1, 1, NULL)) {
error("tag '%s' not found.", *p);
had_error = 1;
continue;
}
if (fn(*p, ref, sha1))
had_error = 1;
}
return had_error;
}
static int delete_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
if (delete_ref(ref, sha1))
return 1;
printf("Deleted tag '%s'\n", name);
return 0;
}
static int verify_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
const char *argv_verify_tag[] = {"git-verify-tag",
"-v", "SHA1_HEX", NULL};
argv_verify_tag[2] = sha1_to_hex(sha1);
if (run_command_v_opt(argv_verify_tag, 0))
return error("could not verify the tag '%s'", name);
return 0;
}
static ssize_t do_sign(char *buffer, size_t size, size_t max)
{
struct child_process gpg;
const char *args[4];
char *bracket;
int len;
if (!*signingkey) {
if (strlcpy(signingkey, git_committer_info(1),
sizeof(signingkey)) > sizeof(signingkey) - 1)
return error("committer info too long.");
bracket = strchr(signingkey, '>');
if (bracket)
bracket[1] = '\0';
}
memset(&gpg, 0, sizeof(gpg));
gpg.argv = args;
gpg.in = -1;
gpg.out = -1;
args[0] = "gpg";
args[1] = "-bsau";
args[2] = signingkey;
args[3] = NULL;
if (start_command(&gpg))
return error("could not run gpg.");
write_or_die(gpg.in, buffer, size);
close(gpg.in);
gpg.close_in = 0;
len = read_in_full(gpg.out, buffer + size, max - size);
finish_command(&gpg);
if (len == max - size)
return error("could not read the entire signature from gpg.");
return size + len;
}
static const char tag_template[] =
"\n"
"#\n"
"# Write a tag message\n"
"#\n";
static int git_tag_config(const char *var, const char *value)
{
if (!strcmp(var, "user.signingkey")) {
if (!value)
die("user.signingkey without value");
if (strlcpy(signingkey, value, sizeof(signingkey))
>= sizeof(signingkey))
die("user.signingkey value too long");
return 0;
}
return git_default_config(var, value);
}
#define MAX_SIGNATURE_LENGTH 1024
/* message must be NULL or allocated, it will be reallocated and freed */
static void create_tag(const unsigned char *object, const char *tag,
char *message, int sign, unsigned char *result)
{
enum object_type type;
char header_buf[1024], *buffer = NULL;
int header_len, max_size;
unsigned long size = 0;
type = sha1_object_info(object, NULL);
if (type <= OBJ_NONE)
die("bad object type.");
header_len = snprintf(header_buf, sizeof(header_buf),
"object %s\n"
"type %s\n"
"tag %s\n"
"tagger %s\n\n",
sha1_to_hex(object),
typename(type),
tag,
git_committer_info(1));
if (header_len > sizeof(header_buf) - 1)
die("tag header too big.");
if (!message) {
char *path;
int fd;
/* write the template message before editing: */
path = xstrdup(git_path("TAG_EDITMSG"));
fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
if (fd < 0)
die("could not create file '%s': %s",
path, strerror(errno));
write_or_die(fd, tag_template, strlen(tag_template));
close(fd);
launch_editor(path, &buffer, &size);
unlink(path);
free(path);
}
else {
buffer = message;
size = strlen(message);
}
size = stripspace(buffer, size, 1);
if (!message && !size)
die("no tag message?");
/* insert the header and add the '\n' if needed: */
max_size = header_len + size + (sign ? MAX_SIGNATURE_LENGTH : 0) + 1;
buffer = xrealloc(buffer, max_size);
if (size)
buffer[size++] = '\n';
memmove(buffer + header_len, buffer, size);
memcpy(buffer, header_buf, header_len);
size += header_len;
if (sign) {
size = do_sign(buffer, size, max_size);
if (size < 0)
die("unable to sign the tag");
}
if (write_sha1_file(buffer, size, tag_type, result) < 0)
die("unable to write tag file");
free(buffer);
}
int cmd_tag(int argc, const char **argv, const char *prefix)
{
unsigned char object[20], prev[20];
int annotate = 0, sign = 0, force = 0, lines = 0;
char *message = NULL;
char ref[PATH_MAX];
const char *object_ref, *tag;
int i;
struct ref_lock *lock;
git_config(git_tag_config);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (arg[0] != '-')
break;
if (!strcmp(arg, "-a")) {
annotate = 1;
continue;
}
if (!strcmp(arg, "-s")) {
annotate = 1;
sign = 1;
continue;
}
if (!strcmp(arg, "-f")) {
force = 1;
continue;
}
if (!strcmp(arg, "-n")) {
if (i + 1 == argc || *argv[i + 1] == '-')
/* no argument */
lines = 1;
else
lines = isdigit(*argv[++i]) ?
atoi(argv[i]) : 1;
continue;
}
if (!strcmp(arg, "-m")) {
annotate = 1;
i++;
if (i == argc)
die("option -m needs an argument.");
if (message)
die("only one -F or -m option is allowed.");
message = xstrdup(argv[i]);
continue;
}
if (!strcmp(arg, "-F")) {
unsigned long len;
int fd;
annotate = 1;
i++;
if (i == argc)
die("option -F needs an argument.");
if (message)
die("only one -F or -m option is allowed.");
if (!strcmp(argv[i], "-"))
fd = 0;
else {
fd = open(argv[i], O_RDONLY);
if (fd < 0)
die("could not open '%s': %s",
argv[i], strerror(errno));
}
len = 1024;
message = xmalloc(len);
if (read_fd(fd, &message, &len)) {
free(message);
die("cannot read %s", argv[i]);
}
continue;
}
if (!strcmp(arg, "-u")) {
annotate = 1;
sign = 1;
i++;
if (i == argc)
die("option -u needs an argument.");
if (strlcpy(signingkey, argv[i], sizeof(signingkey))
>= sizeof(signingkey))
die("argument to option -u too long");
continue;
}
if (!strcmp(arg, "-l"))
return list_tags(argv[i + 1], lines);
if (!strcmp(arg, "-d"))
return for_each_tag_name(argv + i + 1, delete_tag);
if (!strcmp(arg, "-v"))
return for_each_tag_name(argv + i + 1, verify_tag);
usage(builtin_tag_usage);
}
if (i == argc) {
if (annotate)
usage(builtin_tag_usage);
return list_tags(NULL, lines);
}
tag = argv[i++];
object_ref = i < argc ? argv[i] : "HEAD";
if (i + 1 < argc)
die("too many params");
if (get_sha1(object_ref, object))
die("Failed to resolve '%s' as a valid ref.", object_ref);
if (snprintf(ref, sizeof(ref), "refs/tags/%s", tag) > sizeof(ref) - 1)
die("tag name too long: %.*s...", 50, tag);
if (check_ref_format(ref))
die("'%s' is not a valid tag name.", tag);
if (!resolve_ref(ref, prev, 1, NULL))
hashclr(prev);
else if (!force)
die("tag '%s' already exists", tag);
if (annotate)
create_tag(object, tag, message, sign, object);
lock = lock_any_ref_for_update(ref, prev, 0);
if (!lock)
die("%s: cannot lock the ref", ref);
if (write_ref_sha1(lock, object, NULL) < 0)
die("%s: cannot update the ref", ref);
return 0;
}

View File

@@ -86,9 +86,15 @@ static int process_lstat_error(const char *path, int err)
static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
{
int option, size = cache_entry_size(len);
struct cache_entry *ce = xcalloc(1, size);
int option, size;
struct cache_entry *ce;
/* Was the old index entry already up-to-date? */
if (old && !ce_stage(old) && !ce_match_stat(old, st, 0))
return 0;
size = cache_entry_size(len);
ce = xcalloc(1, size);
memcpy(ce->name, path, len);
ce->ce_flags = htons(len);
fill_stat_cache_info(ce, st);

110
builtin-verify-tag.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Builtin "git verify-tag"
*
* Copyright (c) 2007 Carlos Rica <jasampler@gmail.com>
*
* Based on git-verify-tag.sh
*/
#include "cache.h"
#include "builtin.h"
#include "tag.h"
#include "run-command.h"
#include <signal.h>
static const char builtin_verify_tag_usage[] =
"git-verify-tag [-v|--verbose] <tag>...";
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
static int run_gpg_verify(const char *buf, unsigned long size, int verbose)
{
struct child_process gpg;
const char *args_gpg[] = {"gpg", "--verify", "FILE", "-", NULL};
char path[PATH_MAX], *eol;
size_t len;
int fd, ret;
fd = git_mkstemp(path, PATH_MAX, ".git_vtag_tmpXXXXXX");
if (fd < 0)
return error("could not create temporary file '%s': %s",
path, strerror(errno));
if (write_in_full(fd, buf, size) < 0)
return error("failed writing temporary file '%s': %s",
path, strerror(errno));
close(fd);
/* find the length without signature */
len = 0;
while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) {
eol = memchr(buf + len, '\n', size - len);
len += eol ? eol - (buf + len) + 1 : size - len;
}
if (verbose)
write_in_full(1, buf, len);
memset(&gpg, 0, sizeof(gpg));
gpg.argv = args_gpg;
gpg.in = -1;
gpg.out = 1;
args_gpg[2] = path;
if (start_command(&gpg))
return error("could not run gpg.");
write_in_full(gpg.in, buf, len);
close(gpg.in);
gpg.close_in = 0;
ret = finish_command(&gpg);
unlink(path);
return ret;
}
static int verify_tag(const char *name, int verbose)
{
enum object_type type;
unsigned char sha1[20];
char *buf;
unsigned long size;
int ret;
if (get_sha1(name, sha1))
return error("tag '%s' not found.", name);
type = sha1_object_info(sha1, NULL);
if (type != OBJ_TAG)
return error("%s: cannot verify a non-tag object of type %s.",
name, typename(type));
buf = read_sha1_file(sha1, &type, &size);
if (!buf)
return error("%s: unable to read file.", name);
ret = run_gpg_verify(buf, size, verbose);
free(buf);
return ret;
}
int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
git_config(git_default_config);
if (argc == 1)
usage(builtin_verify_tag_usage);
if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")) {
verbose = 1;
i++;
}
/* sometimes the program was terminated because this signal
* was received in the process of writing the gpg input: */
signal(SIGPIPE, SIG_IGN);
while (i < argc)
if (verify_tag(argv[i++], verbose))
had_error = 1;
return had_error;
}

View File

@@ -70,12 +70,14 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
extern int cmd_tag(int argc, const char **argv, const char *prefix);
extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
extern int cmd_version(int argc, const char **argv, const char *prefix);
extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
extern int cmd_write_tree(int argc, const char **argv, const char *prefix);

View File

@@ -173,7 +173,7 @@ extern struct index_state the_index;
#define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
#define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
#define refresh_cache(flags) refresh_index(&the_index, flags)
#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
#define ce_match_stat(ce, st, really) ie_match_stat(&the_index, (ce), (st), (really))
#define ce_modified(ce, st, really) ie_modified(&the_index, (ce), (st), (really))
#endif
@@ -258,6 +258,7 @@ extern int index_name_pos(struct index_state *, const char *name, int namelen);
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
#define ADD_CACHE_SKIP_DFCHECK 4 /* Ok to skip DF conflict checks */
#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern int remove_index_entry_at(struct index_state *, int pos);
@@ -277,7 +278,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
extern int refresh_index(struct index_state *, unsigned int flags);
extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
struct lock_file {
struct lock_file *next;
@@ -574,6 +575,8 @@ extern char *pager_program;
extern int pager_in_use;
extern int pager_use_color;
extern char *editor_program;
/* base85 */
int decode_85(char *dst, const char *line, int linelen);
void encode_85(char *buf, const unsigned char *data, int bytes);

View File

@@ -427,6 +427,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.editor")) {
editor_program = xstrdup(value);
return 0;
}
/* Add other config variables here and to Documentation/config.txt. */
return 0;
}

View File

@@ -146,6 +146,8 @@ static enum protocol get_protocol(const char *name)
return PROTO_SSH;
if (!strcmp(name, "ssh+git"))
return PROTO_SSH;
if (!strcmp(name, "file"))
return PROTO_LOCAL;
die("I don't handle protocol '%s'", name);
}
@@ -515,14 +517,14 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
end = host;
path = strchr(end, c);
if (c == ':') {
/* host must have at least 2 chars to catch DOS C:/path */
if (path && path - end > 1) {
/* host must have at least 2 chars to catch DOS C:/path */
if (path && path - end > 1) {
if (c == ':') {
protocol = PROTO_SSH;
*path++ = '\0';
} else
path = host;
}
}
} else
path = end;
if (!path || !*path)
die("No path specified. See 'man git-pull' for valid url syntax");

View File

@@ -972,6 +972,11 @@ _git_show ()
__git_complete_file
}
_git_stash ()
{
__gitcomp 'list show apply clear'
}
_git ()
{
local i c=1 command __git_dir
@@ -1028,6 +1033,7 @@ _git ()
shortlog) _git_shortlog ;;
show) _git_show ;;
show-branch) _git_log ;;
stash) _git_stash ;;
whatchanged) _git_log ;;
*) COMPREPLY=() ;;
esac
@@ -1073,6 +1079,7 @@ complete -o default -o nospace -F _git_remote git-remote
complete -o default -o nospace -F _git_reset git-reset
complete -o default -o nospace -F _git_shortlog git-shortlog
complete -o default -o nospace -F _git_show git-show
complete -o default -o nospace -F _git_stash git-stash
complete -o default -o nospace -F _git_log git-show-branch
complete -o default -o nospace -F _git_log git-whatchanged

View File

@@ -912,10 +912,12 @@ Return the list of files that haven't been handled."
(defun git-setup-diff-buffer (buffer)
"Setup a buffer for displaying a diff."
(with-current-buffer buffer
(diff-mode)
(goto-char (point-min))
(setq buffer-read-only t))
(let ((dir default-directory))
(with-current-buffer buffer
(diff-mode)
(goto-char (point-min))
(setq default-directory dir)
(setq buffer-read-only t)))
(display-buffer buffer)
(shrink-window-if-larger-than-buffer))
@@ -965,7 +967,13 @@ Return the list of files that haven't been handled."
(defun git-diff-file-idiff ()
"Perform an interactive diff on the current file."
(interactive)
(error "Interactive diffs not implemented yet."))
(let ((files (git-marked-files-state 'added 'deleted 'modified)))
(unless (eq 1 (length files))
(error "Cannot perform an interactive diff on multiple files."))
(let* ((filename (car (git-get-filenames files)))
(buff1 (find-file-noselect filename))
(buff2 (git-run-command-buffer (concat filename ".~HEAD~") "cat-file" "blob" (concat "HEAD:" filename))))
(ediff-buffers buff1 buff2))))
(defun git-log-file ()
"Display a log of changes to the marked file(s)."

View File

@@ -390,6 +390,30 @@ class P4Submit(Command):
return result
def prepareSubmitTemplate(self):
# remove lines in the Files section that show changes to files outside the depot path we're committing into
template = ""
inFilesSection = False
for line in read_pipe_lines("p4 change -o"):
if inFilesSection:
if line.startswith("\t"):
# path starts and ends with a tab
path = line[1:]
lastTab = path.rfind("\t")
if lastTab != -1:
path = path[:lastTab]
if not path.startswith(self.depotPath):
continue
else:
inFilesSection = False
else:
if line.startswith("Files:"):
inFilesSection = True
template += line
return template
def applyCommit(self, id):
if self.directSubmit:
print "Applying local change in working directory/index"
@@ -467,7 +491,7 @@ class P4Submit(Command):
logMessage = logMessage.replace("\n", "\r\n")
logMessage = logMessage.strip()
template = read_pipe("p4 change -o")
template = self.prepareSubmitTemplate()
if self.interactive:
submitTemplate = self.prepareLogMessage(template, logMessage)
@@ -558,24 +582,24 @@ class P4Submit(Command):
return False
[upstream, settings] = findUpstreamBranchPoint()
depotPath = settings['depot-paths'][0]
self.depotPath = settings['depot-paths'][0]
if len(self.origin) == 0:
self.origin = upstream
if self.verbose:
print "Origin branch is " + self.origin
if len(depotPath) == 0:
if len(self.depotPath) == 0:
print "Internal error: cannot locate perforce depot path from existing branches"
sys.exit(128)
self.clientPath = p4Where(depotPath)
self.clientPath = p4Where(self.depotPath)
if len(self.clientPath) == 0:
print "Error: Cannot locate perforce checkout of %s in client view" % depotPath
print "Error: Cannot locate perforce checkout of %s in client view" % self.depotPath
sys.exit(128)
print "Perforce checkout for depot path %s located at %s" % (depotPath, self.clientPath)
print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
self.oldWorkingDirectory = os.getcwd()
if self.directSubmit:
@@ -839,16 +863,20 @@ class P4Sync(Command):
if file["action"] == "delete":
self.gitStream.write("D %s\n" % relPath)
else:
mode = 644
if file["type"].startswith("x"):
mode = 755
data = file['data']
mode = "644"
if file["type"].startswith("x"):
mode = "755"
elif file["type"] == "symlink":
mode = "120000"
# p4 print on a symlink contains "target\n", so strip it off
data = data[:-1]
if self.isWindows and file["type"].endswith("text"):
data = data.replace("\r\n", "\n")
self.gitStream.write("M %d inline %s\n" % (mode, relPath))
self.gitStream.write("M %s inline %s\n" % (mode, relPath))
self.gitStream.write("data %s\n" % len(data))
self.gitStream.write(data)
self.gitStream.write("\n")
@@ -1294,7 +1322,7 @@ class P4Sync(Command):
for line in output:
changeNum = line.split(" ")[1]
changes.append(changeNum)
changes.append(int(changeNum))
changes.sort()

View File

@@ -102,6 +102,8 @@ my ($this_user) = getpwuid $<; # REAL_USER_ID
my $repository_name;
my %user_committer;
my @allow_rules;
my @path_rules;
my %diff_cache;
sub deny ($) {
print STDERR "-Deny- $_[0]\n" if $debug;
@@ -118,22 +120,36 @@ sub info ($) {
print STDERR "-Info- $_[0]\n" if $debug;
}
sub parse_config ($$) {
my ($data, $fn) = @_;
info "Loading $fn";
open(I,'-|','git',"--git-dir=$acl_git",'cat-file','blob',$fn);
sub git_value (@) {
open(T,'-|','git',@_); local $_ = <T>; chop; close T; $_;
}
sub match_string ($$) {
my ($acl_n, $ref) = @_;
($acl_n eq $ref)
|| ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
|| ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:);
}
sub parse_config ($$$$) {
my $data = shift;
local $ENV{GIT_DIR} = shift;
my $br = shift;
my $fn = shift;
info "Loading $br:$fn";
open(I,'-|','git','cat-file','blob',"$br:$fn");
my $section = '';
while (<I>) {
chomp;
if (/^\s*$/ || /^\s*#/) {
} elsif (/^\[([a-z]+)\]$/i) {
$section = $1;
$section = lc $1;
} elsif (/^\[([a-z]+)\s+"(.*)"\]$/i) {
$section = "$1.$2";
$section = join('.',lc $1,$2);
} elsif (/^\s*([a-z][a-z0-9]+)\s*=\s*(.*?)\s*$/i) {
push @{$data->{"$section.$1"}}, $2;
push @{$data->{join('.',$section,lc $1)}}, $2;
} else {
deny "bad config file line $. in $fn";
deny "bad config file line $. in $br:$fn";
}
}
close I;
@@ -202,9 +218,40 @@ sub check_committers (@) {
}
}
sub git_value (@) {
open(T,'-|','git',@_); local $_ = <T>; chop; close T;
$_;
sub load_diff ($) {
my $base = shift;
my $d = $diff_cache{$base};
unless ($d) {
local $/ = "\0";
my %this_diff;
if ($base =~ /^0{40}$/) {
open(T,'-|','git','ls-tree',
'-r','--name-only','-z',
$new) or return undef;
while (<T>) {
chop;
$this_diff{$_} = 'A';
}
close T or return undef;
} else {
open(T,'-|','git','diff-tree',
'-r','--name-status','-z',
$base,$new) or return undef;
while (<T>) {
my $op = $_;
chop $op;
my $path = <T>;
chop $path;
$this_diff{$path} = $op;
}
close T or return undef;
}
$d = \%this_diff;
$diff_cache{$base} = $d;
}
return $d;
}
deny "No GIT_DIR inherited from caller" unless $git_dir;
@@ -231,14 +278,52 @@ $op = 'U' if ($op eq 'R'
&& $ref =~ m,^heads/,
&& $old eq git_value('merge-base',$old,$new));
# Load the user's ACL file.
# Load the user's ACL file. Expand groups (user.memberof) one level.
{
my %data = ('user.committer' => []);
parse_config(\%data, "$acl_branch:users/$this_user.acl");
parse_config(\%data,$acl_git,$acl_branch,"external/$repository_name.acl");
%data = (
'user.committer' => $data{'user.committer'},
'user.memberof' => [],
);
parse_config(\%data,$acl_git,$acl_branch,"users/$this_user.acl");
%user_committer = map {$_ => $_} @{$data{'user.committer'}};
my $rules = $data{"repository.$repository_name.allow"} || [];
my $rule_key = "repository.$repository_name.allow";
my $rules = $data{$rule_key} || [];
foreach my $group (@{$data{'user.memberof'}}) {
my %g;
parse_config(\%g,$acl_git,$acl_branch,"groups/$group.acl");
my $group_rules = $g{$rule_key};
push @$rules, @$group_rules if $group_rules;
}
RULE:
foreach (@$rules) {
if (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
while (/\${user\.([a-z][a-zA-Z0-9]+)}/) {
my $k = lc $1;
my $v = $data{"user.$k"};
next RULE unless defined $v;
next RULE if @$v != 1;
next RULE unless defined $v->[0];
s/\${user\.$k}/$v->[0]/g;
}
if (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)\s+diff\s+([^\s]+)$/) {
my ($ops, $pth, $ref, $bst) = ($1, $2, $3, $4);
$ops =~ s/ //g;
$pth =~ s/\\\\/\\/g;
$ref =~ s/\\\\/\\/g;
push @path_rules, [$ops, $pth, $ref, $bst];
} elsif (/^([AMD ]+)\s+of\s+([^\s]+)\s+for\s+([^\s]+)$/) {
my ($ops, $pth, $ref) = ($1, $2, $3);
$ops =~ s/ //g;
$pth =~ s/\\\\/\\/g;
$ref =~ s/\\\\/\\/g;
push @path_rules, [$ops, $pth, $ref, $old];
} elsif (/^([CDRU ]+)\s+for\s+([^\s]+)$/) {
my $ops = $1;
my $ref = $2;
$ops =~ s/ //g;
@@ -272,13 +357,65 @@ foreach my $acl_entry (@allow_rules) {
next unless $acl_ops =~ /^[CDRU]+$/; # Uhh.... shouldn't happen.
next unless $acl_n;
next unless $op =~ /^[$acl_ops]$/;
next unless match_string $acl_n, $ref;
grant "Allowed by: $acl_ops for $acl_n"
if (
($acl_n eq $ref)
|| ($acl_n =~ m,/$, && substr($ref,0,length $acl_n) eq $acl_n)
|| ($acl_n =~ m,^\^, && $ref =~ m:$acl_n:)
);
# Don't test path rules on branch deletes.
#
grant "Allowed by: $acl_ops for $acl_n" if $op eq 'D';
# Aggregate matching path rules; allow if there aren't
# any matching this ref.
#
my %pr;
foreach my $p_entry (@path_rules) {
my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
next unless $p_ref;
push @{$pr{$p_bst}}, $p_entry if match_string $p_ref, $ref;
}
grant "Allowed by: $acl_ops for $acl_n" unless %pr;
# Allow only if all changes against a single base are
# allowed by file path rules.
#
my @bad;
foreach my $p_bst (keys %pr) {
my $diff_ref = load_diff $p_bst;
deny "Cannot difference trees." unless ref $diff_ref;
my %fd = %$diff_ref;
foreach my $p_entry (@{$pr{$p_bst}}) {
my ($p_ops, $p_n, $p_ref, $p_bst) = @$p_entry;
next unless $p_ops =~ /^[AMD]+$/;
next unless $p_n;
foreach my $f_n (keys %fd) {
my $f_op = $fd{$f_n};
next unless $f_op;
next unless $f_op =~ /^[$p_ops]$/;
delete $fd{$f_n} if match_string $p_n, $f_n;
}
last unless %fd;
}
if (%fd) {
push @bad, [$p_bst, \%fd];
} else {
# All changes relative to $p_bst were allowed.
#
grant "Allowed by: $acl_ops for $acl_n diff $p_bst";
}
}
foreach my $bad_ref (@bad) {
my ($p_bst, $fd) = @$bad_ref;
print STDERR "\n";
print STDERR "Not allowed to make the following changes:\n";
print STDERR "(base: $p_bst)\n";
foreach my $f_n (sort keys %$fd) {
print STDERR " $fd->{$f_n} $f_n\n";
}
}
deny "You are not permitted to $op $ref";
}
close A;
deny "You are not permitted to $op $ref";

View File

@@ -0,0 +1,21 @@
From: Ismail Dönmez <ismail@pardus.org.tr>
Trying to build the documentation with docbook-xsl 1.73 may result in
the following error. This patch fixes it.
$ xmlto -m callouts.xsl man git-add.xml
runtime error: file
file:///usr/share/sgml/docbook/xsl-stylesheets-1.73.0/manpages/other.xsl line
129 element call-template
The called template 'read-character-map' was not found.
--- docbook-xsl-1.73.0/manpages/docbook.xsl.manpages-charmap 2007-07-23 16:24:23.000000000 +0100
+++ docbook-xsl-1.73.0/manpages/docbook.xsl 2007-07-23 16:25:16.000000000 +0100
@@ -37,6 +37,7 @@
<xsl:include href="lists.xsl"/>
<xsl:include href="endnotes.xsl"/>
<xsl:include href="table.xsl"/>
+ <xsl:include href="../common/charmap.xsl"/>
<!-- * we rename the following just to avoid using params with "man" -->
<!-- * prefixes in the table.xsl stylesheet (because that stylesheet -->

View File

@@ -189,6 +189,7 @@ static int handle_diff_files_args(struct rev_info *revs,
!strcmp(argv[1], "--no-index")) {
revs->max_count = -2;
revs->diffopt.exit_with_status = 1;
revs->diffopt.no_index = 1;
}
else if (!strcmp(argv[1], "-q"))
*silent = 1;
@@ -204,8 +205,10 @@ static int handle_diff_files_args(struct rev_info *revs,
*/
read_cache();
if (!is_in_index(revs->diffopt.paths[0]) ||
!is_in_index(revs->diffopt.paths[1]))
!is_in_index(revs->diffopt.paths[1])) {
revs->max_count = -2;
revs->diffopt.no_index = 1;
}
}
/*
@@ -293,6 +296,7 @@ int setup_diff_no_index(struct rev_info *revs,
else
revs->diffopt.paths = argv + argc - 2;
revs->diffopt.nr_paths = 2;
revs->diffopt.no_index = 1;
revs->max_count = -2;
return 0;
}
@@ -304,7 +308,7 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
if (handle_diff_files_args(revs, argc, argv, &silent_on_removed))
return -1;
if (revs->max_count == -2) {
if (revs->diffopt.no_index) {
if (revs->diffopt.nr_paths != 2)
return error("need two files/directories with --no-index");
if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],

53
diff.c
View File

@@ -3138,11 +3138,64 @@ static void diffcore_apply_filter(const char *filter)
*q = outq;
}
static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
{
int i;
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
outq.queue = NULL;
outq.nr = outq.alloc = 0;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
/*
* 1. Entries that come from stat info dirtyness
* always have both sides (iow, not create/delete),
* one side of the object name is unknown, with
* the same mode and size. Keep the ones that
* do not match these criteria. They have real
* differences.
*
* 2. At this point, the file is known to be modified,
* with the same mode and size, and the object
* name of one side is unknown. Need to inspect
* the identical contents.
*/
if (!DIFF_FILE_VALID(p->one) || /* (1) */
!DIFF_FILE_VALID(p->two) ||
(p->one->sha1_valid && p->two->sha1_valid) ||
(p->one->mode != p->two->mode) ||
diff_populate_filespec(p->one, 1) ||
diff_populate_filespec(p->two, 1) ||
(p->one->size != p->two->size) ||
diff_populate_filespec(p->one, 0) || /* (2) */
diff_populate_filespec(p->two, 0) ||
memcmp(p->one->data, p->two->data, p->one->size))
diff_q(&outq, p);
else {
/*
* The caller can subtract 1 from skip_stat_unmatch
* to determine how many paths were dirty only
* due to stat info mismatch.
*/
if (!diffopt->no_index)
diffopt->skip_stat_unmatch++;
diff_free_filepair(p);
}
}
free(q->queue);
*q = outq;
}
void diffcore_std(struct diff_options *options)
{
if (options->quiet)
return;
if (options->skip_stat_unmatch && !options->find_copies_harder)
diffcore_skip_stat_unmatch(options);
if (options->break_opt != -1)
diffcore_break(options->break_opt);
if (options->detect_rename)

2
diff.h
View File

@@ -60,11 +60,13 @@ struct diff_options {
color_diff_words:1,
has_changes:1,
quiet:1,
no_index:1,
allow_external:1,
exit_with_status:1;
int context;
int break_opt;
int detect_rename;
int skip_stat_unmatch;
int line_termination;
int output_format;
int pickaxe_opts;

19
entry.c
View File

@@ -112,6 +112,16 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
if (!new)
return error("git-checkout-index: unable to read sha1 file of %s (%s)",
path, sha1_to_hex(ce->sha1));
/*
* Convert from git internal format to working tree format
*/
buf = convert_to_working_tree(ce->name, new, &size);
if (buf) {
free(new);
new = buf;
}
if (to_tempfile) {
strcpy(path, ".merge_file_XXXXXX");
fd = mkstemp(path);
@@ -123,15 +133,6 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
path, strerror(errno));
}
/*
* Convert from git internal format to working tree format
*/
buf = convert_to_working_tree(ce->name, new, &size);
if (buf) {
free(new);
new = buf;
}
wrote = write_in_full(fd, new, size);
close(fd);
free(new);

View File

@@ -33,6 +33,7 @@ size_t delta_base_cache_limit = 16 * 1024 * 1024;
char *pager_program;
int pager_in_use;
int pager_use_color = 1;
char *editor_program;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
/* This is set by setup_git_dir_gently() and/or git_default_config() */

View File

@@ -663,9 +663,7 @@ static void start_packfile(void)
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_pack_XXXXXX", get_object_directory());
pack_fd = mkstemp(tmpfile);
if (pack_fd < 0)
die("Can't create %s: %s", tmpfile, strerror(errno));
pack_fd = xmkstemp(tmpfile);
p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
strcpy(p->pack_name, tmpfile);
p->pack_fd = pack_fd;
@@ -727,9 +725,7 @@ static char *create_index(void)
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_idx_XXXXXX", get_object_directory());
idx_fd = mkstemp(tmpfile);
if (idx_fd < 0)
die("Can't create %s: %s", tmpfile, strerror(errno));
idx_fd = xmkstemp(tmpfile);
f = sha1fd(idx_fd, tmpfile);
sha1write(f, array, 256 * sizeof(int));
SHA1_Init(&ctx);

View File

@@ -103,7 +103,8 @@ It does not apply to blobs recorded in its index."
}
prec=4
dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= resolvemsg=
dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary=
resolvemsg= resume=
git_apply_opt=
while case "$#" in 0) break;; esac

View File

@@ -87,7 +87,7 @@ Perhaps git-update-server-info needs to be run there?"
quiet=
local=no
use_local=no
use_local_hardlink=yes
local_shared=no
unset template
no_checkout=
@@ -108,9 +108,13 @@ while
no_checkout=yes ;;
*,--na|*,--nak|*,--nake|*,--naked|\
*,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;;
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
*,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local)
use_local_hardlink=yes ;;
*,--no-h|*,--no-ha|*,--no-har|*,--no-hard|*,--no-hardl|\
*,--no-hardli|*,--no-hardlin|*,--no-hardlink|*,--no-hardlinks)
use_local_hardlink=no ;;
*,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared)
local_shared=yes; use_local=yes ;;
local_shared=yes; ;;
1,--template) usage ;;
*,--template)
shift; template="--template=$1" ;;
@@ -211,7 +215,12 @@ else
GIT_DIR="$D/.git"
fi &&
export GIT_DIR &&
git-init $quiet ${template+"$template"} || usage
GIT_CONFIG="$GIT_DIR/config" git-init $quiet ${template+"$template"} || usage
if test -n "$bare"
then
GIT_CONFIG="$GIT_DIR/config" git config core.bare true
fi
if test -n "$reference"
then
@@ -249,34 +258,36 @@ fi
rm -f "$GIT_DIR/CLONE_HEAD"
# We do local magic only when the user tells us to.
case "$local,$use_local" in
yes,yes)
case "$local" in
yes)
( cd "$repo/objects" ) ||
die "-l flag seen but repository '$repo' is not local."
die "cannot chdir to local '$repo/objects'."
case "$local_shared" in
no)
# See if we can hardlink and drop "l" if not.
sample_file=$(cd "$repo" && \
/usr/bin/find objects -type f -print | sed -e 1q)
# objects directory should not be empty since we are cloning!
test -f "$repo/$sample_file" || exit
l=
if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
then
l=l
fi &&
rm -f "$GIT_DIR/objects/sample" &&
cd "$repo" &&
/usr/bin/find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
;;
yes)
mkdir -p "$GIT_DIR/objects/info"
echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
;;
esac
if test "$local_shared" = yes
then
mkdir -p "$GIT_DIR/objects/info"
echo "$repo/objects" >>"$GIT_DIR/objects/info/alternates"
else
l= &&
if test "$use_local_hardlink" = yes
then
# See if we can hardlink and drop "l" if not.
sample_file=$(cd "$repo" && \
/usr/bin/find objects -type f -print | sed -e 1q)
# objects directory should not be empty because
# we are cloning!
test -f "$repo/$sample_file" || exit
if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
then
rm -f "$GIT_DIR/objects/sample"
l=l
else
echo >&2 "Warning: -l asked but cannot hardlink to $repo"
fi
fi &&
cd "$repo" &&
/usr/bin/find objects -depth -print | cpio -pumd$l "$GIT_DIR/" || exit 1
fi
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
;;
*)

View File

@@ -314,6 +314,16 @@ static inline FILE *xfdopen(int fd, const char *mode)
return stream;
}
static inline int xmkstemp(char *template)
{
int fd;
fd = mkstemp(template);
if (fd < 0)
die("Unable to create temporary file: %s", strerror(errno));
return fd;
}
static inline size_t xsize_t(off_t len)
{
return (size_t)len;

View File

@@ -1196,6 +1196,7 @@ sub req_ci
$log->info("Lockless commit start, basing commit on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
$ENV{GIT_WORK_TREE} = ".";
$ENV{GIT_INDEX_FILE} = $file_index;
# Remember where the head was at the beginning.
@@ -1721,6 +1722,7 @@ sub req_annotate
$log->info("Temp checkoutdir creation successful, basing annotate session work on '$tmpdir', index file is '$file_index'");
$ENV{GIT_DIR} = $state->{CVSROOT} . "/";
$ENV{GIT_WORK_TREE} = ".";
$ENV{GIT_INDEX_FILE} = $file_index;
chdir $tmpdir;

View File

@@ -170,13 +170,6 @@ do
esac
done < "$tempdir"/backup-refs
case "$GIT_DIR" in
/*)
;;
*)
GIT_DIR="$(pwd)/../../$GIT_DIR"
;;
esac
export GIT_DIR GIT_WORK_TREE=.
# These refs should be updated if their heads were rewritten

View File

@@ -8,13 +8,7 @@ USAGE='[--start] [--stop] [--restart]
. git-sh-setup
case "$GIT_DIR" in
/*)
fqgitdir="$GIT_DIR" ;;
*)
fqgitdir="$PWD/$GIT_DIR" ;;
esac
fqgitdir="$GIT_DIR"
local="`git config --bool --get instaweb.local`"
httpd="`git config --get instaweb.httpd`"
browser="`git config --get instaweb.browser`"

View File

@@ -137,7 +137,7 @@ my $compose_filename = ".msg.$$";
# Variables we fill in automatically, or via prompting:
my (@to,@cc,@initial_cc,@bcclist,@xh,
$initial_reply_to,$initial_subject,@files,$from,$compose,$time);
$initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
my $smtp_server;
my $envelope_sender;
@@ -179,7 +179,7 @@ if (!@bcclist or !$bcclist[0]) {
# Begin by accumulating all the variables (defined above), that we will end up
# needing, first, from the command line:
my $rc = GetOptions("from=s" => \$from,
my $rc = GetOptions("sender|from=s" => \$sender,
"in-reply-to=s" => \$initial_reply_to,
"subject=s" => \$initial_subject,
"to=s" => \@to,
@@ -216,8 +216,8 @@ foreach my $entry (@bcclist) {
# Now, let's fill any that aren't set in with defaults:
my ($author) = $repo->ident_person('author');
my ($committer) = $repo->ident_person('committer');
my ($repoauthor) = $repo->ident_person('author');
my ($repocommitter) = $repo->ident_person('committer');
my %aliases;
my @alias_files = $repo->config('sendemail.aliasesfile');
@@ -254,17 +254,17 @@ if (@alias_files and $aliasfiletype and defined $parse_alias{$aliasfiletype}) {
}
}
($from) = expand_aliases($from) if defined $from;
($sender) = expand_aliases($sender) if defined $sender;
my $prompting = 0;
if (!defined $from) {
$from = $author || $committer;
if (!defined $sender) {
$sender = $repoauthor || $repocommitter;
do {
$_ = $term->readline("Who should the emails appear to be from? [$from] ");
$_ = $term->readline("Who should the emails appear to be from? [$sender] ");
} while (!defined $_);
$from = $_ if ($_);
print "Emails will be sent from: ", $from, "\n";
$sender = $_ if ($_);
print "Emails will be sent from: ", $sender, "\n";
$prompting++;
}
@@ -289,7 +289,7 @@ sub expand_aliases {
}
@to = expand_aliases(@to);
@to = (map { sanitize_address_rfc822($_) } @to);
@to = (map { sanitize_address($_) } @to);
@initial_cc = expand_aliases(@initial_cc);
@bcclist = expand_aliases(@bcclist);
@@ -330,7 +330,7 @@ if ($compose) {
# effort to have it be unique
open(C,">",$compose_filename)
or die "Failed to open for writing $compose_filename: $!";
print C "From $from # This line is ignored.\n";
print C "From $sender # This line is ignored.\n";
printf C "Subject: %s\n\n", $initial_subject;
printf C <<EOT;
GIT: Please enter your email below.
@@ -408,8 +408,8 @@ sub extract_valid_address {
# check for a local address:
return $address if ($address =~ /^($local_part_regexp)$/);
$address =~ s/^\s*<(.*)>\s*$/$1/;
if ($have_email_valid) {
$address =~ s/^\s*<(.*)>\s*$/$1/;
return scalar Email::Valid->address($address);
} else {
# less robust/correct than the monster regexp in Email::Valid,
@@ -433,11 +433,11 @@ sub make_message_id
my $date = time;
my $pseudo_rand = int (rand(4200));
my $du_part;
for ($from, $committer, $author) {
$du_part = extract_valid_address($_);
last if ($du_part ne '');
for ($sender, $repocommitter, $repoauthor) {
$du_part = extract_valid_address(sanitize_address($_));
last if (defined $du_part and $du_part ne '');
}
if ($du_part eq '') {
if (not defined $du_part or $du_part eq '') {
use Sys::Hostname qw();
$du_part = 'user@' . Sys::Hostname::hostname();
}
@@ -459,22 +459,41 @@ sub unquote_rfc2047 {
return "$_";
}
# If an address contains a . in the name portion, the name must be quoted.
sub sanitize_address_rfc822
# use the simplest quoting being able to handle the recipient
sub sanitize_address
{
my ($recipient) = @_;
my ($recipient_name) = ($recipient =~ /^(.*?)\s+</);
if ($recipient_name && $recipient_name =~ /\./ && $recipient_name !~ /^".*"$/) {
my ($name, $addr) = ($recipient =~ /^(.*?)(\s+<.*)/);
$recipient = "\"$name\"$addr";
my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
if (not $recipient_name) {
return "$recipient";
}
return $recipient;
# if recipient_name is already quoted, do nothing
if ($recipient_name =~ /^(".*"|=\?utf-8\?q\?.*\?=)$/) {
return $recipient;
}
# rfc2047 is needed if a non-ascii char is included
if ($recipient_name =~ /[^[:ascii:]]/) {
$recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
$recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/;
}
# double quotes are needed if specials or CTLs are included
elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
$recipient_name =~ s/(["\\\r])/\\$1/;
$recipient_name = "\"$recipient_name\"";
}
return "$recipient_name $recipient_addr";
}
sub send_message
{
my @recipients = unique_email_list(@to);
@cc = (map { sanitize_address_rfc822($_) } @cc);
@cc = (map { sanitize_address($_) } @cc);
my $to = join (",\n\t", @recipients);
@recipients = unique_email_list(@recipients,@cc,@bcclist);
@recipients = (map { extract_valid_address($_) } @recipients);
@@ -489,10 +508,10 @@ sub send_message
if ($cc ne '') {
$ccline = "\nCc: $cc";
}
$from = sanitize_address_rfc822($from);
my $sanitized_sender = sanitize_address($sender);
make_message_id();
my $header = "From: $from
my $header = "From: $sanitized_sender
To: $to${ccline}
Subject: $subject
Date: $date
@@ -509,7 +528,7 @@ X-Mailer: git-send-email $gitversion
}
my @sendmail_parameters = ('-i', @recipients);
my $raw_from = $from;
my $raw_from = $sanitized_sender;
$raw_from = $envelope_sender if (defined $envelope_sender);
$raw_from = extract_valid_address($raw_from);
unshift (@sendmail_parameters,
@@ -546,7 +565,7 @@ X-Mailer: git-send-email $gitversion
} else {
print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
}
print "From: $from\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
print "From: $sanitized_sender\nSubject: $subject\nCc: $cc\nTo: $to\n\n";
if ($smtp) {
print "Result: ", $smtp->code, ' ',
($smtp->message =~ /\n([^\n]+\n)$/s), "\n";
@@ -563,7 +582,7 @@ $subject = $initial_subject;
foreach my $t (@files) {
open(F,"<",$t) or die "can't open file $t";
my $author_not_sender = undef;
my $author = undef;
@cc = @initial_cc;
@xh = ();
my $input_format = undef;
@@ -585,12 +604,11 @@ foreach my $t (@files) {
$subject = $1;
} elsif (/^(Cc|From):\s+(.*)$/) {
if (unquote_rfc2047($2) eq $from) {
$from = $2;
if (unquote_rfc2047($2) eq $sender) {
next if ($suppress_from);
}
elsif ($1 eq 'From') {
$author_not_sender = $2;
$author = unquote_rfc2047($2);
}
printf("(mbox) Adding cc: %s from line '%s'\n",
$2, $_) unless $quiet;
@@ -634,9 +652,8 @@ foreach my $t (@files) {
}
}
close F;
if (defined $author_not_sender) {
$author_not_sender = unquote_rfc2047($author_not_sender);
$message = "From: $author_not_sender\n\n$message";
if (defined $author) {
$message = "From: $author\n\n$message";
}

View File

@@ -116,6 +116,16 @@ then
exit $exit
}
else
GIT_DIR=$(git rev-parse --git-dir) || exit
GIT_DIR=$(git rev-parse --git-dir) || {
exit=$?
echo >&2 "Failed to find a valid git directory."
exit $exit
}
fi
test -n "$GIT_DIR" && GIT_DIR=$(cd "$GIT_DIR" && pwd) || {
echo >&2 "Unable to determine absolute path of git directory"
exit 1
}
: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}

View File

@@ -3501,11 +3501,17 @@ sub log_use_color {
sub git_svn_log_cmd {
my ($r_min, $r_max, @args) = @_;
my $head = 'HEAD';
my (@files, @log_opts);
foreach my $x (@args) {
last if $x eq '--';
next unless ::verify_ref("$x^0");
$head = $x;
last;
if ($x eq '--' || @files) {
push @files, $x;
} else {
if (::verify_ref("$x^0")) {
$head = $x;
} else {
push @log_opts, $x;
}
}
}
my ($url, $rev, $uuid, $gs) = ::working_head_info($head);
@@ -3515,13 +3521,13 @@ sub git_svn_log_cmd {
push @cmd, '-r' unless $non_recursive;
push @cmd, qw/--raw --name-status/ if $verbose;
push @cmd, '--color' if log_use_color();
return @cmd unless defined $r_max;
if ($r_max == $r_min) {
push @cmd, @log_opts;
if (defined $r_max && $r_max == $r_min) {
push @cmd, '--max-count=1';
if (my $c = $gs->rev_db_get($r_max)) {
push @cmd, $c;
}
} else {
} elsif (defined $r_max) {
my ($c_min, $c_max);
$c_max = $gs->rev_db_get($r_max);
$c_min = $gs->rev_db_get($r_min);
@@ -3537,7 +3543,7 @@ sub git_svn_log_cmd {
push @cmd, $c_min;
}
}
return @cmd;
return (@cmd, @files);
}
# adapted from pager.c
@@ -3702,7 +3708,7 @@ sub cmd_show_log {
}
config_pager();
@args = (git_svn_log_cmd($r_min, $r_max, @args), @args);
@args = git_svn_log_cmd($r_min, $r_max, @args);
my $log = command_output_pipe(@args);
run_pager();
my (@k, $c, $d, $stat);

View File

@@ -49,7 +49,7 @@ getopts("A:b:C:dDFhiI:l:mM:o:rs:t:T:SP:R:uv") or usage();
usage if $opt_h;
my $tag_name = $opt_t || "tags";
my $trunk_name = $opt_T || "trunk";
my $trunk_name = defined $opt_T ? $opt_T : "trunk";
my $branch_name = $opt_b || "branches";
my $project_name = $opt_P || "";
$project_name = "/" . $project_name if ($project_name);

7
git.c
View File

@@ -319,7 +319,8 @@ static void handle_internal_command(int argc, const char **argv)
{ "branch", cmd_branch, RUN_SETUP },
{ "bundle", cmd_bundle },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "checkout-index", cmd_checkout_index,
RUN_SETUP | NEED_WORK_TREE},
{ "check-ref-format", cmd_check_ref_format },
{ "check-attr", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },
{ "cherry", cmd_cherry, RUN_SETUP },
@@ -328,7 +329,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "config", cmd_config },
{ "count-objects", cmd_count_objects, RUN_SETUP },
{ "describe", cmd_describe, RUN_SETUP },
{ "diff", cmd_diff, USE_PAGER },
{ "diff", cmd_diff },
{ "diff-files", cmd_diff_files },
{ "diff-index", cmd_diff_index, RUN_SETUP },
{ "diff-tree", cmd_diff_tree, RUN_SETUP },
@@ -372,11 +373,13 @@ static void handle_internal_command(int argc, const char **argv)
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "stripspace", cmd_stripspace },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP },
{ "tar-tree", cmd_tar_tree },
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP },
{ "update-ref", cmd_update_ref, RUN_SETUP },
{ "upload-archive", cmd_upload_archive },
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version },
{ "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
{ "write-tree", cmd_write_tree, RUN_SETUP },

128
gitk
View File

@@ -296,7 +296,7 @@ proc readcommit {id} {
proc updatecommits {} {
global viewdata curview phase displayorder
global children commitrow selectedline thickerline
global children commitrow selectedline thickerline showneartags
if {$phase ne {}} {
stop_rev_list
@@ -313,7 +313,9 @@ proc updatecommits {} {
catch {unset viewdata($n)}
readrefs
changedrefs
regetallcommits
if {$showneartags} {
getallcommits
}
showview $n
}
@@ -427,7 +429,7 @@ proc readrefs {} {
lappend idotherrefs($id) $name
}
}
close $refd
catch {close $refd}
set mainhead {}
set mainheadid {}
catch {
@@ -823,8 +825,13 @@ proc makewindow {} {
pack .ctop -fill both -expand 1
bindall <1> {selcanvline %W %x %y}
#bindall <B1-Motion> {selcanvline %W %x %y}
bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
if {[tk windowingsystem] == "win32"} {
bind . <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D }
bind $ctext <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D ; break }
} else {
bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
}
bindall <2> "canvscan mark %W %x %y"
bindall <B2-Motion> "canvscan dragto %W %x %y"
bindkey <Home> selfirstline
@@ -879,6 +886,7 @@ proc makewindow {} {
bind $cflist <1> {sel_flist %W %x %y; break}
bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
set maincursor [. cget -cursor]
set textcursor [$ctext cget -cursor]
@@ -916,6 +924,32 @@ proc makewindow {} {
-command cobranch
$headctxmenu add command -label "Remove this branch" \
-command rmbranch
global flist_menu
set flist_menu .flistctxmenu
menu $flist_menu -tearoff 0
$flist_menu add command -label "Highlight this too" \
-command {flist_hl 0}
$flist_menu add command -label "Highlight this only" \
-command {flist_hl 1}
}
# Windows sends all mouse wheel events to the current focused window, not
# the one where the mouse hovers, so bind those events here and redirect
# to the correct window
proc windows_mousewheel_redirector {W X Y D} {
global canv canv2 canv3
set w [winfo containing -displayof $W $X $Y]
if {$w ne ""} {
set u [expr {$D < 0 ? 5 : -5}]
if {$w == $canv || $w == $canv2 || $w == $canv3} {
allcanvs yview scroll $u units
} else {
catch {
$w yview scroll $u units
}
}
}
}
# mouse-2 makes all windows scan vertically, but only the one
@@ -955,8 +989,8 @@ proc bindkey {ev script} {
# set the focus back to the toplevel for any click outside
# the entry widgets
proc click {w} {
global entries
foreach e $entries {
global ctext entries
foreach e [concat $entries $ctext] {
if {$w == $e} return
}
focus .
@@ -1500,6 +1534,33 @@ proc sel_flist {w x y} {
}
}
proc pop_flist_menu {w X Y x y} {
global ctext cflist cmitmode flist_menu flist_menu_file
global treediffs diffids
set l [lindex [split [$w index "@$x,$y"] "."] 0]
if {$l <= 1} return
if {$cmitmode eq "tree"} {
set e [linetoelt $l]
if {[string index $e end] eq "/"} return
} else {
set e [lindex $treediffs($diffids) [expr {$l-2}]]
}
set flist_menu_file $e
tk_popup $flist_menu $X $Y
}
proc flist_hl {only} {
global flist_menu_file highlight_files
set x [shellquote $flist_menu_file]
if {$only || $highlight_files eq {}} {
set highlight_files $x
} else {
append highlight_files " " $x
}
}
# Functions for adding and removing shell-type quoting
proc shellquote {str} {
@@ -2837,17 +2898,12 @@ proc layoutrows {row endrow last} {
set offs [lindex $rowoffsets $row]
while {$row < $endrow} {
set id [lindex $displayorder $row]
set oldolds {}
set newolds {}
set nev [expr {[llength $idlist] - $maxwidth + 1}]
foreach p [lindex $parentlist $row] {
if {![info exists idinlist($p)]} {
lappend newolds $p
} elseif {!$idinlist($p)} {
lappend oldolds $p
if {![info exists idinlist($p)] || !$idinlist($p)} {
incr nev
}
}
set nev [expr {[llength $idlist] + [llength $newolds]
+ [llength $oldolds] - $maxwidth + 1}]
if {$nev > 0} {
if {!$last &&
$row + $uparrowlen + $mingaplen >= $commitidx($curview)} break
@@ -2866,12 +2922,22 @@ proc layoutrows {row endrow last} {
if {[incr nev -1] <= 0} break
continue
}
set rowchk($id) [expr {$row + $r}]
set rowchk($i) [expr {$row + $r}]
}
}
lset rowidlist $row $idlist
lset rowoffsets $row $offs
}
set oldolds {}
set newolds {}
foreach p [lindex $parentlist $row] {
if {![info exists idinlist($p)]} {
lappend newolds $p
} elseif {!$idinlist($p)} {
lappend oldolds $p
}
set idinlist($p) 1
}
set col [lsearch -exact $idlist $id]
if {$col < 0} {
set col [llength $idlist]
@@ -2917,12 +2983,10 @@ proc layoutrows {row endrow last} {
lset offs $col {}
}
foreach i $newolds {
set idinlist($i) 1
set idrowranges($i) $id
}
incr col $l
foreach oid $oldolds {
set idinlist($oid) 1
set idlist [linsert $idlist $col $oid]
set offs [linsert $offs $col $o]
makeuparrow $oid $col $row $o
@@ -2963,7 +3027,7 @@ proc layouttail {} {
set col [expr {[llength $idlist] - 1}]
set id [lindex $idlist $col]
addextraid $id $row
unset idinlist($id)
catch {unset idinlist($id)}
lappend idrowranges($id) $id
lappend rowrangelist $idrowranges($id)
unset idrowranges($id)
@@ -4566,6 +4630,7 @@ proc sellastline {} {
proc selnextline {dir} {
global selectedline
focus .
if {![info exists selectedline]} return
set l [expr {$selectedline + $dir}]
unmarkmatches
@@ -4646,6 +4711,7 @@ proc godo {elt} {
proc goback {} {
global history historyindex
focus .
if {$historyindex > 1} {
incr historyindex -1
@@ -4659,6 +4725,7 @@ proc goback {} {
proc goforw {} {
global history historyindex
focus .
if {$historyindex < [llength $history]} {
set cmd [lindex $history $historyindex]
@@ -6135,17 +6202,13 @@ proc rmbranch {} {
proc getallcommits {} {
global allcommits allids nbmp nextarc seeds
set allids {}
set nbmp 0
set nextarc 0
set allcommits 0
set seeds {}
regetallcommits
}
# Called when the graph might have changed
proc regetallcommits {} {
global allcommits seeds
if {![info exists allcommits]} {
set allids {}
set nbmp 0
set nextarc 0
set allcommits 0
set seeds {}
}
set cmd [concat | git rev-list --all --parents]
foreach id $seeds {
@@ -7576,7 +7639,10 @@ catch {source ~/.gitk}
font create optionfont -family sans-serif -size -12
# check that we can find a .git directory somewhere...
set gitdir [gitdir]
if {[catch {set gitdir [gitdir]}]} {
show_error {} . "Cannot find a git repository here."
exit 1
}
if {![file isdirectory $gitdir]} {
show_error {} . "Cannot find the git directory \"$gitdir\"."
exit 1

View File

@@ -5366,7 +5366,7 @@ sub git_feed {
# log/feed of current (HEAD) branch, log of given branch, history of file/directory
my $head = $hash || 'HEAD';
my @commitlist = parse_commits($head, 150);
my @commitlist = parse_commits($head, 150, 0, undef, $file_name);
my %latest_commit;
my %latest_date;

View File

@@ -191,11 +191,11 @@ static const char *env_hint =
"\n"
"Run\n"
"\n"
" git config user.email \"you@email.com\"\n"
" git config user.name \"Your Name\"\n"
" git config --global user.email \"you@email.com\"\n"
" git config --global user.name \"Your Name\"\n"
"\n"
"To set the identity in this repository.\n"
"Add --global to set your account\'s default\n"
"to set your account\'s default identity.\n"
"Omit --global to set the identity only in this repository.\n"
"\n";
const char *fmt_ident(const char *name, const char *email,

View File

@@ -114,7 +114,7 @@ static const char *open_pack_file(const char *pack_name)
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_pack_XXXXXX", get_object_directory());
output_fd = mkstemp(tmpfile);
output_fd = xmkstemp(tmpfile);
pack_name = xstrdup(tmpfile);
} else
output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);

View File

@@ -295,6 +295,9 @@ void show_log(struct rev_info *opt, const char *sep)
if (opt->add_signoff)
len = append_signoff(&msgbuf, &msgbuf_len, len,
opt->add_signoff);
if (opt->show_log_size)
printf("log size %i\n", len);
printf("%s%s%s", msgbuf, extra, sep);
free(msgbuf);
}

View File

@@ -216,13 +216,19 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
*/
static int index_only = 0;
static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree)
{
parse_tree(tree);
init_tree_desc(desc, tree->buffer, tree->size);
}
static int git_merge_trees(int index_only,
struct tree *common,
struct tree *head,
struct tree *merge)
{
int rc;
struct object_list *trees = NULL;
struct tree_desc t[3];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
@@ -234,11 +240,11 @@ static int git_merge_trees(int index_only,
opts.head_idx = 2;
opts.fn = threeway_merge;
object_list_append(&common->object, &trees);
object_list_append(&head->object, &trees);
object_list_append(&merge->object, &trees);
init_tree_desc_from_tree(t+0, common);
init_tree_desc_from_tree(t+1, head);
init_tree_desc_from_tree(t+2, merge);
rc = unpack_trees(trees, &opts);
rc = unpack_trees(3, t, &opts);
cache_tree_free(&active_cache_tree);
return rc;
}
@@ -671,6 +677,26 @@ struct ll_merge_driver {
/*
* Built-in low-levels
*/
static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
const char *path_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
mmbuffer_t *result)
{
/*
* The tentative merge result is "ours" for the final round,
* or common ancestor for an internal merge. Still return
* "conflicted merge" status.
*/
mmfile_t *stolen = index_only ? orig : src1;
result->ptr = stolen->ptr;
result->size = stolen->size;
stolen->ptr = NULL;
return 1;
}
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
const char *path_unused,
mmfile_t *orig,
@@ -681,10 +707,15 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
xpparam_t xpp;
if (buffer_is_binary(orig->ptr, orig->size) ||
buffer_is_binary(src1->ptr, src1->size) ||
buffer_is_binary(src2->ptr, src2->size))
return error("Cannot merge binary files: %s vs. %s\n",
buffer_is_binary(src1->ptr, src1->size) ||
buffer_is_binary(src2->ptr, src2->size)) {
warning("Cannot merge binary files: %s vs. %s\n",
name1, name2);
return ll_binary_merge(drv_unused, path_unused,
orig, src1, name1,
src2, name2,
result);
}
memset(&xpp, 0, sizeof(xpp));
return xdl_merge(orig,
@@ -737,26 +768,6 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
return 0;
}
static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
const char *path_unused,
mmfile_t *orig,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
mmbuffer_t *result)
{
/*
* The tentative merge result is "ours" for the final round,
* or common ancestor for an internal merge. Still return
* "conflicted merge" status.
*/
mmfile_t *stolen = index_only ? orig : src1;
result->ptr = stolen->ptr;
result->size = stolen->size;
stolen->ptr = NULL;
return 1;
}
#define LL_BINARY_MERGE 0
#define LL_TEXT_MERGE 1
#define LL_UNION_MERGE 2
@@ -771,9 +782,7 @@ static void create_temp(mmfile_t *src, char *path)
int fd;
strcpy(path, ".merge_file_XXXXXX");
fd = mkstemp(path);
if (fd < 0)
die("unable to create temp-file");
fd = xmkstemp(path);
if (write_in_full(fd, src->ptr, src->size) != src->size)
die("unable to write temp-file");
close(fd);

View File

@@ -45,7 +45,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
"%s/tmp_idx_XXXXXX", get_object_directory());
fd = mkstemp(tmpfile);
fd = xmkstemp(tmpfile);
index_name = xstrdup(tmpfile);
} else {
unlink(index_name);

View File

@@ -44,8 +44,11 @@ void setup_pager(void)
if (!isatty(1))
return;
if (!pager)
if (!pager) {
if (!pager_program)
git_config(git_default_config);
pager = pager_program;
}
if (!pager)
pager = getenv("PAGER");
if (!pager)

View File

@@ -76,16 +76,18 @@ struct path_list_item *path_list_lookup(const char *path, struct path_list *list
return list->items + i;
}
void path_list_clear(struct path_list *list, int free_items)
void path_list_clear(struct path_list *list, int free_util)
{
if (list->items) {
int i;
if (free_items)
for (i = 0; i < list->nr; i++) {
if (list->strdup_paths)
free(list->items[i].path);
if (list->strdup_paths) {
for (i = 0; i < list->nr; i++)
free(list->items[i].path);
}
if (free_util) {
for (i = 0; i < list->nr; i++)
free(list->items[i].util);
}
}
free(list->items);
}
list->items = NULL;

View File

@@ -15,7 +15,7 @@ struct path_list
void print_path_list(const char *text, const struct path_list *p);
int path_list_has_path(const struct path_list *list, const char *path);
void path_list_clear(struct path_list *list, int free_items);
void path_list_clear(struct path_list *list, int free_util);
struct path_list_item *path_list_insert(const char *path, struct path_list *list);
struct path_list_item *path_list_lookup(const char *path, struct path_list *list);

View File

@@ -7,6 +7,7 @@
#include "cache.h"
#include "cache-tree.h"
#include "refs.h"
#include "dir.h"
/* Index extensions.
*
@@ -665,7 +666,7 @@ static int check_file_directory_conflict(struct index_state *istate,
return retval + has_dir_name(istate, ce, pos, ok_to_replace);
}
int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
static int add_index_entry_with_check(struct index_state *istate, struct cache_entry *ce, int option)
{
int pos;
int ok_to_add = option & ADD_CACHE_OK_TO_ADD;
@@ -707,6 +708,22 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
pos = -pos-1;
}
return pos + 1;
}
int add_index_entry(struct index_state *istate, struct cache_entry *ce, int option)
{
int pos;
if (option & ADD_CACHE_JUST_APPEND)
pos = istate->cache_nr;
else {
int ret;
ret = add_index_entry_with_check(istate, ce, option);
if (ret <= 0)
return ret;
pos = ret - 1;
}
/* Make sure the array is big enough .. */
if (istate->cache_nr == istate->cache_alloc) {
@@ -717,7 +734,7 @@ int add_index_entry(struct index_state *istate, struct cache_entry *ce, int opti
/* Add it in.. */
istate->cache_nr++;
if (istate->cache_nr > pos)
if (istate->cache_nr > pos + 1)
memmove(istate->cache + pos + 1,
istate->cache + pos,
(istate->cache_nr - pos - 1) * sizeof(ce));
@@ -782,7 +799,7 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
return updated;
}
int refresh_index(struct index_state *istate, unsigned int flags)
int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen)
{
int i;
int has_errors = 0;
@@ -808,6 +825,9 @@ int refresh_index(struct index_state *istate, unsigned int flags)
continue;
}
if (pathspec && !match_pathspec(pathspec, ce->name, strlen(ce->name), 0, seen))
continue;
new = refresh_cache_ent(istate, ce, really, &cache_errno);
if (new == ce)
continue;

1
refs.c
View File

@@ -869,6 +869,7 @@ static int repack_without_ref(const char *refname)
die("too long a refname '%s'", list->name);
write_or_die(fd, line, len);
}
close(fd);
return commit_lock_file(&packlock);
}

View File

@@ -1150,6 +1150,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
die("unknown date format %s", arg);
continue;
}
if (!strcmp(arg, "--log-size")) {
revs->show_log_size = 1;
continue;
}
/*
* Grepping the commit log

View File

@@ -81,6 +81,7 @@ struct rev_info {
const char *log_reencode;
const char *subject_prefix;
int no_inline;
int show_log_size;
/* Filter by commit log message */
struct grep_opt *grep_filter;

54
setup.c
View File

@@ -141,7 +141,7 @@ void verify_non_filename(const char *prefix, const char *arg)
if (!lstat(name, &st))
die("ambiguous argument '%s': both revision and filename\n"
"Use '--' to separate filenames from revisions", arg);
if (errno != ENOENT)
if (errno != ENOENT && errno != ENOTDIR)
die("'%s': %s", arg, strerror(errno));
}
@@ -223,53 +223,21 @@ int is_inside_work_tree(void)
}
/*
* If no worktree was given, and we are outside of a default work tree,
* now is the time to set it.
*
* In other words, if the user calls git with something like
*
* git --git-dir=/some/where/else/.git bla
*
* default to /some/where/else as working directory; if the specified
* git-dir does not end in "/.git", the cwd is used as working directory.
* set_work_tree() is only ever called if you set GIT_DIR explicitely.
* The old behaviour (which we retain here) is to set the work tree root
* to the cwd, unless overridden by the config, the command line, or
* GIT_WORK_TREE.
*/
const char *set_work_tree(const char *dir)
static const char *set_work_tree(const char *dir)
{
char dir_buffer[PATH_MAX], *rel = NULL;
static char buffer[PATH_MAX + 1];
int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;
char buffer[PATH_MAX + 1];
/* strip the variable 'dir' of the postfix "/.git" if it has it */
len = strlen(dir);
if (len > suffix_len &&
!strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) {
if ((len - suffix_len) >= sizeof(dir_buffer))
die("directory name too long");
memcpy(dir_buffer, dir, len - suffix_len);
dir_buffer[len - suffix_len] = '\0';
/* are we inside the default work tree? */
rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);
}
/* if rel is set, the cwd is _not_ the current working tree */
if (rel && *rel) {
if (!is_absolute_path(dir))
set_git_dir(make_absolute_path(dir));
dir = dir_buffer;
if (chdir(dir))
die("cannot chdir to %s: %s", dir, strerror(errno));
else
strcat(rel, "/");
inside_git_dir = 0;
} else {
rel = NULL;
dir = getcwd(buffer, sizeof(buffer));
}
git_work_tree_cfg = xstrdup(dir);
if (!getcwd(buffer, sizeof(buffer)))
die ("Could not get the current working directory");
git_work_tree_cfg = xstrdup(buffer);
inside_work_tree = 1;
return rel;
return NULL;
}
/*

View File

@@ -505,7 +505,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
*/
if (idx_size != 4*256 + nr * 24 + 20 + 20) {
munmap(idx_map, idx_size);
return error("wrong index file size in %s", path);
return error("wrong index v1 file size in %s", path);
}
} else if (version == 2) {
/*
@@ -527,7 +527,7 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
max_size += (nr - 1)*8;
if (idx_size < min_size || idx_size > max_size) {
munmap(idx_map, idx_size);
return error("wrong index file size in %s", path);
return error("wrong index v2 file size in %s", path);
}
if (idx_size != min_size) {
/* make sure we can deal with large pack offsets */

View File

@@ -290,4 +290,85 @@ test_expect_success '.gitattributes says two and three are text' '
fi
'
test_expect_success 'in-tree .gitattributes (1)' '
echo "one -crlf" >>.gitattributes &&
git add .gitattributes &&
git commit -m "Add .gitattributes" &&
rm -rf tmp one dir .gitattributes patch.file three &&
git read-tree --reset -u HEAD &&
if remove_cr one >/dev/null
then
echo "Eh? one should not have CRLF"
false
else
: happy
fi &&
remove_cr three >/dev/null || {
echo "Eh? three should still have CRLF"
false
}
'
test_expect_success 'in-tree .gitattributes (2)' '
rm -rf tmp one dir .gitattributes patch.file three &&
git read-tree --reset HEAD &&
git checkout-index -f -q -u -a &&
if remove_cr one >/dev/null
then
echo "Eh? one should not have CRLF"
false
else
: happy
fi &&
remove_cr three >/dev/null || {
echo "Eh? three should still have CRLF"
false
}
'
test_expect_success 'in-tree .gitattributes (3)' '
rm -rf tmp one dir .gitattributes patch.file three &&
git read-tree --reset HEAD &&
git checkout-index -u .gitattributes &&
git checkout-index -u one dir/two three &&
if remove_cr one >/dev/null
then
echo "Eh? one should not have CRLF"
false
else
: happy
fi &&
remove_cr three >/dev/null || {
echo "Eh? three should still have CRLF"
false
}
'
test_expect_success 'in-tree .gitattributes (4)' '
rm -rf tmp one dir .gitattributes patch.file three &&
git read-tree --reset HEAD &&
git checkout-index -u one dir/two three &&
git checkout-index -u .gitattributes &&
if remove_cr one >/dev/null
then
echo "Eh? one should not have CRLF"
false
else
: happy
fi &&
remove_cr three >/dev/null || {
echo "Eh? three should still have CRLF"
false
}
'
test_done

View File

@@ -392,4 +392,9 @@ test_expect_success \
git diff expect actual
'
test_expect_success 'strip comments, too' '
test ! -z "$(echo "# comment" | git stripspace)" &&
test -z "$(echo "# comment" | git stripspace -s)"
'
test_done

View File

@@ -21,7 +21,16 @@ test_expect_success 'update-server-info honors core.sharedRepository' '
git commit -m a1 &&
umask 0277 &&
git update-server-info &&
test 444 = $(stat -c %a .git/info/refs)
actual="$(ls -l .git/info/refs)" &&
case "$actual" in
-r--r--r--*)
: happy
;;
*)
echo Oops, .git/info/refs is not 0444
false
;;
esac
'
test_done

View File

@@ -28,6 +28,8 @@ test_rev_parse() {
[ $# -eq 0 ] && return
}
# label is-bare is-inside-git is-inside-work prefix
test_rev_parse toplevel false false true ''
cd .git || exit 1
@@ -53,13 +55,13 @@ export GIT_DIR=../.git
export GIT_CONFIG="$(pwd)"/../.git/config
git config core.bare false
test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true work/
test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
git config core.bare true
test_rev_parse 'GIT_DIR=../.git, core.bare = true' true false false ''
git config --unset core.bare
test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true work/
test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
mv ../.git ../repo.git || exit 1
export GIT_DIR=../repo.git

55
t/t2050-git-dir-relative.sh Executable file
View File

@@ -0,0 +1,55 @@
#!/bin/sh
test_description='check problems with relative GIT_DIR
This test creates a working tree state with a file and subdir:
top (committed several times)
subdir (a subdirectory)
It creates a commit-hook and tests it, then moves .git
into the subdir while keeping the worktree location,
and tries commits from the top and the subdir, checking
that the commit-hook still gets called.'
. ./test-lib.sh
COMMIT_FILE="$(pwd)/output"
export COMMIT_FILE
test_expect_success 'Setting up post-commit hook' '
mkdir -p .git/hooks &&
echo >.git/hooks/post-commit "#!/bin/sh
touch \"\${COMMIT_FILE}\"
echo Post commit hook was called." &&
chmod +x .git/hooks/post-commit'
test_expect_success 'post-commit hook used ordinarily' '
echo initial >top &&
git-add top
git-commit -m initial &&
test -r "${COMMIT_FILE}"
'
rm -rf "${COMMIT_FILE}"
mkdir subdir
mv .git subdir
test_expect_success 'post-commit-hook created and used from top dir' '
echo changed >top &&
git --git-dir subdir/.git add top &&
git --git-dir subdir/.git commit -m topcommit &&
test -r "${COMMIT_FILE}"
'
rm -rf "${COMMIT_FILE}"
test_expect_success 'post-commit-hook from sub dir' '
echo changed again >top
cd subdir &&
git --git-dir .git --work-tree .. add ../top &&
git --git-dir .git --work-tree .. commit -m subcommit &&
test -r "${COMMIT_FILE}"
'
test_done

View File

@@ -13,26 +13,67 @@ only the updates to dir/sub.'
. ./test-lib.sh
test_expect_success 'setup' '
echo initial >top &&
mkdir dir &&
echo initial >dir/sub &&
git add dir/sub top &&
git-commit -m initial &&
echo changed >top &&
echo changed >dir/sub &&
echo other >dir/other
test_expect_success setup '
echo initial >check &&
echo initial >top &&
mkdir dir1 dir2 &&
echo initial >dir1/sub1 &&
echo initial >dir1/sub2 &&
echo initial >dir2/sub3 &&
git add check dir1 dir2 top &&
test_tick
git-commit -m initial &&
echo changed >check &&
echo changed >top &&
echo changed >dir2/sub3 &&
rm -f dir1/sub1 &&
echo other >dir2/other
'
test_expect_success 'update' 'git add -u dir'
test_expect_success update '
git add -u dir1 dir2
'
test_expect_success 'update touched correct path' \
'test "`git diff-files --name-status dir/sub`" = ""'
test_expect_success 'update noticed a removal' '
test "$(git-ls-files dir1/sub1)" = ""
'
test_expect_success 'update did not touch other tracked files' \
'test "`git diff-files --name-status top`" = "M top"'
test_expect_success 'update touched correct path' '
test "$(git-diff-files --name-status dir2/sub3)" = ""
'
test_expect_success 'update did not touch untracked files' \
'test "`git diff-files --name-status dir/other`" = ""'
test_expect_success 'update did not touch other tracked files' '
test "$(git-diff-files --name-status check)" = "M check" &&
test "$(git-diff-files --name-status top)" = "M top"
'
test_expect_success 'update did not touch untracked files' '
test "$(git-ls-files dir2/other)" = ""
'
test_expect_success 'cache tree has not been corrupted' '
git ls-files -s |
sed -e "s/ 0 / /" >expect &&
git ls-tree -r $(git write-tree) |
sed -e "s/ blob / /" >current &&
diff -u expect current
'
test_expect_success 'update from a subdirectory' '
(
cd dir1 &&
echo more >sub2 &&
git add -u sub2
)
'
test_expect_success 'change gets noticed' '
test "$(git diff-files --name-status dir1)" = ""
'
test_done

View File

@@ -61,29 +61,31 @@ test_expect_success 'setup' '
git tag I
'
cat > fake-editor.sh << EOF
cat > fake-editor.sh <<\EOF
#!/bin/sh
test "\$1" = .git/COMMIT_EDITMSG && {
test -z "\$FAKE_COMMIT_MESSAGE" || echo "\$FAKE_COMMIT_MESSAGE" > "\$1"
test -z "\$FAKE_COMMIT_AMEND" || echo "\$FAKE_COMMIT_AMEND" >> "\$1"
case "$1" in
*/COMMIT_EDITMSG)
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
exit
}
test -z "\$EXPECT_COUNT" ||
test "\$EXPECT_COUNT" = \$(grep -ve "^#" -e "^$" < "\$1" | wc -l) ||
;;
esac
test -z "$EXPECT_COUNT" ||
test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
exit
test -z "\$FAKE_LINES" && exit
grep -v "^#" < "\$1" > "\$1".tmp
rm "\$1"
cat "\$1".tmp
test -z "$FAKE_LINES" && exit
grep -v '^#' < "$1" > "$1".tmp
rm -f "$1"
cat "$1".tmp
action=pick
for line in \$FAKE_LINES; do
case \$line in
for line in $FAKE_LINES; do
case $line in
squash)
action="\$line";;
action="$line";;
*)
echo sed -n "\${line}s/^pick/\$action/p"
sed -n "\${line}p" < "\$1".tmp
sed -n "\${line}s/^pick/\$action/p" < "\$1".tmp >> "\$1"
echo sed -n "${line}s/^pick/$action/p"
sed -n "${line}p" < "$1".tmp
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
action=pick;;
esac
done

View File

@@ -147,4 +147,16 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st
git ls-files --stage | grep "^120000 .* 0 symlink$"
'
test_expect_success 'git add --refresh' '
>foo && git add foo && git commit -a -m "commit all" &&
test -z "`git diff-index HEAD -- foo`" &&
git read-tree HEAD &&
case "`git diff-index HEAD -- foo`" in
:100644" "*"M foo") echo ok;;
*) echo fail; (exit 1);;
esac &&
git add --refresh -- foo &&
test -z "`git diff-index HEAD -- foo`"
'
test_done

View File

@@ -7,6 +7,12 @@ test_description='quoted output'
. ./test-lib.sh
P1='pathname with HT'
: >"$P1" 2>&1 && test -f "$P1" && rm -f "$P1" || {
echo >&2 'Filesystem does not support HT in names'
test_done
}
FN='濱野'
GN='純'
HT=' '

View File

@@ -129,7 +129,7 @@ pull_to_client 2nd "B" $((64*3))
pull_to_client 3rd "A" $((1*3)) # old fails
test_expect_success "clone shallow" "git-clone --depth 2 . shallow"
test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow"
(cd shallow; git count-objects -v) > count.shallow

View File

@@ -145,4 +145,12 @@ test_expect_success 'bundle does not prerequisite objects' '
test 4 = $(git verify-pack -v bundle.pack | wc -l)
'
test_expect_success 'bundle should be able to create a full history' '
cd "$D" &&
git tag -a -m '1.0' v1.0 master &&
git bundle create bundle4 v1.0
'
test_done

View File

@@ -51,7 +51,7 @@ diff expected current'
cd "$base_dir"
test_expect_success 'cloning with reference (no -l -s)' \
'git clone --reference B A D'
'git clone --reference B file://`pwd`/A D'
cd "$base_dir"

View File

@@ -8,13 +8,16 @@ D=`pwd`
test_expect_success 'preparing origin repository' '
: >file && git add . && git commit -m1 &&
git clone --bare . a.git &&
git clone --bare . x
git clone --bare . x &&
test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
'
test_expect_success 'local clone without .git suffix' '
cd "$D" &&
git clone -l -s a b &&
cd b &&
test "$(GIT_CONFIG=.git/config git config --bool core.bare)" = false &&
git fetch
'
@@ -43,4 +46,21 @@ test_expect_success 'local clone from x.git that does not exist' '
fi
'
test_expect_success 'With -no-hardlinks, local will make a copy' '
cd "$D" &&
git clone --bare --no-hardlinks x w &&
cd w &&
linked=$(find objects -type f ! -links 1 | wc -l) &&
test 0 = $linked
'
test_expect_success 'Even without -l, local will make a hardlink' '
cd "$D" &&
rm -fr w &&
git clone -l --bare x w &&
cd w &&
copied=$(find objects -type f -links 1 | wc -l) &&
test 0 = $copied
'
test_done

67
t/t6027-merge-binary.sh Executable file
View File

@@ -0,0 +1,67 @@
#!/bin/sh
test_description='ask merge-recursive to merge binary files'
. ./test-lib.sh
test_expect_success setup '
cat ../test4012.png >m &&
git add m &&
git ls-files -s | sed -e "s/ 0 / 1 /" >E1 &&
test_tick &&
git commit -m "initial" &&
git branch side &&
echo frotz >a &&
git add a &&
echo nitfol >>m &&
git add a m &&
git ls-files -s a >E0 &&
git ls-files -s m | sed -e "s/ 0 / 3 /" >E3 &&
test_tick &&
git commit -m "master adds some" &&
git checkout side &&
echo rezrov >>m &&
git add m &&
git ls-files -s m | sed -e "s/ 0 / 2 /" >E2 &&
test_tick &&
git commit -m "side modifies" &&
git tag anchor &&
cat E0 E1 E2 E3 >expect
'
test_expect_success resolve '
rm -f a* m* &&
git reset --hard anchor &&
if git merge -s resolve master
then
echo Oops, should not have succeeded
false
else
git ls-files -s >current
diff -u current expect
fi
'
test_expect_success recursive '
rm -f a* m* &&
git reset --hard anchor &&
if git merge -s recursive master
then
echo Oops, should not have succeeded
false
else
git ls-files -s >current
diff -u current expect
fi
'
test_done

View File

@@ -5,7 +5,7 @@
test_description='git-tag
Basic tests for operations with tags.'
Tests for operations with tags.'
. ./test-lib.sh
@@ -16,11 +16,15 @@ tag_exists () {
}
# todo: git tag -l now returns always zero, when fixed, change this test
test_expect_success 'listing all tags in an empty tree should succeed' \
'git tag -l'
test_expect_success 'listing all tags in an empty tree should succeed' '
git tag -l &&
git tag
'
test_expect_success 'listing all tags in an empty tree should output nothing' \
'test `git-tag -l | wc -l` -eq 0'
test_expect_success 'listing all tags in an empty tree should output nothing' '
test `git-tag -l | wc -l` -eq 0 &&
test `git-tag | wc -l` -eq 0
'
test_expect_failure 'looking for a tag in an empty tree should fail' \
'tag_exists mytag'
@@ -49,11 +53,15 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
git tag mytag
'
test_expect_success 'listing all tags if one exists should succeed' \
'git-tag -l'
test_expect_success 'listing all tags if one exists should succeed' '
git-tag -l &&
git-tag
'
test_expect_success 'listing all tags if one exists should output that tag' \
'test `git-tag -l` = mytag'
test_expect_success 'listing all tags if one exists should output that tag' '
test `git-tag -l` = mytag &&
test `git-tag` = mytag
'
# pattern matching:
@@ -165,6 +173,8 @@ test_expect_success 'listing all tags should print them ordered' '
git tag v1.0 &&
git tag t210 &&
git tag -l > actual &&
git diff expect actual &&
git tag > actual &&
git diff expect actual
'
@@ -264,6 +274,10 @@ test_expect_failure \
'trying to verify a non-annotated and non-signed tag should fail' \
'git-tag -v non-annotated-tag'
test_expect_failure \
'trying to verify many non-annotated or unknown tags, should fail' \
'git-tag -v unknown-tag1 non-annotated-tag unknown-tag2'
# creating annotated tags:
get_tag_msg () {
@@ -306,6 +320,45 @@ test_expect_success \
git diff expect actual
'
cat >inputmsg <<EOF
A message from the
standard input
EOF
get_tag_header stdin-annotated-tag $commit commit $time >expect
cat inputmsg >>expect
test_expect_success 'creating an annotated tag with -F - should succeed' '
git-tag -F - stdin-annotated-tag <inputmsg &&
get_tag_msg stdin-annotated-tag >actual &&
git diff expect actual
'
test_expect_success \
'trying to create a tag with a non-existing -F file should fail' '
! test -f nonexistingfile &&
! tag_exists notag &&
! git-tag -F nonexistingfile notag &&
! tag_exists notag
'
test_expect_success \
'trying to create tags giving many -m or -F options should fail' '
echo "message file 1" >msgfile1 &&
echo "message file 2" >msgfile2 &&
! tag_exists msgtag &&
! git-tag -m "message 1" -m "message 2" msgtag &&
! tag_exists msgtag &&
! git-tag -F msgfile1 -F msgfile2 msgtag &&
! tag_exists msgtag &&
! git-tag -m "message 1" -F msgfile1 msgtag &&
! tag_exists msgtag &&
! git-tag -F msgfile1 -m "message 1" msgtag &&
! tag_exists msgtag &&
! git-tag -F msgfile1 -m "message 1" -F msgfile2 msgtag &&
! tag_exists msgtag &&
! git-tag -m "message 1" -F msgfile1 -m "message 2" msgtag &&
! tag_exists msgtag
'
# blank and empty messages:
get_tag_header empty-annotated-tag $commit commit $time >expect
@@ -551,6 +604,12 @@ test_expect_success \
! git-tag -v file-annotated-tag
'
test_expect_success \
'trying to verify two annotated non-signed tags should fail' '
tag_exists annotated-tag file-annotated-tag &&
! git-tag -v annotated-tag file-annotated-tag
'
# creating and verifying signed tags:
gpg --version >/dev/null
@@ -589,9 +648,55 @@ test_expect_success 'creating a signed tag with -m message should succeed' '
git diff expect actual
'
cat >sigmsgfile <<EOF
Another signed tag
message in a file.
EOF
get_tag_header file-signed-tag $commit commit $time >expect
cat sigmsgfile >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect
test_expect_success \
'creating a signed tag with -F messagefile should succeed' '
git-tag -s -F sigmsgfile file-signed-tag &&
get_tag_msg file-signed-tag >actual &&
git diff expect actual
'
cat >siginputmsg <<EOF
A signed tag message from
the standard input
EOF
get_tag_header stdin-signed-tag $commit commit $time >expect
cat siginputmsg >>expect
echo '-----BEGIN PGP SIGNATURE-----' >>expect
test_expect_success 'creating a signed tag with -F - should succeed' '
git-tag -s -F - stdin-signed-tag <siginputmsg &&
get_tag_msg stdin-signed-tag >actual &&
git diff expect actual
'
test_expect_success \
'trying to create a signed tag with non-existing -F file should fail' '
! test -f nonexistingfile &&
! tag_exists nosigtag &&
! git-tag -s -F nonexistingfile nosigtag &&
! tag_exists nosigtag
'
test_expect_success 'verifying a signed tag should succeed' \
'git-tag -v signed-tag'
test_expect_success 'verifying two signed tags in one command should succeed' \
'git-tag -v signed-tag file-signed-tag'
test_expect_success \
'verifying many signed and non-signed tags should fail' '
! git-tag -v signed-tag annotated-tag &&
! git-tag -v file-annotated-tag file-signed-tag &&
! git-tag -v annotated-tag file-signed-tag file-annotated-tag &&
! git-tag -v signed-tag annotated-tag file-signed-tag
'
test_expect_success 'verifying a forged tag should fail' '
forged=$(git cat-file tag signed-tag |
sed -e "s/signed-tag/forged-tag/" |

View File

@@ -175,4 +175,21 @@ test_expect_success 'checkout superproject with subproject already present' '
git-checkout master
'
test_expect_success 'apply submodule diff' '
git branch second &&
(
cd lib &&
echo s >s &&
git add s &&
git commit -m "change subproject"
) &&
git update-index --add lib &&
git-commit -m "change lib" &&
git-format-patch -1 --stdout >P.diff &&
git checkout second &&
git apply --index P.diff &&
D=$(git diff --cached master) &&
test -z "$D"
'
test_done

48
t/t9116-git-svn-log.sh Executable file
View File

@@ -0,0 +1,48 @@
#!/bin/sh
#
# Copyright (c) 2007 Eric Wong
#
test_description='git-svn log tests'
. ./lib-git-svn.sh
test_expect_success 'setup repository and import' "
mkdir import &&
cd import &&
for i in trunk branches/a branches/b \
tags/0.1 tags/0.2 tags/0.3; do
mkdir -p \$i && \
echo hello >> \$i/README || exit 1
done && \
svn import -m test . $svnrepo
cd .. &&
git-svn init $svnrepo -T trunk -b branches -t tags &&
git-svn fetch &&
git reset --hard trunk &&
echo bye >> README &&
git commit -a -m bye &&
git svn dcommit &&
git reset --hard a &&
echo why >> FEEDME &&
git update-index --add FEEDME &&
git commit -m feedme &&
git svn dcommit &&
git reset --hard trunk &&
echo aye >> README &&
git commit -a -m aye &&
git svn dcommit
"
test_expect_success 'run log' "
git reset --hard a &&
git svn log -r2 trunk | grep ^r2 &&
git svn log -r4 trunk | grep ^r4 &&
git svn log -r3 | grep ^r3
"
test_expect_success 'run log against a from trunk' "
git reset --hard trunk &&
git svn log -r3 a | grep ^r3
"
test_done

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