summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes-1.5.3.txt18
-rw-r--r--Documentation/blame-options.txt20
-rwxr-xr-xDocumentation/cmd-list.perl16
-rw-r--r--Documentation/config.txt2
-rw-r--r--Documentation/git-add.txt41
-rw-r--r--Documentation/git-commit.txt29
-rw-r--r--Documentation/git-config.txt4
-rw-r--r--Documentation/git-push.txt2
-rw-r--r--Documentation/git-rev-parse.txt2
-rw-r--r--Documentation/git-stash.txt8
-rw-r--r--Documentation/git-status.txt7
-rw-r--r--Documentation/git-svn.txt20
-rw-r--r--Documentation/git.txt16
-rwxr-xr-xDocumentation/install-doc-quick.sh12
-rw-r--r--Documentation/user-manual.txt66
-rw-r--r--INSTALL4
-rw-r--r--Makefile4
-rw-r--r--builtin-apply.c2
-rw-r--r--builtin-bundle.c87
-rw-r--r--builtin-read-tree.c27
-rw-r--r--builtin-update-index.c10
-rw-r--r--cache.h1
-rwxr-xr-xcontrib/completion/git-completion.bash7
-rwxr-xr-xcontrib/fast-import/git-p458
-rw-r--r--contrib/hooks/update-paranoid179
-rw-r--r--contrib/patches/docbook-xsl-manpages-charmap.patch21
-rwxr-xr-xgit-am.sh3
-rwxr-xr-xgit-cvsserver.perl2
-rwxr-xr-xgit-send-email.perl89
-rw-r--r--git.c3
-rwxr-xr-xgitweb/gitweb.perl3
-rw-r--r--merge-recursive.c16
-rw-r--r--pager.c5
-rw-r--r--read-cache.c20
-rw-r--r--refs.c1
-rw-r--r--setup.c50
-rwxr-xr-xt/t1500-rev-parse.sh6
-rwxr-xr-xt/t3700-add.sh4
-rwxr-xr-xt/t5510-fetch.sh8
-rw-r--r--tree.c69
-rw-r--r--unpack-trees.c75
-rw-r--r--unpack-trees.h13
43 files changed, 747 insertions, 284 deletions
diff --git a/.gitignore b/.gitignore
index 20ee642420..63c918c667 100644
--- a/.gitignore
+++ b/.gitignore
@@ -148,6 +148,7 @@ git-write-tree
git-core-*/?*
gitk-wish
gitweb/gitweb.cgi
+test-absolute-path
test-chmtime
test-date
test-delta
diff --git a/Documentation/RelNotes-1.5.3.txt b/Documentation/RelNotes-1.5.3.txt
index 785bd38455..21bb1fc6f2 100644
--- a/Documentation/RelNotes-1.5.3.txt
+++ b/Documentation/RelNotes-1.5.3.txt
@@ -40,16 +40,22 @@ Updates since v1.5.2
$GIT_DIR to work in a subdirectory of a working tree that is
not located at "$GIT_DIR/..".
+ - Giving "--file=<file>" option to "git config" is the same as
+ running the command with GIT_CONFIG=<file> environment.
+
- "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
- the current branch, creating a new branch. You can specify a
- number of filters to modify the commits, files and trees.
+ 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 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
configuration variable to prime the commit message given to you in the
editor.
@@ -120,6 +126,9 @@ Updates since v1.5.2
of the format ('tgz', 'tbz2' or 'zip'). Please update the
your configuration file accordingly.
+ - "git diff" (but not the plumbing level "git diff-tree") now
+ recursively descends into trees by default.
+
- 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
@@ -165,6 +174,9 @@ Updates since v1.5.2
- The diffstat given after a merge (or a pull) honors the
color.diff configuration.
+ - "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
the end of the file.
@@ -262,6 +274,6 @@ this release, unless otherwise noted.
--
exec >/var/tmp/1
-O=v1.5.3-rc3
+O=v1.5.3-rc4
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index a46bf6ce70..17379f0576 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -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.
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index 2143995ece..4ee76eaf99 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -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
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 3135cb7a66..de9e72b562 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -101,7 +101,7 @@ Example
# Proxy settings
[core]
- gitProxy="ssh" for "ssh://kernel.org/"
+ gitProxy="ssh" for "kernel.org"
gitProxy=default-proxy ; for the rest
Variables
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 76d2b05854..4af3a9b0d7 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -3,7 +3,7 @@ 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
--------
@@ -11,24 +11,27 @@ SYNOPSIS
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 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 'git status' command can be used to obtain a summary of what is included
-for the next commit.
-
-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.
+This command adds the current content of new or modified files to the
+index, thus staging that content for inclusion in the next 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.
+
+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.
+
+The 'git status' command can be used to obtain a summary of which
+files have changes that are staged for the next commit.
+
+The 'add' 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.
Please see gitlink:git-commit[1] for alternative ways to add content to a
commit.
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 627994eb97..e54fb12103 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -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
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 8451cccf8a..c3dffffe32 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -214,9 +214,7 @@ Given a .git/config like this:
; Proxy settings
[core]
- gitproxy="ssh" for "ssh://kernel.org/"
gitproxy="proxy-command" for kernel.org
- gitproxy="myprotocol-command" for "my://"
gitproxy=default-proxy ; for all the rest
you can set the filemode to true with
@@ -291,7 +289,7 @@ To actually match only values with an exclamation mark, you have to
To add a new proxy, without altering any of the existing ones, use
------------
-% git config core.gitproxy '"proxy" for example.com'
+% git config core.gitproxy '"proxy-command" for example.com'
------------
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 74a0da1ed4..0dd9caf867 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -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.
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index eea9c9cfe9..4b4d229e60 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -224,7 +224,7 @@ left-to-right.
G H I J
\ / \ /
D E F
- \ | / \
+ \ | / \
\ | / |
\|/ |
B C
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 17121ade56..05f40cff6c 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -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>]::
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index 6f16eb0328..8fd0fc6236 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -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
------
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 0a210e4bea..816340b944 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -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
---------------------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 4c4d1746e0..18f8b6a0a1 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -421,6 +421,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,
diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh
index e6601bdd82..5433cf8ced 100755
--- a/Documentation/install-doc-quick.sh
+++ b/Documentation/install-doc-quick.sh
@@ -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"
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 0071cd070e..f89952ad84 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -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:
diff --git a/INSTALL b/INSTALL
index 79e71b6922..c62b12c288 100644
--- a/INSTALL
+++ b/INSTALL
@@ -116,3 +116,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
diff --git a/Makefile b/Makefile
index b593446efb..2f3b9b23e3 100644
--- a/Makefile
+++ b/Makefile
@@ -458,6 +458,10 @@ ifeq ($(uname_S),AIX)
NO_STRLCPY = YesPlease
NEEDS_LIBICONV=YesPlease
endif
+ifeq ($(uname_S),GNU)
+ # GNU/Hurd
+ NO_STRLCPY=YesPlease
+endif
ifeq ($(uname_S),IRIX64)
NO_IPV6=YesPlease
NO_SETENV=YesPlease
diff --git a/builtin-apply.c b/builtin-apply.c
index 0a0b4a9e3f..da270755a7 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2508,7 +2508,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);
}
diff --git a/builtin-bundle.c b/builtin-bundle.c
index 6ae5ab04c9..f4b4f034f4 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -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;
}
@@ -207,7 +196,12 @@ static int create_bundle(struct bundle_header *header, const char *path,
char buffer[1024];
struct rev_info revs;
struct child_process rls;
+ FILE *rls_fout;
+ /*
+ * NEEDSWORK: this should use something like lock-file
+ * to create temporary that is cleaned up upon error.
+ */
bundle_fd = (!strcmp(path, "-") ? 1 :
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
if (bundle_fd < 0)
@@ -232,10 +226,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 +241,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 +263,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);
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 41f8110238..1967d100f2 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -13,14 +13,19 @@
#include "dir.h"
#include "builtin.h"
-static struct object_list *trees;
+static int nr_trees;
+static struct tree *trees[4];
static int list_tree(unsigned char *sha1)
{
- struct tree *tree = parse_tree_indirect(sha1);
+ struct tree *tree;
+
+ if (nr_trees >= 4)
+ return -1;
+ tree = parse_tree_indirect(sha1);
if (!tree)
return -1;
- object_list_append(&tree->object, &trees);
+ trees[nr_trees++] = tree;
return 0;
}
@@ -76,11 +81,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,12 +96,12 @@ 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[3];
struct unpack_trees_options opts;
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
- setup_git_directory();
git_config(git_default_config);
newfd = hold_locked_index(&lock_file, 1);
@@ -259,7 +263,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,
@@ -267,7 +276,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();
}
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 509369e9e7..a7a4574f2b 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -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);
diff --git a/cache.h b/cache.h
index e97af18eea..e5276e6add 100644
--- a/cache.h
+++ b/cache.h
@@ -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);
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index f2b10fa5f6..82b9ed40d8 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -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
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 1f5a56ee7f..805d632a68 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -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")
@@ -1181,11 +1209,11 @@ class P4Sync(Command):
elif ',' not in self.changeRange:
self.revision = self.changeRange
self.changeRange = ""
- p = p[0:atIdx]
+ p = p[:atIdx]
elif p.find("#") != -1:
hashIdx = p.index("#")
self.revision = p[hashIdx:]
- p = p[0:hashIdx]
+ p = p[:hashIdx]
elif self.previousDepotPaths == []:
self.revision = "#head"
@@ -1296,10 +1324,10 @@ class P4Sync(Command):
changeNum = line.split(" ")[1]
changes.append(changeNum)
- changes.reverse()
+ changes.sort()
if len(self.maxChanges) > 0:
- changes = changes[0:min(int(self.maxChanges), len(changes))]
+ changes = changes[:min(int(self.maxChanges), len(changes))]
if len(changes) == 0:
if not self.silent:
diff --git a/contrib/hooks/update-paranoid b/contrib/hooks/update-paranoid
index 5ee1835c80..068fa37083 100644
--- a/contrib/hooks/update-paranoid
+++ b/contrib/hooks/update-paranoid
@@ -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;
+
+ # 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;
- 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:)
- );
+ # 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";
diff --git a/contrib/patches/docbook-xsl-manpages-charmap.patch b/contrib/patches/docbook-xsl-manpages-charmap.patch
new file mode 100644
index 0000000000..f2b08b4f4a
--- /dev/null
+++ b/contrib/patches/docbook-xsl-manpages-charmap.patch
@@ -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 -->
diff --git a/git-am.sh b/git-am.sh
index 6cf0eeee71..b5ed8ca15c 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -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
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index ae7d511589..13dbd27a80 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -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;
diff --git a/git-send-email.perl b/git-send-email.perl
index f43f92f957..69559b289a 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -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";
+ }
+
+ # 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\?=/;
}
- return $recipient;
+
+ # 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";
}
diff --git a/git.c b/git.c
index 25b8274d3e..f8c4545208 100644
--- a/git.c
+++ b/git.c
@@ -315,7 +315,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 },
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 498b936dd4..f282a677aa 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1515,6 +1515,7 @@ sub git_get_projects_list {
File::Find::find({
follow_fast => 1, # follow symbolic links
+ follow_skip => 2, # ignore duplicates
dangling_symlinks => 0, # ignore dangling symlinks, silently
wanted => sub {
# skip project-list toplevel, if we get it.
@@ -5365,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;
diff --git a/merge-recursive.c b/merge-recursive.c
index c8539ec0ba..f7d1b84999 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -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;
}
diff --git a/pager.c b/pager.c
index 3bfed02599..8bac9d9903 100644
--- a/pager.c
+++ b/pager.c
@@ -31,8 +31,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)
diff --git a/read-cache.c b/read-cache.c
index e060392d1d..865369df0e 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -665,7 +665,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 +707,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 +733,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));
diff --git a/refs.c b/refs.c
index fac6548001..09a2c87fc2 100644
--- a/refs.c
+++ b/refs.c
@@ -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);
}
diff --git a/setup.c b/setup.c
index 3653092ab6..06004f1587 100644
--- a/setup.c
+++ b/setup.c
@@ -107,7 +107,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));
}
@@ -189,47 +189,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];
- static char buffer[PATH_MAX + 1], *rel = NULL;
- int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;
-
- /* strip the variable 'dir' of the postfix "/.git" if it has it */
- len = strlen(dir);
- if (len > postfix_len && !strcmp(dir + len - postfix_len,
- "/" DEFAULT_GIT_DIR_ENVIRONMENT)) {
- strncpy(dir_buffer, dir, len - postfix_len);
-
- /* 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;
- chdir(dir);
- strcat(rel, "/");
- inside_git_dir = 0;
- } else {
- rel = NULL;
- dir = getcwd(buffer, sizeof(buffer));
- }
- git_work_tree_cfg = xstrdup(dir);
+ char buffer[PATH_MAX + 1];
+
+ 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;
}
/*
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index bea40cba8d..e474b3f1d5 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -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
diff --git a/t/t3700-add.sh b/t/t3700-add.sh
index b52fde8577..213e9249da 100755
--- a/t/t3700-add.sh
+++ b/t/t3700-add.sh
@@ -131,8 +131,8 @@ test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over st
(
echo "100644 $(git hash-object -w stage1) 1 file"
echo "100755 $(git hash-object -w stage2) 2 file"
- echo "100644 $(printf $s | git hash-object -w -t blob --stdin) 1 symlink"
- echo "120000 $(printf $s | git hash-object -w -t blob --stdin) 2 symlink"
+ echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink"
+ echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink"
) | git update-index --index-info &&
git config core.filemode 0 &&
git config core.symlinks 0 &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 426017e1d0..439430f569 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -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
diff --git a/tree.c b/tree.c
index 04fe653a8e..8c0819fa72 100644
--- a/tree.c
+++ b/tree.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "cache-tree.h"
#include "tree.h"
#include "blob.h"
#include "commit.h"
@@ -7,7 +8,7 @@
const char *tree_type = "tree";
-static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+static int read_one_entry_opt(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, int opt)
{
int len;
unsigned int size;
@@ -25,7 +26,23 @@ static int read_one_entry(const unsigned char *sha1, const char *base, int basel
memcpy(ce->name, base, baselen);
memcpy(ce->name + baselen, pathname, len+1);
hashcpy(ce->sha1, sha1);
- return add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+ return add_cache_entry(ce, opt);
+}
+
+static int read_one_entry(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+{
+ return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
+ ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
+}
+
+/*
+ * This is used when the caller knows there is no existing entries at
+ * the stage that will conflict with the entry being added.
+ */
+static int read_one_entry_quick(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage)
+{
+ return read_one_entry_opt(sha1, base, baselen, pathname, mode, stage,
+ ADD_CACHE_JUST_APPEND);
}
static int match_tree_entry(const char *base, int baselen, const char *path, unsigned int mode, const char **paths)
@@ -119,9 +136,55 @@ int read_tree_recursive(struct tree *tree,
return 0;
}
+static int cmp_cache_name_compare(const void *a_, const void *b_)
+{
+ const struct cache_entry *ce1, *ce2;
+
+ ce1 = *((const struct cache_entry **)a_);
+ ce2 = *((const struct cache_entry **)b_);
+ return cache_name_compare(ce1->name, ntohs(ce1->ce_flags),
+ ce2->name, ntohs(ce2->ce_flags));
+}
+
int read_tree(struct tree *tree, int stage, const char **match)
{
- return read_tree_recursive(tree, "", 0, stage, match, read_one_entry);
+ read_tree_fn_t fn = NULL;
+ int i, err;
+
+ /*
+ * Currently the only existing callers of this function all
+ * call it with stage=1 and after making sure there is nothing
+ * at that stage; we could always use read_one_entry_quick().
+ *
+ * But when we decide to straighten out git-read-tree not to
+ * use unpack_trees() in some cases, this will probably start
+ * to matter.
+ */
+
+ /*
+ * See if we have cache entry at the stage. If so,
+ * do it the original slow way, otherwise, append and then
+ * sort at the end.
+ */
+ for (i = 0; !fn && i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (ce_stage(ce) == stage)
+ fn = read_one_entry;
+ }
+
+ if (!fn)
+ fn = read_one_entry_quick;
+ err = read_tree_recursive(tree, "", 0, stage, match, fn);
+ if (fn == read_one_entry || err)
+ return err;
+
+ /*
+ * Sort the cache entry -- we need to nuke the cache tree, though.
+ */
+ cache_tree_free(&active_cache_tree);
+ qsort(active_cache, active_nr, sizeof(active_cache[0]),
+ cmp_cache_name_compare);
+ return 0;
}
struct tree *lookup_tree(const unsigned char *sha1)
diff --git a/unpack-trees.c b/unpack-trees.c
index 3b32718436..ccfeb6e245 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -16,19 +16,13 @@ struct tree_entry_list {
const unsigned char *sha1;
};
-static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
+static struct tree_entry_list *create_tree_entry_list(struct tree_desc *desc)
{
- struct tree_desc desc;
struct name_entry one;
struct tree_entry_list *ret = NULL;
struct tree_entry_list **list_p = &ret;
- if (!tree->object.parsed)
- parse_tree(tree);
-
- init_tree_desc(&desc, tree->buffer, tree->size);
-
- while (tree_entry(&desc, &one)) {
+ while (tree_entry(desc, &one)) {
struct tree_entry_list *entry;
entry = xmalloc(sizeof(struct tree_entry_list));
@@ -64,10 +58,17 @@ static int entcmp(const char *name1, int dir1, const char *name2, int dir2)
return ret;
}
+static inline void remove_entry(int remove)
+{
+ if (remove >= 0)
+ remove_cache_entry_at(remove);
+}
+
static int unpack_trees_rec(struct tree_entry_list **posns, int len,
const char *base, struct unpack_trees_options *o,
struct tree_entry_list *df_conflict_list)
{
+ int remove;
int baselen = strlen(base);
int src_size = len + 1;
int i_stk = i_stk;
@@ -151,10 +152,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
subposns = xcalloc(len, sizeof(struct tree_list_entry *));
+ remove = -1;
if (cache_name && !strcmp(cache_name, first)) {
any_files = 1;
src[0] = active_cache[o->pos];
- remove_cache_entry_at(o->pos);
+ remove = o->pos;
}
for (i = 0; i < len; i++) {
@@ -173,9 +175,11 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
if (S_ISDIR(posns[i]->mode)) {
struct tree *tree = lookup_tree(posns[i]->sha1);
+ struct tree_desc t;
any_dirs = 1;
parse_tree(tree);
- subposns[i] = create_tree_entry_list(tree);
+ init_tree_desc(&t, tree->buffer, tree->size);
+ subposns[i] = create_tree_entry_list(&t);
posns[i] = posns[i]->next;
src[i + o->merge] = o->df_conflict_entry;
continue;
@@ -218,13 +222,14 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
printf("\n");
}
#endif
- ret = o->fn(src, o);
+ ret = o->fn(src, o, remove);
#if DBRT_DEBUG > 1
printf("Added %d entries\n", ret);
#endif
o->pos += ret;
} else {
+ remove_entry(remove);
for (i = 0; i < src_size; i++) {
if (src[i]) {
add_cache_entry(src[i], ADD_CACHE_OK_TO_ADD|ADD_CACHE_SKIP_DFCHECK);
@@ -331,12 +336,10 @@ static void check_updates(struct cache_entry **src, int nr,
stop_progress(&progress);;
}
-int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
+int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
{
- unsigned len = object_list_length(trees);
struct tree_entry_list **posns;
int i;
- struct object_list *posn = trees;
struct tree_entry_list df_conflict_list;
static struct cache_entry *dfc;
@@ -356,10 +359,9 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
if (len) {
posns = xmalloc(len * sizeof(struct tree_entry_list *));
- for (i = 0; i < len; i++) {
- posns[i] = create_tree_entry_list((struct tree *) posn->item);
- posn = posn->next;
- }
+ for (i = 0; i < len; i++)
+ posns[i] = create_tree_entry_list(t+i);
+
if (unpack_trees_rec(posns, len, o->prefix ? o->prefix : "",
o, &df_conflict_list))
return -1;
@@ -407,6 +409,15 @@ static void verify_uptodate(struct cache_entry *ce,
unsigned changed = ce_match_stat(ce, &st, 1);
if (!changed)
return;
+ /*
+ * NEEDSWORK: the current default policy is to allow
+ * submodule to be out of sync wrt the supermodule
+ * index. This needs to be tightened later for
+ * submodules that are marked to be automatically
+ * checked out.
+ */
+ if (S_ISGITLINK(ntohl(ce->ce_mode)))
+ return;
errno = 0;
}
if (errno == ENOENT)
@@ -639,7 +650,8 @@ static void show_stage_entry(FILE *o,
#endif
int threeway_merge(struct cache_entry **stages,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *index;
struct cache_entry *head;
@@ -717,8 +729,10 @@ int threeway_merge(struct cache_entry **stages,
}
/* #1 */
- if (!head && !remote && any_anc_missing)
+ if (!head && !remote && any_anc_missing) {
+ remove_entry(remove);
return 0;
+ }
/* Under the new "aggressive" rule, we resolve mostly trivial
* cases that we historically had git-merge-one-file resolve.
@@ -750,6 +764,7 @@ int threeway_merge(struct cache_entry **stages,
if ((head_deleted && remote_deleted) ||
(head_deleted && remote && remote_match) ||
(remote_deleted && head && head_match)) {
+ remove_entry(remove);
if (index)
return deleted_entry(index, index, o);
else if (ce && !head_deleted)
@@ -772,6 +787,7 @@ int threeway_merge(struct cache_entry **stages,
verify_uptodate(index, o);
}
+ remove_entry(remove);
o->nontrivial_merge = 1;
/* #2, #3, #4, #6, #7, #9, #10, #11. */
@@ -807,7 +823,8 @@ int threeway_merge(struct cache_entry **stages,
*
*/
int twoway_merge(struct cache_entry **src,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *current = src[0];
struct cache_entry *oldtree = src[1];
@@ -835,6 +852,7 @@ int twoway_merge(struct cache_entry **src,
}
else if (oldtree && !newtree && same(current, oldtree)) {
/* 10 or 11 */
+ remove_entry(remove);
return deleted_entry(oldtree, current, o);
}
else if (oldtree && newtree &&
@@ -844,6 +862,7 @@ int twoway_merge(struct cache_entry **src,
}
else {
/* all other failures */
+ remove_entry(remove);
if (oldtree)
reject_merge(oldtree);
if (current)
@@ -855,8 +874,8 @@ int twoway_merge(struct cache_entry **src,
}
else if (newtree)
return merged_entry(newtree, current, o);
- else
- return deleted_entry(oldtree, current, o);
+ remove_entry(remove);
+ return deleted_entry(oldtree, current, o);
}
/*
@@ -866,7 +885,8 @@ int twoway_merge(struct cache_entry **src,
* stage0 does not have anything there.
*/
int bind_merge(struct cache_entry **src,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -889,7 +909,8 @@ int bind_merge(struct cache_entry **src,
* - take the stat information from stage0, take the data from stage1
*/
int oneway_merge(struct cache_entry **src,
- struct unpack_trees_options *o)
+ struct unpack_trees_options *o,
+ int remove)
{
struct cache_entry *old = src[0];
struct cache_entry *a = src[1];
@@ -898,8 +919,10 @@ int oneway_merge(struct cache_entry **src,
return error("Cannot do a oneway merge of %d trees",
o->merge_size);
- if (!a)
+ if (!a) {
+ remove_entry(remove);
return deleted_entry(old, old, o);
+ }
if (old && same(old, a)) {
if (o->reset) {
struct stat st;
diff --git a/unpack-trees.h b/unpack-trees.h
index fee7da4382..5517faafad 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -4,7 +4,8 @@
struct unpack_trees_options;
typedef int (*merge_fn_t)(struct cache_entry **src,
- struct unpack_trees_options *options);
+ struct unpack_trees_options *options,
+ int remove);
struct unpack_trees_options {
int reset;
@@ -26,12 +27,12 @@ struct unpack_trees_options {
struct cache_entry *df_conflict_entry;
};
-extern int unpack_trees(struct object_list *trees,
+extern int unpack_trees(unsigned n, struct tree_desc *t,
struct unpack_trees_options *options);
-int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o);
-int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int bind_merge(struct cache_entry **src, struct unpack_trees_options *o);
-int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o);
+int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o, int);
+int twoway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int bind_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
+int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o, int);
#endif