Merge branch 'ff/submodule-no-fetch'
authorJunio C Hamano <gitster@pobox.com>
Sun, 15 Feb 2009 09:44:20 +0000 (01:44 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sun, 15 Feb 2009 09:44:20 +0000 (01:44 -0800)
* ff/submodule-no-fetch:
submodule: add --no-fetch parameter to update command

117 files changed:
.gitignore
Documentation/RelNotes-1.6.1.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.1.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.2.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-blame.txt
Documentation/git-bundle.txt
Documentation/git-filter-branch.txt
Documentation/git-gc.txt
Documentation/git-imap-send.txt
Documentation/git-notes.txt [deleted file]
Documentation/git-rebase.txt
Documentation/git-shortlog.txt
Documentation/git-svn.txt
Documentation/git.txt
Documentation/gitk.txt
Documentation/mailmap.txt [new file with mode: 0644]
Documentation/pretty-formats.txt
Makefile
archive.c
branch.c
builtin-blame.c
builtin-branch.c
builtin-clone.c
builtin-commit.c
builtin-fast-export.c
builtin-gc.c
builtin-ls-files.c
builtin-ls-tree.c
builtin-merge.c
builtin-receive-pack.c
builtin-remote.c
builtin-rev-list.c
builtin-revert.c
builtin-shortlog.c
builtin-symbolic-ref.c
cache.h
command-list.txt
commit.c
config.c
contrib/completion/git-completion.bash
contrib/emacs/Makefile
contrib/emacs/git.el
contrib/emacs/vc-git.el [deleted file]
contrib/hooks/post-receive-email
diff-lib.c
diff.c
diff.h
environment.c
fast-import.c
git-add--interactive.perl
git-filter-branch.sh
git-notes.sh [deleted file]
git-repack.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
gitweb/README
gitweb/gitweb.perl
http-push.c
log-tree.c
mailmap.c
mailmap.h
merge-recursive.c
notes.c [deleted file]
notes.h [deleted file]
path.c
pretty.c
refs.c
refs.h
rerere.c
revision.c
setup.c
sha1_file.c
sha1_name.c
string-list.c
string-list.h
t/t0060-path-utils.sh
t/t0100-previous.sh [new file with mode: 0755]
t/t1401-symbolic-ref.sh
t/t1500-rev-parse.sh
t/t1501-worktree.sh
t/t1504-ceiling-dirs.sh
t/t3301-notes.sh [deleted file]
t/t3302-notes-index-expensive.sh [deleted file]
t/t3400-rebase.sh
t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
t/t4013/diff.log_--patch-with-stat_master
t/t4013/diff.log_--patch-with-stat_master_--_dir_
t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
t/t4013/diff.log_--root_--patch-with-stat_--summary_master
t/t4013/diff.log_--root_--patch-with-stat_master
t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
t/t4013/diff.log_--root_-p_master
t/t4013/diff.log_--root_master
t/t4013/diff.log_-p_master
t/t4013/diff.log_master
t/t4013/diff.show_master
t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
t/t4020-diff-external.sh
t/t4203-mailmap.sh [new file with mode: 0755]
t/t5304-prune.sh
t/t5307-pack-missing-commit.sh [new file with mode: 0755]
t/t5400-send-pack.sh
t/t5505-remote.sh
t/t5540-http-push.sh
t/t5601-clone.sh
t/t7003-filter-branch.sh
t/t7400-submodule-basic.sh
t/t9131-git-svn-empty-symlink.sh
t/t9135-git-svn-moved-branch-empty-file.sh [new file with mode: 0755]
t/t9135/svn.dump [new file with mode: 0644]
test-path-utils.c
tree.c
walker.c
index 13311f1d5e32db381cc555a97e6eb405e63d5aba..1c57d4c958bc5e8ff539c5f5ddb1c784d923271b 100644 (file)
@@ -82,7 +82,6 @@ git-mktag
 git-mktree
 git-name-rev
 git-mv
-git-notes
 git-pack-redundant
 git-pack-objects
 git-pack-refs
diff --git a/Documentation/RelNotes-1.6.1.3.txt b/Documentation/RelNotes-1.6.1.3.txt
new file mode 100644 (file)
index 0000000..6f0bde1
--- /dev/null
@@ -0,0 +1,32 @@
+GIT v1.6.1.3 Release Notes
+==========================
+
+Fixes since v1.6.1.2
+--------------------
+
+* "git diff --binary | git apply" pipeline did not work well when
+  a binary blob is changed to a symbolic link.
+
+* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
+  not work as expected.
+
+* "git grep" did not pass the -I (ignore binary) option when
+  calling out an external grep program.
+
+* "git log" and friends include HEAD to the set of starting points
+  when --all is given.  This makes a difference when you are not
+  on any branch.
+
+* "git mv" to move an untracked file to overwrite a tracked
+  contents misbehaved.
+
+* "git merge -s octopus" with many potential merge bases did not
+  work correctly.
+
+* RPM binary package installed the html manpages in a wrong place.
+
+Also includes minor documentation fixes and updates.
+
+
+--
+git shortlog --no-merges v1.6.1.2-33-gc789350..
diff --git a/Documentation/RelNotes-1.6.1.4.txt b/Documentation/RelNotes-1.6.1.4.txt
new file mode 100644 (file)
index 0000000..a9f1a6b
--- /dev/null
@@ -0,0 +1,19 @@
+GIT v1.6.1.4 Release Notes
+==========================
+
+Fixes since v1.6.1.3
+--------------------
+
+* "git fast-export" produced wrong output with some parents missing from
+  commits, when the history is clock-skewed.
+
+* "git fast-import" sometimes failed to read back objects it just wrote
+  out and aborted, because it failed to flush stale cached data.
+
+* "git repack" did not error out when necessary object was missing in the
+  repository.
+
+Also includes minor documentation fixes and updates.
+
+--
+git shortlog --no-merges v1.6.1.3..
index 3151c85d880f520fbde338b8eaa0981e9ceaa510..6ed31595baa2325c224948ef7ddb75f62d5b2fb2 100644 (file)
@@ -1,6 +1,20 @@
 GIT v1.6.2 Release Notes
 ========================
 
+With the next major release, "git push" into a branch that is
+currently checked out will be refused by default.  You can choose
+what should happen upon such a push by setting the configuration
+variable receive.denyCurrentBranch in the receiving repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing.  Please refer to:
+
+  http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the transition plan.
+
+
 Updates since v1.6.1
 --------------------
 
@@ -11,7 +25,9 @@ Updates since v1.6.1
 * gitweb updates, including a new patch view and RSS/Atom feed
   improvements.
 
-(portability)
+* (contrib/emacs) git.el now has commands for checking out a branch,
+  creating a branch, cherry-picking and reverting commits; vc-git.el
+  is not shipped with git anymore (it is part of official Emacs).
 
 (performance)
 
@@ -29,12 +45,22 @@ Updates since v1.6.1
 
 * "git add -p" learned 'g'oto action to jump directly to a hunk.
 
+* "git add -p" learned to find a hunk with given text with '/'.
+
+* "git add -p" optionally can be told to work with just the command letter
+  without Enter.
+
 * when "git am" stops upon a patch that does not apply, it shows the
   title of the offending patch.
 
 * "git am --directory=<dir>" and "git am --reject" passes these options
   to underlying "git apply".
 
+* "git am" learned --ignore-date option.
+
+* "git blame" aligns author names better when they are spelled in
+  non US-ASCII encoding.
+
 * "git clone" now makes its best effort when cloning from an empty
   repository to set up configuration variables to refer to the remote
   repository.
@@ -48,6 +74,9 @@ Updates since v1.6.1
   the commit log message it serves via gitcvs.commitmsgannotation
   configuration.
 
+* "git cvsserver" learned to handle 'noop' command some CVS clients seem
+  to expect to work.
+
 * "git diff" learned a new option --inter-hunk-context to coalesce close
   hunks together and show context between them.
 
@@ -56,20 +85,13 @@ Updates since v1.6.1
 
 * "git diff" learned --patience to run "patience diff" algorithm.
 
-* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
-  not work as expected.
-
 * "git filter-branch" learned --prune-empty option that discards commits
   that do not change the contents.
 
-* "git grep -w" and "git grep" for fixed strings have been optimized.
-
-* "git log" and friends include HEAD to the set of starting points
-  when --all is given.  This makes a difference when you are not on
-  any branch.
+* "git fsck" now checks loose objects in alternate object stores, instead
+  of misreporting them as missing.
 
-* "git ls-tree" learned --full-tree option that shows the path in full
-  regardless of where in the work tree hierarchy the command was started.
+* "git grep -w" and "git grep" for fixed strings have been optimized.
 
 * "git mergetool" learned -y(--no-prompt) option to disable prompting.
 
@@ -80,7 +102,8 @@ Updates since v1.6.1
   "git checkout" switches branches, taking the local changes while
   switching to another commit.
 
-(internal)
+* "git tag" learned --contains that works the same way as the same option
+  from "git branch".
 
 
 Fixes since v1.6.1
@@ -89,18 +112,34 @@ Fixes since v1.6.1
 All of the fixes in v1.6.1.X maintenance series are included in this
 release, unless otherwise noted.
 
+Here are fixes that this release has, but have not been backported to
+v1.6.1.X series.
+
 * "git-add sub/file" when sub is a submodule incorrectly added the path to
   the superproject.
 
-* git-bundle did not exclude annotated tags even when a range given from the
-  command line wanted to.
+* "git bundle" did not exclude annotated tags even when a range given
+  from the command line wanted to.
+
+* "git filter-branch" unnecessarily refused to work when you had
+  checked out a different commit from what is recorded in the superproject
+  index in a submodule.
+
+* "git filter-branch" incorrectly tried to update a nonexistent work tree
+  at the end when it is run in a bare repository.
+
+* "git mergetool" used to ignore autocrlf and other attributes
+  based content rewriting.
 
 * branch switching and merges had a silly bug that did not validate
   the correct directory when making sure an existing subdirectory is
   clean.
 
+* "git -p cmd" when cmd is not a built-in one left the display in funny state
+  when killed in the middle.
+
 --
 exec >/var/tmp/1
-O=v1.6.1.2-252-g8c95d3c
+O=v1.6.1.3-371-gc19923a
 echo O=$(git describe master)
 git shortlog --no-merges $O..master ^maint
index ba07c8c57107a8438d5b3ce1a49761dba7182d25..9b559adefce5c9a89e58d30ae79820a9ef2e9072 100644 (file)
@@ -376,9 +376,36 @@ Thunderbird
 
 (A Large Angry SCM)
 
+By default, Thunderbird will both wrap emails as well as flag them as
+being 'format=flowed', both of which will make the resulting email unusable
+by git.
+
 Here are some hints on how to successfully submit patches inline using
 Thunderbird.
 
+There are two different approaches.  One approach is to configure
+Thunderbird to not mangle patches.  The second approach is to use
+an external editor to keep Thunderbird from mangling the patches.
+
+Approach #1 (configuration):
+
+This recipe is current as of Thunderbird 2.0.0.19.  Three steps:
+  1.  Configure your mail server composition as plain text
+      Edit...Account Settings...Composition & Addressing,
+        uncheck 'Compose Messages in HTML'.
+  2.  Configure your general composition window to not wrap
+      Edit..Preferences..Composition, wrap plain text messages at 0
+  3.  Disable the use of format=flowed
+      Edit..Preferences..Advanced..Config Editor.  Search for:
+        mailnews.send_plaintext_flowed
+      toggle it to make sure it is set to 'false'.
+
+After that is done, you should be able to compose email as you
+otherwise would (cut + paste, git-format-patch | git-imap-send, etc),
+and the patches should not be mangled.
+
+Approach #2 (external editor):
+
 This recipe appears to work with the current [*1*] Thunderbird from Suse.
 
 The following Thunderbird extensions are needed:
index 7fbf64d24cabbe609cf10b34055165a323d4e8b0..f5152c5038b49ab5ebe3804e8e9b314d4a752690 100644 (file)
@@ -422,19 +422,6 @@ relatively high IO latencies.  With this set to 'true', git will do the
 index comparison to the filesystem data in parallel, allowing
 overlapping IO's.
 
-core.notesRef::
-       When showing commit messages, also show notes which are stored in
-       the given ref.  This ref is expected to contain files named
-       after the full SHA-1 of the commit they annotate.
-+
-If such a file exists in the given ref, the referenced blob is read, and
-appended to the commit message, separated by a "Notes:" line.  If the
-given ref itself does not exist, it is not an error, but means that no
-notes should be printed.
-+
-This setting defaults to "refs/notes/commits", and can be overridden by
-the `GIT_NOTES_REF` environment variable.
-
 alias.*::
        Command aliases for the linkgit:git[1] command wrapper - e.g.
        after defining "alias.last = cat-file commit HEAD", the invocation
@@ -569,8 +556,8 @@ color.interactive::
 
 color.interactive.<slot>::
        Use customized color for 'git-add --interactive'
-       output. `<slot>` may be `prompt`, `header`, or `help`, for
-       three distinct types of normal output from interactive
+       output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
+       four distinct types of normal output from interactive
        programs.  The values of these variables may be specified as
        in color.branch.<slot>.
 
@@ -1013,6 +1000,13 @@ instaweb.port::
        The port number to bind the gitweb httpd to. See
        linkgit:git-instaweb[1].
 
+interactive.singlekey::
+       In interactive programs, allow the user to provide one-letter
+       input with a single key (i.e., without hitting enter).
+       Currently this is used only by the `\--patch` mode of
+       linkgit:git-add[1].  Note that this setting is silently
+       ignored if portable keystroke input is not available.
+
 log.date::
        Set default date-time mode for the log command. Setting log.date
        value is similar to using 'git-log'\'s --date option. The value is one of the
@@ -1025,6 +1019,14 @@ log.showroot::
        Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
        normally hide the root commit will now show it. True by default.
 
+mailmap.file::
+       The location of an augmenting mailmap file. The default
+       mailmap, located in the root of the repository, is loaded
+       first, then the mailmap file pointed to by this variable.
+       The location of the mailmap file may be in a repository
+       subdirectory, or somewhere outside of the repository itself.
+       See linkgit:git-shortlog[1] and linkgit:git-blame[1].
+
 man.viewer::
        Specify the programs that may be used to display help in the
        'man' format. See linkgit:git-help[1].
index fba374d652723161c3683d1be98c08ba573057cc..6999cf2a65723af963e087f57bf4f8bbd1a9350e 100644 (file)
@@ -184,6 +184,12 @@ there is ever added information (like the commit encoding or extended
 commit commentary), a blame viewer won't ever care.
 
 
+MAPPING AUTHORS
+---------------
+
+include::mailmap.txt[]
+
+
 SEE ALSO
 --------
 linkgit:git-annotate[1]
index ea0f6a0f3ab93b80df7a3b305aed07b312dcfdb5..57590b148011c44485d28d93d31c6a75a681bfb0 100644 (file)
@@ -107,17 +107,17 @@ incremental bundle,
 
 ----------------
 machineA$ cd R1
-machineA$ git bundle create file.bdl master
+machineA$ git bundle create file.bundle master
 machineA$ git tag -f lastR2bundle master
 ----------------
 
-Then you sneakernet file.bdl to the target machine B. Because you don't
+Then you sneakernet file.bundle to the target machine B. Because you don't
 have to have any object to extract objects from such a bundle, not only
 you can fetch/pull from a bundle, you can clone from it as if it was a
 remote repository.
 
 ----------------
-machineB$ git clone /home/me/tmp/file.bdl R2
+machineB$ git clone /home/me/tmp/file.bundle R2
 ----------------
 
 This will define a remote called "origin" in the resulting repository that
@@ -126,12 +126,12 @@ have an entry like this:
 
 ------------------------
 [remote "origin"]
-    url = /home/me/tmp/file.bdl
+    url = /home/me/tmp/file.bundle
     fetch = refs/heads/*:refs/remotes/origin/*
 ------------------------
 
 You can fetch/pull to update the resulting mine.git repository after
-replacing the bundle you store at /home/me/tmp/file.bdl with incremental
+replacing the bundle you store at /home/me/tmp/file.bundle with incremental
 updates from here on.
 
 After working more in the original repository, you can create an
@@ -139,11 +139,11 @@ incremental bundle to update the other:
 
 ----------------
 machineA$ cd R1
-machineA$ git bundle create file.bdl lastR2bundle..master
+machineA$ git bundle create file.bundle lastR2bundle..master
 machineA$ git tag -f lastR2bundle master
 ----------------
 
-and sneakernet it to the other machine to replace /home/me/tmp/file.bdl,
+and sneakernet it to the other machine to replace /home/me/tmp/file.bundle,
 and pull from it.
 
 ----------------
index 451950bab67702a236c55e77d6f3a0723a812488..7ffe03f4279a8ca8110806260f33a58f849351e4 100644 (file)
@@ -212,6 +212,11 @@ git filter-branch --index-filter 'git rm --cached filename' HEAD
 
 Now, you will get the rewritten history saved in HEAD.
 
+As with using `rm filename`, `git rm --cached filename` will fail
+if the file is absent from the tree of a commit.  If it is not important
+whether the file is already absent from the tree, you can use
+`git rm --cached --ignore-unmatch filename` instead.
+
 To rewrite the repository to look as if `foodir/` had been its project
 root, and discard all other history:
 
@@ -334,6 +339,47 @@ git filter-branch --index-filter \
 ---------------------------------------------------------------
 
 
+
+Checklist for Shrinking a Repository
+------------------------------------
+
+git-filter-branch is often used to get rid of a subset of files,
+usually with some combination of `\--index-filter` and
+`\--subdirectory-filter`.  People expect the resulting repository to
+be smaller than the original, but you need a few more steps to
+actually make it smaller, because git tries hard not to lose your
+objects until you tell it to.  First make sure that:
+
+* You really removed all variants of a filename, if a blob was moved
+  over its lifetime.  `git log \--name-only \--follow \--all \--
+  filename` can help you find renames.
+
+* You really filtered all refs: use `\--tag-name-filter cat \--
+  \--all` when calling git-filter-branch.
+
+Then there are two ways to get a smaller repository.  A safer way is
+to clone, that keeps your original intact.
+
+* Clone it with `git clone +++file:///path/to/repo+++`.  The clone
+  will not have the removed objects.  See linkgit:git-clone[1].  (Note
+  that cloning with a plain path just hardlinks everything!)
+
+If you really don't want to clone it, for whatever reasons, check the
+following points instead (in this order).  This is a very destructive
+approach, so *make a backup* or go back to cloning it.  You have been
+warned.
+
+* Remove the original refs backed up by git-filter-branch: say `git
+  for-each-ref \--format="%(refname)" refs/original/ | xargs -n 1 git
+  update-ref -d`.
+
+* Expire all reflogs with `git reflog expire \--expire=now \--all`.
+
+* Garbage collect all unreferenced objects with `git gc \--prune=now`
+  (or if your git-gc is not new enough to support arguments to
+  `\--prune`, use `git repack -ad; git prune` instead).
+
+
 Author
 ------
 Written by Petr "Pasky" Baudis <pasky@suse.cz>,
index 7086eea74a38b036130f61db362bae209a065e47..b292e9843aa9da86cd44bd07d3ce35053be32177 100644 (file)
@@ -8,7 +8,7 @@ git-gc - Cleanup unnecessary files and optimize the local repository
 
 SYNOPSIS
 --------
-'git gc' [--aggressive] [--auto] [--quiet]
+'git gc' [--aggressive] [--auto] [--quiet] [--prune=<date> | --no-prune]
 
 DESCRIPTION
 -----------
@@ -59,6 +59,14 @@ are consolidated into a single pack by using the `-A` option of
 'git-repack'. Setting `gc.autopacklimit` to 0 disables
 automatic consolidation of packs.
 
+--prune=<date>::
+       Prune loose objects older than date (default is 2 weeks ago,
+       overrideable by the config variable `gc.pruneExpire`).  This
+       option is on by default.
+
+--no-prune::
+       Do not prune any loose objects.
+
 --quiet::
        Suppress all progress reports.
 
index bd49a0aee8881f983077d74bd8d0fbe5764dfea5..1685f04efea3d162377325406e08a762dad94d40 100644 (file)
@@ -98,6 +98,20 @@ Using direct mode with SSL:
 ..........................
 
 
+CAUTION
+-------
+It is still your responsibility to make sure that the email message
+sent by your email program meets the standards of your project.
+Many projects do not like patches to be attached.  Some mail
+agents will transform patches (e.g. wrap lines, send them as
+format=flowed) in ways that make them fail.  You will get angry
+flames ridiculing you if you don't check this.
+
+Thunderbird in particular is known to be problematic.  Thunderbird
+users may wish to visit this web page for more information:
+  http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
+
+
 BUGS
 ----
 Doesn't handle lines starting with "From " in the message body.
diff --git a/Documentation/git-notes.txt b/Documentation/git-notes.txt
deleted file mode 100644 (file)
index 3d93625..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-git-notes(1)
-============
-
-NAME
-----
-git-notes - Add/inspect commit notes
-
-SYNOPSIS
---------
-[verse]
-'git-notes' (edit | show) [commit]
-
-DESCRIPTION
------------
-This command allows you to add notes to commit messages, without
-changing the commit.  To discern these notes from the message stored
-in the commit object, the notes are indented like the message, after
-an unindented line saying "Notes:".
-
-To disable commit notes, you have to set the config variable
-core.notesRef to the empty string.  Alternatively, you can set it
-to a different ref, something like "refs/notes/bugzilla".  This setting
-can be overridden by the environment variable "GIT_NOTES_REF".
-
-
-SUBCOMMANDS
------------
-
-edit::
-       Edit the notes for a given commit (defaults to HEAD).
-
-show::
-       Show the notes for a given commit (defaults to HEAD).
-
-
-Author
-------
-Written by Johannes Schindelin <johannes.schindelin@gmx.de>
-
-Documentation
--------------
-Documentation by Johannes Schindelin
-
-GIT
----
-Part of the gitlink:git[7] suite
index 3d6d429e5e28d6a4520c3b54adb3ad065a3b774f..30487de48fa83aa63b83043e090053c07c54456a 100644 (file)
@@ -246,6 +246,7 @@ OPTIONS
 --whitespace=<nowarn|warn|error|error-all|strip>::
        This flag is passed to the 'git-apply' program
        (see linkgit:git-apply[1]) that applies the patch.
+       Incompatible with the --interactive option.
 
 -i::
 --interactive::
index 498bd289297803e372cf6bc0079d040c23b0a6bb..42463a955dd3f5dad25f3a45f0eec62d8712f731 100644 (file)
@@ -45,45 +45,16 @@ OPTIONS
        and subsequent lines are indented by `indent2` spaces. `width`,
        `indent1`, and `indent2` default to 76, 6 and 9 respectively.
 
-FILES
------
-
-If a file `.mailmap` exists at the toplevel of the repository,
-it is used to map an author email address to a canonical real name. This
-can be used to coalesce together commits by the same person where their
-name was spelled differently (whether with the same email address or
-not).
-
-Each line in the file consists, in this order, of the canonical real name
-of an author, whitespace, and an email address (enclosed by '<' and '>')
-to map to the name. Use hash '#' for comments, either on their own line,
-or after the email address.
-
-A canonical name may appear in more than one line, associated with
-different email addresses, but it doesn't make sense for a given address
-to appear more than once (if that happens, a later line overrides the
-earlier ones).
-
-So, for example, if your history contains commits by two authors, Jane
-and Joe, whose names appear in the repository under several forms:
-
-------------
-Joe Developer <joe@example.com>
-Joe R. Developer <joe@example.com>
-Jane Doe <jane@example.com>
-Jane Doe <jane@laptop.(none)>
-Jane D. <jane@desktop.(none)>
-------------
-
-Then, supposing Joe wants his middle name initial used, and Jane prefers
-her family name fully spelled out, a proper `.mailmap` file would look like:
-
-------------
-# Note how we don't need an entry for <jane@laptop.(none)>, because the
-# real name of that author is correct already, and coalesced directly.
-Jane Doe <jane@desktop.(none)>
-Joe R. Developer <joe@example.com>
-------------
+
+MAPPING AUTHORS
+---------------
+
+The `.mailmap` feature is used to coalesce together commits by the same
+person in the shortlog, where their name and/or email address was
+spelled differently.
+
+include::mailmap.txt[]
+
 
 Author
 ------
index 7b654f7928c668abec364a33478ed4be70dfb01a..3d456545d7bf6b81a6ea1ab7b3671939ba3be045 100644 (file)
@@ -499,6 +499,14 @@ svn-remote.<name>.rewriteRoot::
        the repository with a public http:// or svn:// URL in the
        metadata so users of it will see the public URL.
 
+svn.brokenSymlinkWorkaround::
+This disables potentially expensive checks to workaround broken symlinks
+checked into SVN by broken clients.  Set this option to "false" if you
+track a SVN repository with many empty blobs that are not symlinks.
+This option may be changed while "git-svn" is running and take effect on
+the next revision fetched.  If unset, git-svn assumes this option to be
+"true".
+
 --
 
 Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
index cd527c6252a678e0bd5e6fa918771efbfe51bd47..0c7bba3fa98ed10c9c4857ed14e45fab5bba10cf 100644 (file)
@@ -43,9 +43,11 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.1.1/git.html[documentation for release 1.6.1.1]
+* link:v1.6.1.3/git.html[documentation for release 1.6.1.3]
 
 * release notes for
+  link:RelNotes-1.6.1.3.txt[1.6.1.3],
+  link:RelNotes-1.6.1.2.txt[1.6.1.2],
   link:RelNotes-1.6.1.1.txt[1.6.1.1],
   link:RelNotes-1.6.1.txt[1.6.1].
 
index 4673a75a98da1f778c1323d8e5a4f7faff388589..bd005bc5c8c7d877ea9de79e2f6452d5703d0892 100644 (file)
@@ -47,7 +47,8 @@ frequently used options.
 
        After an attempt to merge stops with conflicts, show the commits on
        the history between two branches (i.e. the HEAD and the MERGE_HEAD)
-       that modify the conflicted files.
+       that modify the conflicted files and do not exist on all the heads
+       being merged.
 
 --argscmd=<command>::
        Command to be run each time gitk has to determine the list of
diff --git a/Documentation/mailmap.txt b/Documentation/mailmap.txt
new file mode 100644 (file)
index 0000000..e25b154
--- /dev/null
@@ -0,0 +1,75 @@
+If the file `.mailmap` exists at the toplevel of the repository, or at
+the location pointed to by the mailmap.file configuration option, it
+is used to map author and committer names and email addresses to
+canonical real names and email addresses.
+
+In the simple form, each line in the file consists of the canonical
+real name of an author, whitespace, and an email address used in the
+commit (enclosed by '<' and '>') to map to the name. Thus, looks like
+this
+--
+       Proper Name <commit@email.xx>
+--
+
+The more complex forms are
+--
+       <proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace only the email part of a commit, and
+--
+       Proper Name <proper@email.xx> <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a
+commit matching the specified commit email address, and
+--
+       Proper Name <proper@email.xx> Commit Name <commit@email.xx>
+--
+which allows mailmap to replace both the name and the email of a
+commit matching both the specified commit name and email address.
+
+Example 1: Your history contains commits by two authors, Jane
+and Joe, whose names appear in the repository under several forms:
+
+------------
+Joe Developer <joe@example.com>
+Joe R. Developer <joe@example.com>
+Jane Doe <jane@example.com>
+Jane Doe <jane@laptop.(none)>
+Jane D. <jane@desktop.(none)>
+------------
+
+Now suppose that Joe wants his middle name initial used, and Jane
+prefers her family name fully spelled out. A proper `.mailmap` file
+would look like:
+
+------------
+Jane Doe         <jane@desktop.(none)>
+Joe R. Developer <joe@example.com>
+------------
+
+Note how we don't need an entry for <jane@laptop.(none)>, because the
+real name of that author is correct already.
+
+Example 2: Your repository contains commits from the following
+authors:
+
+------------
+nick1 <bugs@company.xx>
+nick2 <bugs@company.xx>
+nick2 <nick2@company.xx>
+santa <me@company.xx>
+claus <me@company.xx>
+CTO <cto@coompany.xx>
+------------
+
+Then, you might want a `.mailmap` file looking like:
+------------
+<cto@company.xx>                       <cto@coompany.xx>
+Some Dude <some@dude.xx>         nick1 <bugs@company.xx>
+Other Author <other@author.xx>   nick2 <bugs@company.xx>
+Other Author <other@author.xx>         <nick2@company.xx>
+Santa Claus <santa.claus@northpole.xx> <me@company.xx>
+------------
+
+Use hash '#' for comments that are either on their own line, or after
+the email address.
\ No newline at end of file
index 3d87d3edd55e2ea1d73907bbf391d42d9806fae3..159390c35a9413590b83f512c3f9f59e6a275e77 100644 (file)
@@ -101,16 +101,18 @@ The placeholders are:
 - '%P': parent hashes
 - '%p': abbreviated parent hashes
 - '%an': author name
-- '%aN': author name (respecting .mailmap)
+- '%aN': author name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ae': author email
+- '%aE': author email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ad': author date (format respects --date= option)
 - '%aD': author date, RFC2822 style
 - '%ar': author date, relative
 - '%at': author date, UNIX timestamp
 - '%ai': author date, ISO 8601 format
 - '%cn': committer name
-- '%cN': committer name (respecting .mailmap)
+- '%cN': committer name (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%ce': committer email
+- '%cE': committer email (respecting .mailmap, see linkgit:git-shortlog[1] or linkgit:git-blame[1])
 - '%cd': committer date
 - '%cD': committer date, RFC2822 style
 - '%cr': committer date, relative
index 27b9569746179e68c635bdaab8e57395f63faf01..b040a96e50a30c5413f109d4e49d9c538625918f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -228,7 +228,7 @@ GITWEB_FAVICON = git-favicon.png
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
 
-export prefix bindir sharedir htmldir sysconfdir
+export prefix bindir sharedir sysconfdir
 
 CC = gcc
 AR = ar
@@ -265,7 +265,6 @@ SCRIPT_SH += git-merge-octopus.sh
 SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
-SCRIPT_SH += git-notes.sh
 SCRIPT_SH += git-parse-remote.sh
 SCRIPT_SH += git-pull.sh
 SCRIPT_SH += git-quiltimport.sh
@@ -318,8 +317,8 @@ PROGRAMS += git-var$X
 # builtin-$C.o but is linked in as part of some other command.
 BUILT_INS += $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
 
-BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-cherry$X
+BUILT_INS += git-cherry-pick$X
 BUILT_INS += git-format-patch$X
 BUILT_INS += git-fsck-objects$X
 BUILT_INS += git-get-tar-commit-id$X
@@ -358,8 +357,8 @@ LIB_H += builtin.h
 LIB_H += cache.h
 LIB_H += cache-tree.h
 LIB_H += commit.h
-LIB_H += compat/mingw.h
 LIB_H += compat/cygwin.h
+LIB_H += compat/mingw.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
@@ -378,14 +377,12 @@ LIB_H += ll-merge.h
 LIB_H += log-tree.h
 LIB_H += mailmap.h
 LIB_H += merge-recursive.h
-LIB_H += notes.h
 LIB_H += object.h
 LIB_H += pack.h
 LIB_H += pack-refs.h
 LIB_H += pack-revindex.h
 LIB_H += parse-options.h
 LIB_H += patch-ids.h
-LIB_H += string-list.h
 LIB_H += pkt-line.h
 LIB_H += progress.h
 LIB_H += quote.h
@@ -399,6 +396,7 @@ LIB_H += sha1-lookup.h
 LIB_H += sideband.h
 LIB_H += sigchain.h
 LIB_H += strbuf.h
+LIB_H += string-list.h
 LIB_H += tag.h
 LIB_H += transport.h
 LIB_H += tree.h
@@ -437,8 +435,8 @@ LIB_OBJS += diffcore-order.o
 LIB_OBJS += diffcore-pickaxe.o
 LIB_OBJS += diffcore-rename.o
 LIB_OBJS += diff-delta.o
-LIB_OBJS += diff-no-index.o
 LIB_OBJS += diff-lib.o
+LIB_OBJS += diff-no-index.o
 LIB_OBJS += diff.o
 LIB_OBJS += dir.o
 LIB_OBJS += editor.o
@@ -461,7 +459,6 @@ LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += name-hash.o
-LIB_OBJS += notes.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-refs.o
@@ -471,9 +468,9 @@ LIB_OBJS += pager.o
 LIB_OBJS += parse-options.o
 LIB_OBJS += patch-delta.o
 LIB_OBJS += patch-ids.o
-LIB_OBJS += string-list.o
 LIB_OBJS += path.o
 LIB_OBJS += pkt-line.o
+LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
 LIB_OBJS += progress.o
 LIB_OBJS += quote.o
@@ -487,13 +484,14 @@ LIB_OBJS += revision.o
 LIB_OBJS += run-command.o
 LIB_OBJS += server-info.o
 LIB_OBJS += setup.o
-LIB_OBJS += sha1_file.o
 LIB_OBJS += sha1-lookup.o
+LIB_OBJS += sha1_file.o
 LIB_OBJS += sha1_name.o
 LIB_OBJS += shallow.o
 LIB_OBJS += sideband.o
 LIB_OBJS += sigchain.o
 LIB_OBJS += strbuf.o
+LIB_OBJS += string-list.o
 LIB_OBJS += symlinks.o
 LIB_OBJS += tag.o
 LIB_OBJS += trace.o
@@ -502,8 +500,8 @@ LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
-LIB_OBJS += userdiff.o
 LIB_OBJS += usage.o
+LIB_OBJS += userdiff.o
 LIB_OBJS += utf8.o
 LIB_OBJS += walker.o
 LIB_OBJS += wrapper.o
@@ -511,7 +509,6 @@ LIB_OBJS += write_or_die.o
 LIB_OBJS += ws.o
 LIB_OBJS += wt-status.o
 LIB_OBJS += xdiff-interface.o
-LIB_OBJS += preload-index.o
 
 BUILTIN_OBJS += builtin-add.o
 BUILTIN_OBJS += builtin-annotate.o
index 9ac455d889b72deba8c949da1d9efe2be3a50244..e6de0397cc82ae97018cbf4fc82d5697ec4d915d 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -132,7 +132,7 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                err = write_entry(args, sha1, path.buf, path.len, mode, NULL, 0);
                if (err)
                        return err;
-               return READ_TREE_RECURSIVE;
+               return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
        }
 
        buffer = sha1_file_to_archive(path_without_prefix, sha1, mode,
index b1ac837f3d30c826ddbe29492b906bf2d0de0a1a..1f00e44deb28afe74ae4f0b85b23039476032fe5 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -103,14 +103,22 @@ void create_branch(const char *head,
        struct ref_lock *lock;
        struct commit *commit;
        unsigned char sha1[20];
-       char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20];
+       char *real_ref, msg[PATH_MAX + 20];
+       struct strbuf ref = STRBUF_INIT;
        int forcing = 0;
+       int len;
 
-       snprintf(ref, sizeof ref, "refs/heads/%s", name);
-       if (check_ref_format(ref))
+       len = strlen(name);
+       if (interpret_nth_last_branch(name, &ref) != len) {
+               strbuf_reset(&ref);
+               strbuf_add(&ref, name, len);
+       }
+       strbuf_splice(&ref, 0, 0, "refs/heads/", 11);
+
+       if (check_ref_format(ref.buf))
                die("'%s' is not a valid branch name.", name);
 
-       if (resolve_ref(ref, sha1, 1, NULL)) {
+       if (resolve_ref(ref.buf, sha1, 1, NULL)) {
                if (!force)
                        die("A branch named '%s' already exists.", name);
                else if (!is_bare_repository() && !strcmp(head, name))
@@ -142,7 +150,7 @@ void create_branch(const char *head,
                die("Not a valid branch point: '%s'.", start_name);
        hashcpy(sha1, commit->object.sha1);
 
-       lock = lock_any_ref_for_update(ref, NULL, 0);
+       lock = lock_any_ref_for_update(ref.buf, NULL, 0);
        if (!lock)
                die("Failed to lock ref for update: %s.", strerror(errno));
 
@@ -162,6 +170,7 @@ void create_branch(const char *head,
        if (write_ref_sha1(lock, sha1, msg) < 0)
                die("Failed to write ref: %s.", strerror(errno));
 
+       strbuf_release(&ref);
        free(real_ref);
 }
 
index 9b9f5442a277082c8ad04295107eb9ddd6c95b19..114a214ed3fef40ae5cc13737d037f29d6f8acfd 100644 (file)
@@ -1264,11 +1264,12 @@ struct commit_info
  * Parse author/committer line in the commit object buffer
  */
 static void get_ac_line(const char *inbuf, const char *what,
-                       int bufsz, char *person, const char **mail,
+                       int person_len, char *person,
+                       int mail_len, char *mail,
                        unsigned long *time, const char **tz)
 {
        int len, tzlen, maillen;
-       char *tmp, *endp, *timepos;
+       char *tmp, *endp, *timepos, *mailpos;
 
        tmp = strstr(inbuf, what);
        if (!tmp)
@@ -1279,10 +1280,11 @@ static void get_ac_line(const char *inbuf, const char *what,
                len = strlen(tmp);
        else
                len = endp - tmp;
-       if (bufsz <= len) {
+       if (person_len <= len) {
        error_out:
                /* Ugh */
-               *mail = *tz = "(unknown)";
+               *tz = "(unknown)";
+               strcpy(mail, *tz);
                *time = 0;
                return;
        }
@@ -1305,9 +1307,10 @@ static void get_ac_line(const char *inbuf, const char *what,
        *tmp = 0;
        while (*tmp != ' ')
                tmp--;
-       *mail = tmp + 1;
+       mailpos = tmp + 1;
        *tmp = 0;
        maillen = timepos - tmp;
+       memcpy(mail, mailpos, maillen);
 
        if (!mailmap.nr)
                return;
@@ -1316,20 +1319,23 @@ static void get_ac_line(const char *inbuf, const char *what,
         * mailmap expansion may make the name longer.
         * make room by pushing stuff down.
         */
-       tmp = person + bufsz - (tzlen + 1);
+       tmp = person + person_len - (tzlen + 1);
        memmove(tmp, *tz, tzlen);
        tmp[tzlen] = 0;
        *tz = tmp;
 
-       tmp = tmp - (maillen + 1);
-       memmove(tmp, *mail, maillen);
-       tmp[maillen] = 0;
-       *mail = tmp;
-
        /*
-        * Now, convert e-mail using mailmap
+        * Now, convert both name and e-mail using mailmap
         */
-       map_email(&mailmap, tmp + 1, person, tmp-person-1);
+       if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+               /* Add a trailing '>' to email, since map_user returns plain emails
+                  Note: It already has '<', since we replace from mail+1 */
+               mailpos = memchr(mail, '\0', mail_len);
+               if (mailpos && mailpos-mail < mail_len - 1) {
+                       *mailpos = '>';
+                       *(mailpos+1) = '\0';
+               }
+       }
 }
 
 static void get_commit_info(struct commit *commit,
@@ -1338,8 +1344,10 @@ static void get_commit_info(struct commit *commit,
 {
        int len;
        char *tmp, *endp, *reencoded, *message;
-       static char author_buf[1024];
-       static char committer_buf[1024];
+       static char author_name[1024];
+       static char author_mail[1024];
+       static char committer_name[1024];
+       static char committer_mail[1024];
        static char summary_buf[1024];
 
        /*
@@ -1357,9 +1365,11 @@ static void get_commit_info(struct commit *commit,
        }
        reencoded = reencode_commit_message(commit, NULL);
        message   = reencoded ? reencoded : commit->buffer;
-       ret->author = author_buf;
+       ret->author = author_name;
+       ret->author_mail = author_mail;
        get_ac_line(message, "\nauthor ",
-                   sizeof(author_buf), author_buf, &ret->author_mail,
+                   sizeof(author_name), author_name,
+                   sizeof(author_mail), author_mail,
                    &ret->author_time, &ret->author_tz);
 
        if (!detailed) {
@@ -1367,9 +1377,11 @@ static void get_commit_info(struct commit *commit,
                return;
        }
 
-       ret->committer = committer_buf;
+       ret->committer = committer_name;
+       ret->committer_mail = committer_mail;
        get_ac_line(message, "\ncommitter ",
-                   sizeof(committer_buf), committer_buf, &ret->committer_mail,
+                   sizeof(committer_name), committer_name,
+                   sizeof(committer_mail), committer_mail,
                    &ret->committer_time, &ret->committer_tz);
 
        ret->summary = summary_buf;
@@ -2396,7 +2408,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                die("reading graft file %s failed: %s",
                    revs_file, strerror(errno));
 
-       read_mailmap(&mailmap, ".mailmap", NULL);
+       read_mailmap(&mailmap, NULL);
 
        if (!incremental)
                setup_pager();
index 56a1971d690e32ca1cb0bc2d66ee05be48bfe565..504a981ad56f73a547c11bf3e18185f67111d000 100644 (file)
@@ -99,6 +99,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
        const char *fmt, *remote;
        int i;
        int ret = 0;
+       struct strbuf bname = STRBUF_INIT;
 
        switch (kinds) {
        case REF_REMOTE_BRANCH:
@@ -119,20 +120,25 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                if (!head_rev)
                        die("Couldn't look up commit object for HEAD");
        }
-       for (i = 0; i < argc; i++) {
-               if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) {
+       for (i = 0; i < argc; i++, strbuf_release(&bname)) {
+               int len = strlen(argv[i]);
+
+               if (interpret_nth_last_branch(argv[i], &bname) != len)
+                       strbuf_add(&bname, argv[i], len);
+
+               if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) {
                        error("Cannot delete the branch '%s' "
-                               "which you are currently on.", argv[i]);
+                             "which you are currently on.", bname.buf);
                        ret = 1;
                        continue;
                }
 
                free(name);
 
-               name = xstrdup(mkpath(fmt, argv[i]));
+               name = xstrdup(mkpath(fmt, bname.buf));
                if (!resolve_ref(name, sha1, 1, NULL)) {
                        error("%sbranch '%s' not found.",
-                                       remote, argv[i]);
+                                       remote, bname.buf);
                        ret = 1;
                        continue;
                }
@@ -152,22 +158,23 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                if (!force &&
                    !in_merge_bases(rev, &head_rev, 1)) {
                        error("The branch '%s' is not an ancestor of "
-                               "your current HEAD.\n"
-                               "If you are sure you want to delete it, "
-                               "run 'git branch -D %s'.", argv[i], argv[i]);
+                             "your current HEAD.\n"
+                             "If you are sure you want to delete it, "
+                             "run 'git branch -D %s'.", bname.buf, bname.buf);
                        ret = 1;
                        continue;
                }
 
                if (delete_ref(name, sha1, 0)) {
                        error("Error deleting %sbranch '%s'", remote,
-                              argv[i]);
+                             bname.buf);
                        ret = 1;
                } else {
                        struct strbuf buf = STRBUF_INIT;
-                       printf("Deleted %sbranch %s (%s).\n", remote, argv[i],
-                               find_unique_abbrev(sha1, DEFAULT_ABBREV));
-                       strbuf_addf(&buf, "branch.%s", argv[i]);
+                       printf("Deleted %sbranch %s (%s).\n", remote,
+                              bname.buf,
+                              find_unique_abbrev(sha1, DEFAULT_ABBREV));
+                       strbuf_addf(&buf, "branch.%s", bname.buf);
                        if (git_config_rename_section(buf.buf, NULL) < 0)
                                warning("Update of config-file failed");
                        strbuf_release(&buf);
index f73029e2ba285bfb20bce1760cef711f7a55fd02..c338910b1c76f3c994e4a22c9c0a1da38843e050 100644 (file)
@@ -350,6 +350,19 @@ static struct ref *write_remote_refs(const struct ref *refs,
        return local_refs;
 }
 
+static void install_branch_config(const char *local,
+                                 const char *origin,
+                                 const char *remote)
+{
+       struct strbuf key = STRBUF_INIT;
+       strbuf_addf(&key, "branch.%s.remote", local);
+       git_config_set(key.buf, origin);
+       strbuf_reset(&key);
+       strbuf_addf(&key, "branch.%s.merge", local);
+       git_config_set(key.buf, remote);
+       strbuf_release(&key);
+}
+
 int cmd_clone(int argc, const char **argv, const char *prefix)
 {
        int use_local_hardlinks = 1;
@@ -539,6 +552,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                head_points_at = NULL;
                remote_head = NULL;
                option_no_checkout = 1;
+               if (!option_bare)
+                       install_branch_config("master", option_origin,
+                                             "refs/heads/master");
        }
 
        if (head_points_at) {
@@ -567,11 +583,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                                      head_points_at->peer_ref->name,
                                      reflog_msg.buf);
 
-                       strbuf_addf(&key, "branch.%s.remote", head);
-                       git_config_set(key.buf, option_origin);
-                       strbuf_reset(&key);
-                       strbuf_addf(&key, "branch.%s.merge", head);
-                       git_config_set(key.buf, head_points_at->name);
+                       install_branch_config(head, option_origin,
+                                             head_points_at->name);
                }
        } else if (remote_head) {
                /* Source had detached HEAD pointing somewhere. */
index d6a3a6203aee399218c89d31ff5cb28f16dc0cc6..46e649cd7ca0a2ede8f010a6d3bf294f81b85d55 100644 (file)
@@ -561,7 +561,6 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                commitable = run_status(fp, index_file, prefix, 1);
                wt_status_use_color = saved_color_setting;
        } else {
-               struct rev_info rev;
                unsigned char sha1[20];
                const char *parent = "HEAD";
 
@@ -573,16 +572,8 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
 
                if (get_sha1(parent, sha1))
                        commitable = !!active_nr;
-               else {
-                       init_revisions(&rev, "");
-                       rev.abbrev = 0;
-                       setup_revisions(0, NULL, &rev, parent);
-                       DIFF_OPT_SET(&rev.diffopt, QUIET);
-                       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-                       run_diff_index(&rev, 1 /* cached */);
-
-                       commitable = !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
-               }
+               else
+                       commitable = index_differs_from(parent, 0);
        }
 
        fclose(fp);
index e9ee2c79ac0395fde30de329d181733d7be88d40..fdf4ae9ebdba7832a0ac736d56a7b564bba41baa 100644 (file)
@@ -514,6 +514,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 
        get_tags_and_duplicates(&revs.pending, &extra_refs);
 
+       revs.topo_order = 1;
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        revs.diffopt.format_callback = show_filemodify;
index a2014388dad65fab8c7d32baf275295a0e36b210..8d990ed4935804591a17c864e280c0c9b4b7597f 100644 (file)
@@ -161,7 +161,8 @@ static int need_to_gc(void)
         */
        if (too_many_packs())
                append_option(argv_repack,
-                             !strcmp(prune_expire, "now") ? "-a" : "-A",
+                             prune_expire && !strcmp(prune_expire, "now") ?
+                             "-a" : "-A",
                              MAX_ADD);
        else if (!too_many_loose_objects())
                return 0;
@@ -173,14 +174,15 @@ static int need_to_gc(void)
 
 int cmd_gc(int argc, const char **argv, const char *prefix)
 {
-       int prune = 0;
        int aggressive = 0;
        int auto_gc = 0;
        int quiet = 0;
        char buf[80];
 
        struct option builtin_gc_options[] = {
-               OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
+               { OPTION_STRING, 0, "prune", &prune_expire, "date",
+                       "prune unreferenced objects",
+                       PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
                OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
                OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
                OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
@@ -218,7 +220,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        "\"git help gc\" for more information.\n");
        } else
                append_option(argv_repack,
-                             !strcmp(prune_expire, "now") ? "-a" : "-A",
+                             prune_expire && !strcmp(prune_expire, "now")
+                             ? "-a" : "-A",
                              MAX_ADD);
 
        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
@@ -230,9 +233,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (run_command_v_opt(argv_repack, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_repack[0]);
 
-       argv_prune[2] = prune_expire;
-       if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
-               return error(FAILED_RUN, argv_prune[0]);
+       if (prune_expire) {
+               argv_prune[2] = prune_expire;
+               if (run_command_v_opt(argv_prune, RUN_GIT_CMD))
+                       return error(FAILED_RUN, argv_prune[0]);
+       }
 
        if (run_command_v_opt(argv_rerere, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_rerere[0]);
index 34340312953867fdcb4ae62d530bdae3162744a5..9dec282fba6ba22e37d13bdc5e35fa4a031433b5 100644 (file)
@@ -262,6 +262,21 @@ static const char *verify_pathspec(const char *prefix)
        return max ? xmemdupz(prev, max) : NULL;
 }
 
+static void strip_trailing_slash_from_submodules(void)
+{
+       const char **p;
+
+       for (p = pathspec; *p != NULL; p++) {
+               int len = strlen(*p), pos;
+
+               if (len < 1 || (*p)[len - 1] != '/')
+                       continue;
+               pos = cache_name_pos(*p, len - 1);
+               if (pos >= 0 && S_ISGITLINK(active_cache[pos]->ce_mode))
+                       *p = xstrndup(*p, len - 1);
+       }
+}
+
 /*
  * Read the tree specified with --with-tree option
  * (typically, HEAD) into stage #1 and then
@@ -510,6 +525,11 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
 
        pathspec = get_pathspec(prefix, argv + i);
 
+       /* be nice with submodule patsh ending in a slash */
+       read_cache();
+       if (pathspec)
+               strip_trailing_slash_from_submodules();
+
        /* Verify that the pathspec matches the prefix */
        if (pathspec)
                prefix = verify_pathspec(prefix);
@@ -533,7 +553,6 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
              show_killed | show_modified))
                show_cached = 1;
 
-       read_cache();
        if (prefix)
                prune_cache(prefix);
        if (with_tree) {
index 5b63e6eada5cd6de764acef694da624a70ce6dab..fca46312f681caf077565fb4bd5b59a70008eb59 100644 (file)
@@ -68,13 +68,8 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,
                 *
                 * Something similar to this incomplete example:
                 *
-               if (show_subprojects(base, baselen, pathname)) {
-                       struct child_process ls_tree;
-
-                       ls_tree.dir = base;
-                       ls_tree.argv = ls-tree;
-                       start_command(&ls_tree);
-               }
+               if (show_subprojects(base, baselen, pathname))
+                       retval = READ_TREE_RECURSIVE;
                 *
                 */
                type = commit_type;
index 885fad9bba1310e6ed8424f9d4b481e066760a9b..6d2160d0a3db1705d1372029dfa24752d453c8ec 100644 (file)
@@ -356,9 +356,14 @@ static void merge_name(const char *remote, struct strbuf *msg)
        struct object *remote_head;
        unsigned char branch_head[20], buf_sha[20];
        struct strbuf buf = STRBUF_INIT;
+       struct strbuf bname = STRBUF_INIT;
        const char *ptr;
        int len, early;
 
+       len = strlen(remote);
+       if (interpret_nth_last_branch(remote, &bname) == len)
+               remote = bname.buf;
+
        memset(branch_head, 0, sizeof(branch_head));
        remote_head = peel_to_type(remote, 0, NULL, OBJ_COMMIT);
        if (!remote_head)
@@ -371,7 +376,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        if (!hashcmp(remote_head->sha1, branch_head)) {
                strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
                        sha1_to_hex(branch_head), remote);
-               return;
+               goto cleanup;
        }
 
        /* See if remote matches <name>^^^.. or <name>~<number> */
@@ -411,7 +416,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
                                    sha1_to_hex(remote_head->sha1),
                                    truname.buf + 11,
                                    (early ? " (early part)" : ""));
-                       return;
+                       strbuf_release(&truname);
+                       goto cleanup;
                }
        }
 
@@ -432,10 +438,13 @@ static void merge_name(const char *remote, struct strbuf *msg)
                        strbuf_remove(&line, ptr-line.buf+1, 13);
                strbuf_addbuf(msg, &line);
                strbuf_release(&line);
-               return;
+               goto cleanup;
        }
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
                sha1_to_hex(remote_head->sha1), remote);
+cleanup:
+       strbuf_release(&buf);
+       strbuf_release(&bname);
 }
 
 static int git_merge_config(const char *k, const char *v, void *cb)
index 6de186c397fcd5eaab2331db56546acab5f545f4..f7e04c45f997d433a5af4352f3dd0dee11dd43fa 100644 (file)
@@ -18,14 +18,16 @@ enum deny_action {
        DENY_REFUSE,
 };
 
-static int deny_deletes = 0;
-static int deny_non_fast_forwards = 0;
+static int deny_deletes;
+static int deny_non_fast_forwards;
 static enum deny_action deny_current_branch = DENY_UNCONFIGURED;
+static enum deny_action deny_delete_current = DENY_UNCONFIGURED;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
 static int unpack_limit = 100;
 static int report_status;
+static const char *head_name;
 
 static char capabilities[] = " report-status delete-refs ";
 static int capabilities_sent;
@@ -77,6 +79,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "receive.denydeletecurrent") == 0) {
+               deny_delete_current = parse_deny_action(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -203,16 +210,12 @@ static int run_update_hook(struct command *cmd)
 
 static int is_ref_checked_out(const char *ref)
 {
-       unsigned char sha1[20];
-       const char *head;
-
        if (is_bare_repository())
                return 0;
 
-       head = resolve_ref("HEAD", sha1, 0, NULL);
-       if (!head)
+       if (!head_name)
                return 0;
-       return !strcmp(head, ref);
+       return !strcmp(head_name, ref);
 }
 
 static char *warn_unconfigured_deny_msg[] = {
@@ -244,6 +247,32 @@ static void warn_unconfigured_deny(void)
                warning(warn_unconfigured_deny_msg[i]);
 }
 
+static char *warn_unconfigured_deny_delete_current_msg[] = {
+       "Deleting the current branch can cause confusion by making the next",
+       "'git clone' not check out any file.",
+       "",
+       "You can set 'receive.denyDeleteCurrent' configuration variable to",
+       "'refuse' in the remote repository to disallow deleting the current",
+       "branch.",
+       "",
+       "You can set it to 'ignore' to allow such a delete without a warning.",
+       "",
+       "To make this warning message less loud, you can set it to 'warn'.",
+       "",
+       "Note that the default will change in a future version of git",
+       "to refuse deleting the current branch unless you have the",
+       "configuration variable set to either 'ignore' or 'warn'."
+};
+
+static void warn_unconfigured_deny_delete_current(void)
+{
+       int i;
+       for (i = 0;
+            i < ARRAY_SIZE(warn_unconfigured_deny_delete_current_msg);
+            i++)
+               warning(warn_unconfigured_deny_delete_current_msg[i]);
+}
+
 static const char *update(struct command *cmd)
 {
        const char *name = cmd->ref_name;
@@ -278,12 +307,30 @@ static const char *update(struct command *cmd)
                      "but I can't find it!", sha1_to_hex(new_sha1));
                return "bad pack";
        }
-       if (deny_deletes && is_null_sha1(new_sha1) &&
-           !is_null_sha1(old_sha1) &&
-           !prefixcmp(name, "refs/heads/")) {
-               error("denying ref deletion for %s", name);
-               return "deletion prohibited";
+
+       if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
+               if (deny_deletes && !prefixcmp(name, "refs/heads/")) {
+                       error("denying ref deletion for %s", name);
+                       return "deletion prohibited";
+               }
+
+               if (!strcmp(name, head_name)) {
+                       switch (deny_delete_current) {
+                       case DENY_IGNORE:
+                               break;
+                       case DENY_WARN:
+                       case DENY_UNCONFIGURED:
+                               if (deny_delete_current == DENY_UNCONFIGURED)
+                                       warn_unconfigured_deny_delete_current();
+                               warning("deleting the current branch");
+                               break;
+                       case DENY_REFUSE:
+                               error("refusing to delete the current branch: %s", name);
+                               return "deletion of the current branch prohibited";
+                       }
+               }
        }
+
        if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
            !is_null_sha1(old_sha1) &&
            !prefixcmp(name, "refs/heads/")) {
@@ -377,6 +424,7 @@ static void run_update_post_hook(struct command *cmd)
 static void execute_commands(const char *unpacker_error)
 {
        struct command *cmd = commands;
+       unsigned char sha1[20];
 
        if (unpacker_error) {
                while (cmd) {
@@ -394,6 +442,8 @@ static void execute_commands(const char *unpacker_error)
                return;
        }
 
+       head_name = resolve_ref("HEAD", sha1, 0, NULL);
+
        while (cmd) {
                cmd->error_string = update(cmd);
                cmd = cmd->next;
index db18bcfc97f0739c2e77164dc0222148a44c67a6..ac69d37c8af415a64cba88a888e9b80db95ef97a 100644 (file)
@@ -756,12 +756,17 @@ static int prune(int argc, const char **argv)
                OPT_END()
        };
        struct ref_states states;
+       const char *dangling_msg;
 
        argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
 
        if (argc < 1)
                usage_with_options(builtin_remote_usage, options);
 
+       dangling_msg = (dry_run
+                       ? " %s will become dangling!\n"
+                       : " %s has become dangling!\n");
+
        memset(&states, 0, sizeof(states));
        for (; argc; argc--, argv++) {
                int i;
@@ -784,6 +789,7 @@ static int prune(int argc, const char **argv)
 
                        printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
                               abbrev_ref(refname, "refs/remotes/"));
+                       warn_dangling_symref(dangling_msg, refname);
                }
 
                /* NEEDSWORK: free remote */
index 857742a14f82e049c5b9e8b234dae9e9e1a7dc30..436afa45f5b7569551aa8301aee8a0752009a900 100644 (file)
@@ -608,6 +608,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                if (!strcmp(arg, "--bisect-all")) {
                        bisect_list = 1;
                        bisect_find_all = 1;
+                       revs.show_decorations = 1;
                        continue;
                }
                if (!strcmp(arg, "--bisect-vars")) {
index d48313c7453c24e7ab4cbb3e024d05ec311edf7b..d210150671830c10f098463175e5a6e8d304c1a4 100644 (file)
@@ -223,17 +223,6 @@ static char *help_msg(const unsigned char *sha1)
        return helpbuf;
 }
 
-static int index_is_dirty(void)
-{
-       struct rev_info rev;
-       init_revisions(&rev, NULL);
-       setup_revisions(0, NULL, &rev, "HEAD");
-       DIFF_OPT_SET(&rev.diffopt, QUIET);
-       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
-       run_diff_index(&rev, 1);
-       return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
-}
-
 static struct tree *empty_tree(void)
 {
        struct tree *tree = xcalloc(1, sizeof(struct tree));
@@ -279,7 +268,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        } else {
                if (get_sha1("HEAD", head))
                        die ("You do not have a valid HEAD");
-               if (index_is_dirty())
+               if (index_differs_from("HEAD", 0))
                        die ("Dirty index: cannot %s", me);
        }
        discard_cache();
index 5f9f3f09b11b2e7f54a26bdbce7cb0e6974740d5..badd9120388569c79b137f679505b495cc1dbbe9 100644 (file)
@@ -40,6 +40,7 @@ static void insert_one_record(struct shortlog *log,
        char *buffer, *p;
        struct string_list_item *item;
        char namebuf[1024];
+       char emailbuf[1024];
        size_t len;
        const char *eol;
        const char *boemail, *eoemail;
@@ -51,7 +52,19 @@ static void insert_one_record(struct shortlog *log,
        eoemail = strchr(boemail, '>');
        if (!eoemail)
                return;
-       if (!map_email(&log->mailmap, boemail+1, namebuf, sizeof(namebuf))) {
+
+       /* copy author name to namebuf, to support matching on both name and email */
+       memcpy(namebuf, author, boemail - author);
+       len = boemail - author;
+       while(len > 0 && isspace(namebuf[len-1]))
+               len--;
+       namebuf[len] = 0;
+
+       /* copy email name to emailbuf, to allow email replacement as well */
+       memcpy(emailbuf, boemail+1, eoemail - boemail);
+       emailbuf[eoemail - boemail - 1] = 0;
+
+       if (!map_user(&log->mailmap, emailbuf, sizeof(emailbuf), namebuf, sizeof(namebuf))) {
                while (author < boemail && isspace(*author))
                        author++;
                for (len = 0;
@@ -67,8 +80,8 @@ static void insert_one_record(struct shortlog *log,
 
        if (log->email) {
                size_t room = sizeof(namebuf) - len - 1;
-               int maillen = eoemail - boemail + 1;
-               snprintf(namebuf + len, room, " %.*s", maillen, boemail);
+               int maillen = strlen(emailbuf);
+               snprintf(namebuf + len, room, " <%.*s>", maillen, emailbuf);
        }
 
        item = string_list_insert(namebuf, &log->list);
@@ -219,7 +232,7 @@ void shortlog_init(struct shortlog *log)
 {
        memset(log, 0, sizeof(*log));
 
-       read_mailmap(&log->mailmap, ".mailmap", &log->common_repo_prefix);
+       read_mailmap(&log->mailmap, &log->common_repo_prefix);
 
        log->list.strdup_strings = 1;
        log->wrap = DEFAULT_WRAPLEN;
@@ -248,6 +261,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
        struct parse_opt_ctx_t ctx;
 
        prefix = setup_git_directory_gently(&nongit);
+       git_config(git_default_config, NULL);
        shortlog_init(&log);
        init_revisions(&rev, prefix);
        parse_options_start(&ctx, argc, argv, PARSE_OPT_KEEP_DASHDASH |
@@ -320,6 +334,5 @@ void shortlog_output(struct shortlog *log)
 
        log->list.strdup_strings = 1;
        string_list_clear(&log->list, 1);
-       log->mailmap.strdup_strings = 1;
-       string_list_clear(&log->mailmap, 1);
+       clear_mailmap(&log->mailmap);
 }
index cafc4eba7cc0f8e2c89d49403b56bb746fe14545..6ae6bcc0e8d02d9af8a81a7d694c0bfd2c6c0514 100644 (file)
@@ -45,8 +45,8 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
                break;
        case 2:
                if (!strcmp(argv[0], "HEAD") &&
-                   prefixcmp(argv[1], "refs/heads/"))
-                       die("Refusing to point HEAD outside of refs/heads/");
+                   prefixcmp(argv[1], "refs/"))
+                       die("Refusing to point HEAD outside of refs/");
                create_symref(argv[0], argv[1], msg);
                break;
        default:
diff --git a/cache.h b/cache.h
index 2d889deb264f189e16c2e9d2fc9d6fb795180773..37dfb1c18f5b19082ae82349cf4e89dd110dfdc9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -371,8 +371,6 @@ static inline enum object_type object_type(unsigned int mode)
 #define GITATTRIBUTES_FILE ".gitattributes"
 #define INFOATTRIBUTES_FILE "info/attributes"
 #define ATTRIBUTE_MACRO_PREFIX "[attr]"
-#define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
-#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
 
 extern int is_bare_repository_cfg;
 extern int is_bare_repository(void);
@@ -544,7 +542,6 @@ enum rebase_setup_type {
 
 extern enum branch_track git_branch_track;
 extern enum rebase_setup_type autorebase;
-extern char *notes_ref_name;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -627,7 +624,7 @@ int is_directory(const char *);
 const char *make_absolute_path(const char *path);
 const char *make_nonrelative_path(const char *path);
 const char *make_relative_path(const char *abs, const char *base);
-int normalize_absolute_path(char *buf, const char *path);
+int normalize_path_copy(char *dst, const char *src);
 int longest_ancestor_length(const char *path, const char *prefix_list);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
@@ -830,6 +827,7 @@ extern unsigned char* use_pack(struct packed_git *, struct pack_window **, off_t
 extern void close_pack_windows(struct packed_git *);
 extern void unuse_pack(struct pack_window **);
 extern void free_pack_by_name(const char *);
+extern void clear_delta_base_cache(void);
 extern struct packed_git *add_packed_git(const char *, int, int);
 extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t);
 extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
@@ -870,6 +868,7 @@ extern int user_ident_explicitly_given;
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
+extern const char *git_mailmap_file;
 
 /* IO helper functions */
 extern void maybe_flush_or_die(FILE *, const char *);
index 2dc2c3320cef1d2b8218b6e75e27e6399294d6cf..3583a33ee90647d8e6ded02643eb75753760d94f 100644 (file)
@@ -73,7 +73,6 @@ git-mktag                               plumbingmanipulators
 git-mktree                              plumbingmanipulators
 git-mv                                  mainporcelain common
 git-name-rev                            plumbinginterrogators
-git-notes                               mainporcelain
 git-pack-objects                        plumbingmanipulators
 git-pack-redundant                      plumbinginterrogators
 git-pack-refs                           ancillarymanipulators
index cf72143f58346c93879a98709d5d6fa8a0981b40..aa3b35b6a86891ac9d0628e20a6a46d506bf7700 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -5,7 +5,6 @@
 #include "utf8.h"
 #include "diff.h"
 #include "revision.h"
-#include "notes.h"
 
 int save_commit_buffer = 1;
 
index e5d5b4bd0689d8c2932d501779356fc33a0d0379..0c8c76f13b03028ad400b1c5b72b3cf0a7ec0940 100644 (file)
--- a/config.c
+++ b/config.c
@@ -469,11 +469,6 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
-       if (!strcmp(var, "core.notesref")) {
-               notes_ref_name = xstrdup(value);
-               return 0;
-       }
-
        if (!strcmp(var, "core.pager"))
                return git_config_string(&pager_program, var, value);
 
@@ -570,6 +565,15 @@ static int git_default_branch_config(const char *var, const char *value)
        return 0;
 }
 
+static int git_default_mailmap_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "mailmap.file"))
+               return git_config_string(&git_mailmap_file, var, value);
+
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
 int git_default_config(const char *var, const char *value, void *dummy)
 {
        if (!prefixcmp(var, "core."))
@@ -584,6 +588,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (!prefixcmp(var, "branch."))
                return git_default_branch_config(var, value);
 
+       if (!prefixcmp(var, "mailmap."))
+               return git_default_mailmap_config(var, value);
+
        if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
                pager_use_color = git_config_bool(var,value);
                return 0;
index 307bf5d4f98d2d4c26b130fe69807223b64f5bb4..412d2c0dab8118689c37b8ecbe29c7c0b2a2575a 100755 (executable)
 #       are currently in a git repository.  The %s token will be
 #       the name of the current branch.
 #
-#      In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
-#      value, unstaged (*) and staged (+) changes will be shown next
-#      to the branch name.  You can configure this per-repository
-#      with the bash.showDirtyState variable, which defaults to true
-#      once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#       In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
+#       value, unstaged (*) and staged (+) changes will be shown next
+#       to the branch name.  You can configure this per-repository
+#       with the bash.showDirtyState variable, which defaults to true
+#       once GIT_PS1_SHOWDIRTYSTATE is enabled.
 #
 # To submit patches:
 #
@@ -125,7 +125,7 @@ __git_ps1 ()
                local w
                local i
 
-               if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then
+               if test -n "${GIT_PS1_SHOWDIRTYSTATE-}"; then
                        if test "$(git config --bool bash.showDirtyState)" != "false"; then
                                git diff --no-ext-diff --ignore-submodules \
                                        --quiet --exit-code || w="*"
@@ -1196,10 +1196,14 @@ _git_config ()
                __gitcomp "$(__git_merge_strategies)"
                return
                ;;
-       color.branch|color.diff|color.status)
+       color.branch|color.diff|color.interactive|color.status|color.ui)
                __gitcomp "always never auto"
                return
                ;;
+       color.pager)
+               __gitcomp "false true"
+               return
+               ;;
        color.*.*)
                __gitcomp "
                        normal black red green yellow blue magenta cyan white
@@ -1606,7 +1610,7 @@ _git_svn ()
                        --follow-parent --authors-file= --repack=
                        --no-metadata --use-svm-props --use-svnsync-props
                        --log-window-size= --no-checkout --quiet
-                       --repack-flags --user-log-author --localtime $remote_opts
+                       --repack-flags --use-log-author --localtime $remote_opts
                        "
                local init_opts="
                        --template= --shared= --trunk= --tags=
index a48540a92b4aa5140a87469b36c1f9b3d8e46e7f..24d931294180c10646249bbbe31b5215f9996a66 100644 (file)
@@ -2,7 +2,7 @@
 
 EMACS = emacs
 
-ELC = git.elc vc-git.elc git-blame.elc
+ELC = git.elc git-blame.elc
 INSTALL ?= install
 INSTALL_ELC = $(INSTALL) -m 644
 prefix ?= $(HOME)
index 09e8bae3a41827a20f6f0693c28f91a98adcb8a4..fcbe2d9cf549c0479c634e83f3f670bc82ab862d 100644 (file)
@@ -1,6 +1,6 @@
 ;;; git.el --- A user interface for git
 
-;; Copyright (C) 2005, 2006, 2007 Alexandre Julliard <julliard@winehq.org>
+;; Copyright (C) 2005, 2006, 2007, 2008, 2009 Alexandre Julliard <julliard@winehq.org>
 
 ;; Version: 1.0
 
 ;; To start: `M-x git-status'
 ;;
 ;; TODO
-;;  - portability to XEmacs
 ;;  - diff against other branch
 ;;  - renaming files from the status buffer
 ;;  - creating tags
 ;;  - fetch/pull
-;;  - switching branches
 ;;  - revlist browser
 ;;  - git-show-branch browser
-;;  - menus
+;;
+
+;;; Compatibility:
+;;
+;; This file works on GNU Emacs 21 or later. It may work on older
+;; versions but this is not guaranteed.
+;;
+;; It may work on XEmacs 21, provided that you first install the ewoc
+;; and log-edit packages.
 ;;
 
 (eval-when-compile (require 'cl))
@@ -222,7 +228,7 @@ the process output as a string, or nil if the git command failed."
     (with-current-buffer buffer
       (cd dir)
       (apply #'call-process-region start end program
-             nil (list output-buffer nil) nil args))))
+             nil (list output-buffer t) nil args))))
 
 (defun git-run-command-buffer (buffer-name &rest args)
   "Run a git command, sending the output to a buffer named BUFFER-NAME."
@@ -239,13 +245,15 @@ the process output as a string, or nil if the git command failed."
 
 (defun git-run-command-region (buffer start end env &rest args)
   "Run a git command with specified buffer region as input."
-  (unless (eq 0 (if env
-                    (git-run-process-region
-                     buffer start end "env"
-                     (append (git-get-env-strings env) (list "git") args))
+  (with-temp-buffer
+    (if (eq 0 (if env
                   (git-run-process-region
-                   buffer start end "git" args)))
-    (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
+                   buffer start end "env"
+                   (append (git-get-env-strings env) (list "git") args))
+                (git-run-process-region buffer start end "git" args)))
+        (buffer-string)
+      (display-message-or-buffer (current-buffer))
+      nil)))
 
 (defun git-run-hook (hook env &rest args)
   "Run a git hook and display its output if any."
@@ -397,6 +405,17 @@ the process output as a string, or nil if the git command failed."
     (unless newval (push "-d" args))
     (apply 'git-call-process-display-error "update-ref" args)))
 
+(defun git-for-each-ref (&rest specs)
+  "Return a list of refs using git-for-each-ref.
+Each entry is a cons of (SHORT-NAME . FULL-NAME)."
+  (let (refs)
+    (with-temp-buffer
+      (apply #'git-call-process t "for-each-ref" "--format=%(refname)" specs)
+      (goto-char (point-min))
+      (while (re-search-forward "^[^/\n]+/[^/\n]+/\\(.+\\)$" nil t)
+       (push (cons (match-string 1) (match-string 0)) refs)))
+    (nreverse refs)))
+
 (defun git-read-tree (tree &optional index-file)
   "Read a tree into the index file."
   (let ((process-environment
@@ -447,18 +466,16 @@ the process output as a string, or nil if the git command failed."
       (setq coding-system-for-write buffer-file-coding-system))
     (let ((commit
            (git-get-string-sha1
-            (with-output-to-string
-              (with-current-buffer standard-output
-                (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
-                             ("GIT_AUTHOR_EMAIL" . ,author-email)
-                             ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
-                             ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
-                  (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
-                  (apply #'git-run-command-region
-                         buffer log-start log-end env
-                         "commit-tree" tree (nreverse args))))))))
-      (and (git-update-ref "HEAD" commit head subject)
-           commit))))
+            (let ((env `(("GIT_AUTHOR_NAME" . ,author-name)
+                         ("GIT_AUTHOR_EMAIL" . ,author-email)
+                         ("GIT_COMMITTER_NAME" . ,(git-get-committer-name))
+                         ("GIT_COMMITTER_EMAIL" . ,(git-get-committer-email)))))
+              (when author-date (push `("GIT_AUTHOR_DATE" . ,author-date) env))
+              (apply #'git-run-command-region
+                     buffer log-start log-end env
+                     "commit-tree" tree (nreverse args))))))
+      (when commit (git-update-ref "HEAD" commit head subject))
+      commit)))
 
 (defun git-empty-db-p ()
   "Check if the git db is empty (no commit done yet)."
@@ -562,29 +579,29 @@ the process output as a string, or nil if the git command failed."
   (let* ((old-type (lsh (or old-perm 0) -9))
         (new-type (lsh (or new-perm 0) -9))
         (str (case new-type
-               (?\100  ;; file
+               (64  ;; file
                 (case old-type
-                  (?\100 nil)
-                  (?\120 "   (type change symlink -> file)")
-                  (?\160 "   (type change subproject -> file)")))
-                (?\120  ;; symlink
+                  (64 nil)
+                  (80 "   (type change symlink -> file)")
+                  (112 "   (type change subproject -> file)")))
+                (80  ;; symlink
                  (case old-type
-                   (?\100 "   (type change file -> symlink)")
-                   (?\160 "   (type change subproject -> symlink)")
+                   (64 "   (type change file -> symlink)")
+                   (112 "   (type change subproject -> symlink)")
                    (t "   (symlink)")))
-                 (?\160  ;; subproject
+                 (112  ;; subproject
                   (case old-type
-                    (?\100 "   (type change file -> subproject)")
-                    (?\120 "   (type change symlink -> subproject)")
+                    (64 "   (type change file -> subproject)")
+                    (80 "   (type change symlink -> subproject)")
                     (t "   (subproject)")))
-                  (?\110 nil)  ;; directory (internal, not a real git state)
-                 (?\000  ;; deleted or unknown
+                  (72 nil)  ;; directory (internal, not a real git state)
+                 (0  ;; deleted or unknown
                   (case old-type
-                    (?\120 "   (symlink)")
-                    (?\160 "   (subproject)")))
+                    (80 "   (symlink)")
+                    (112 "   (subproject)")))
                  (t (format "   (unknown type %o)" new-type)))))
     (cond (str (propertize str 'face 'git-status-face))
-          ((eq new-type ?\110) "/")
+          ((eq new-type 72) "/")
           (t ""))))
 
 (defun git-rename-as-string (info)
@@ -1320,6 +1337,7 @@ Return the list of files that haven't been handled."
                                         (log-edit-diff-function . git-log-edit-diff)) buffer)
        (log-edit 'git-do-commit nil 'git-log-edit-files buffer))
       (setq font-lock-keywords (font-lock-compile-keywords git-log-edit-font-lock-keywords))
+      (setq paragraph-separate (concat (regexp-quote git-log-msg-separator) "$\\|Author: \\|Date: \\|Merge: \\|Signed-off-by: \\|\f\\|[        ]*$"))
       (setq buffer-file-coding-system coding-system)
       (re-search-forward (regexp-quote (concat git-log-msg-separator "\n")) nil t))))
 
@@ -1356,6 +1374,36 @@ Return the list of files that haven't been handled."
         (push (match-string 1) files)))
     files))
 
+(defun git-read-commit-name (prompt &optional default)
+  "Ask for a commit name, with completion for local branch, remote branch and tag."
+  (completing-read prompt
+                   (list* "HEAD" "ORIG_HEAD" "FETCH_HEAD" (mapcar #'car (git-for-each-ref)))
+                  nil nil nil nil default))
+
+(defun git-checkout (branch &optional merge)
+  "Checkout a branch, tag, or any commit.
+Use a prefix arg if git should merge while checking out."
+  (interactive
+   (list (git-read-commit-name "Checkout: ")
+         current-prefix-arg))
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((args (list branch "--")))
+    (when merge (push "-m" args))
+    (when (apply #'git-call-process-display-error "checkout" args)
+      (git-update-status-files))))
+
+(defun git-branch (branch)
+  "Create a branch from the current HEAD and switch to it."
+  (interactive (list (git-read-commit-name "Branch: ")))
+  (unless git-status (error "Not in git-status buffer."))
+  (if (git-rev-parse (concat "refs/heads/" branch))
+      (if (yes-or-no-p (format "Branch %s already exists, replace it? " branch))
+          (and (git-call-process-display-error "branch" "-f" branch)
+               (git-call-process-display-error "checkout" branch))
+        (message "Canceled."))
+    (git-call-process-display-error "checkout" "-b" branch))
+    (git-refresh-ewoc-hf git-status))
+
 (defun git-amend-commit ()
   "Undo the last commit on HEAD, and set things up to commit an
 amended version of it."
@@ -1372,6 +1420,44 @@ amended version of it."
       (git-setup-commit-buffer commit)
       (git-commit-file))))
 
+(defun git-cherry-pick-commit (arg)
+  "Cherry-pick a commit."
+  (interactive (list (git-read-commit-name "Cherry-pick commit: ")))
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((commit (git-rev-parse (concat arg "^0"))))
+    (unless commit (error "Not a valid commit '%s'." arg))
+    (when (git-rev-parse (concat commit "^2"))
+      (error "Cannot cherry-pick a merge commit."))
+    (let ((files (git-get-commit-files commit))
+          (ok (git-call-process-display-error "cherry-pick" "-n" commit)))
+      (git-update-status-files files ok)
+      (with-current-buffer (git-setup-commit-buffer commit)
+        (goto-char (point-min))
+        (if (re-search-forward "^\n*Signed-off-by:" nil t 1)
+            (goto-char (match-beginning 0))
+          (goto-char (point-max)))
+        (insert "(cherry picked from commit " commit ")\n"))
+      (when ok (git-commit-file)))))
+
+(defun git-revert-commit (arg)
+  "Revert a commit."
+  (interactive (list (git-read-commit-name "Revert commit: ")))
+  (unless git-status (error "Not in git-status buffer."))
+  (let ((commit (git-rev-parse (concat arg "^0"))))
+    (unless commit (error "Not a valid commit '%s'." arg))
+    (when (git-rev-parse (concat commit "^2"))
+      (error "Cannot revert a merge commit."))
+    (let ((files (git-get-commit-files commit))
+          (subject (git-get-commit-description commit))
+          (ok (git-call-process-display-error "revert" "-n" commit)))
+      (git-update-status-files files ok)
+      (when (string-match "^[0-9a-f]+ - \\(.*\\)$" subject)
+        (setq subject (match-string 1 subject)))
+      (git-setup-log-buffer (get-buffer-create "*git-commit*")
+                            (git-get-merge-heads) nil nil (format "Revert \"%s\"" subject) nil
+                            (format "This reverts commit %s.\n" commit))
+      (when ok (git-commit-file)))))
+
 (defun git-find-file ()
   "Visit the current file in its own buffer."
   (interactive)
@@ -1471,6 +1557,10 @@ amended version of it."
     (define-key map "\M-\C-?" 'git-unmark-all)
     ; the commit submap
     (define-key commit-map "\C-a" 'git-amend-commit)
+    (define-key commit-map "\C-b" 'git-branch)
+    (define-key commit-map "\C-o" 'git-checkout)
+    (define-key commit-map "\C-p" 'git-cherry-pick-commit)
+    (define-key commit-map "\C-v" 'git-revert-commit)
     ; the diff submap
     (define-key diff-map "b" 'git-diff-file-base)
     (define-key diff-map "c" 'git-diff-file-combined)
@@ -1491,6 +1581,10 @@ amended version of it."
     `("Git"
       ["Refresh" git-refresh-status t]
       ["Commit" git-commit-file t]
+      ["Checkout..." git-checkout t]
+      ["New Branch..." git-branch t]
+      ["Cherry-pick Commit..." git-cherry-pick-commit t]
+      ["Revert Commit..." git-revert-commit t]
       ("Merge"
        ["Next Unmerged File" git-next-unmerged-file t]
        ["Prev Unmerged File" git-prev-unmerged-file t]
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
deleted file mode 100644 (file)
index b8f6be5..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-;;; vc-git.el --- VC backend for the git version control system
-
-;; Copyright (C) 2006 Alexandre Julliard
-
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2 of
-;; the License, or (at your option) any later version.
-;;
-;; This program is distributed in the hope that it will be
-;; useful, but WITHOUT ANY WARRANTY; without even the implied
-;; warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-;; PURPOSE.  See the GNU General Public License for more details.
-;;
-;; You should have received a copy of the GNU General Public
-;; License along with this program; if not, write to the Free
-;; Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
-;; MA 02111-1307 USA
-
-;;; Commentary:
-
-;; This file contains a VC backend for the git version control
-;; system.
-;;
-;; To install: put this file on the load-path and add GIT to the list
-;; of supported backends in `vc-handled-backends'; the following line,
-;; placed in your ~/.emacs, will accomplish this:
-;;
-;;     (add-to-list 'vc-handled-backends 'GIT)
-;;
-;; TODO
-;;  - changelog generation
-;;  - working with revisions other than HEAD
-;;
-
-(eval-when-compile (require 'cl))
-
-(defvar git-commits-coding-system 'utf-8
-  "Default coding system for git commits.")
-
-(defun vc-git--run-command-string (file &rest args)
-  "Run a git command on FILE and return its output as string."
-  (let* ((ok t)
-         (str (with-output-to-string
-                (with-current-buffer standard-output
-                  (unless (eq 0 (apply #'call-process "git" nil '(t nil) nil
-                                       (append args (list (file-relative-name file)))))
-                    (setq ok nil))))))
-    (and ok str)))
-
-(defun vc-git--run-command (file &rest args)
-  "Run a git command on FILE, discarding any output."
-  (let ((name (file-relative-name file)))
-    (eq 0 (apply #'call-process "git" nil (get-buffer "*Messages") nil (append args (list name))))))
-
-(defun vc-git-registered (file)
-  "Check whether FILE is registered with git."
-  (with-temp-buffer
-    (let* ((dir (file-name-directory file))
-           (name (file-relative-name file dir)))
-      (and (ignore-errors
-             (when dir (cd dir))
-             (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
-           (let ((str (buffer-string)))
-             (and (> (length str) (length name))
-                  (string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))
-
-(defun vc-git-state (file)
-  "git-specific version of `vc-state'."
-  (let ((diff (vc-git--run-command-string file "diff-index" "-z" "HEAD" "--")))
-    (if (and diff (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} [ADMU]\0[^\0]+\0" diff))
-        'edited
-      'up-to-date)))
-
-(defun vc-git-workfile-version (file)
-  "git-specific version of `vc-workfile-version'."
-  (let ((str (with-output-to-string
-               (with-current-buffer standard-output
-                 (call-process "git" nil '(t nil) nil "symbolic-ref" "HEAD")))))
-    (if (string-match "^\\(refs/heads/\\)?\\(.+\\)$" str)
-        (match-string 2 str)
-      str)))
-
-(defun vc-git-symbolic-commit (commit)
-  "Translate COMMIT string into symbolic form.
-Returns nil if not possible."
-  (and commit
-       (with-temp-buffer
-        (and
-         (zerop
-          (call-process "git" nil '(t nil) nil "name-rev"
-                        "--name-only" "--tags"
-                        commit))
-         (goto-char (point-min))
-         (= (forward-line 2) 1)
-         (bolp)
-         (buffer-substring-no-properties (point-min) (1- (point-max)))))))
-
-(defun vc-git-previous-version (file rev)
-  "git-specific version of `vc-previous-version'."
-  (let ((default-directory (file-name-directory (expand-file-name file)))
-       (file (file-name-nondirectory file)))
-    (vc-git-symbolic-commit
-     (with-temp-buffer
-       (and
-       (zerop
-        (call-process "git" nil '(t nil) nil "rev-list"
-                      "-2" rev "--" file))
-       (goto-char (point-max))
-       (bolp)
-       (zerop (forward-line -1))
-       (not (bobp))
-       (buffer-substring-no-properties
-          (point)
-          (1- (point-max))))))))
-
-(defun vc-git-next-version (file rev)
-  "git-specific version of `vc-next-version'."
-  (let* ((default-directory (file-name-directory
-                            (expand-file-name file)))
-       (file (file-name-nondirectory file))
-       (current-rev
-        (with-temp-buffer
-          (and
-           (zerop
-            (call-process "git" nil '(t nil) nil "rev-list"
-                          "-1" rev "--" file))
-           (goto-char (point-max))
-           (bolp)
-           (zerop (forward-line -1))
-           (bobp)
-           (buffer-substring-no-properties
-            (point)
-            (1- (point-max)))))))
-    (and current-rev
-        (vc-git-symbolic-commit
-         (with-temp-buffer
-           (and
-            (zerop
-             (call-process "git" nil '(t nil) nil "rev-list"
-                           "HEAD" "--" file))
-            (goto-char (point-min))
-            (search-forward current-rev nil t)
-            (zerop (forward-line -1))
-            (buffer-substring-no-properties
-             (point)
-             (progn (forward-line 1) (1- (point))))))))))
-
-(defun vc-git-revert (file &optional contents-done)
-  "Revert FILE to the version stored in the git repository."
-  (if contents-done
-      (vc-git--run-command file "update-index" "--")
-    (vc-git--run-command file "checkout" "HEAD")))
-
-(defun vc-git-checkout-model (file)
-  'implicit)
-
-(defun vc-git-workfile-unchanged-p (file)
-  (let ((sha1 (vc-git--run-command-string file "hash-object" "--"))
-        (head (vc-git--run-command-string file "ls-tree" "-z" "HEAD" "--")))
-    (and head
-         (string-match "[0-7]\\{6\\} blob \\([0-9a-f]\\{40\\}\\)\t[^\0]+\0" head)
-         (string= (car (split-string sha1 "\n")) (match-string 1 head)))))
-
-(defun vc-git-register (file &optional rev comment)
-  "Register FILE into the git version-control system."
-  (vc-git--run-command file "update-index" "--add" "--"))
-
-(defun vc-git-print-log (file &optional buffer)
-  (let ((name (file-relative-name file))
-        (coding-system-for-read git-commits-coding-system))
-    (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
-
-(defun vc-git-diff (file &optional rev1 rev2 buffer)
-  (let ((name (file-relative-name file))
-        (buf (or buffer "*vc-diff*")))
-    (if (and rev1 rev2)
-        (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
-      (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
-    ; git-diff-index doesn't set exit status like diff does
-    (if (vc-git-workfile-unchanged-p file) 0 1)))
-
-(defun vc-git-checkin (file rev comment)
-  (let ((coding-system-for-write git-commits-coding-system))
-    (vc-git--run-command file "commit" "-m" comment "--only" "--")))
-
-(defun vc-git-checkout (file &optional editable rev destfile)
-  (if destfile
-      (let ((fullname (substring
-                       (vc-git--run-command-string file "ls-files" "-z" "--full-name" "--")
-                       0 -1))
-            (coding-system-for-read 'no-conversion)
-            (coding-system-for-write 'no-conversion))
-        (with-temp-file destfile
-          (eq 0 (call-process "git" nil t nil "cat-file" "blob"
-                              (concat (or rev "HEAD") ":" fullname)))))
-    (vc-git--run-command file "checkout" (or rev "HEAD"))))
-
-(defun vc-git-annotate-command (file buf &optional rev)
-  ; FIXME: rev is ignored
-  (let ((name (file-relative-name file)))
-    (call-process "git" nil buf nil "blame" name)))
-
-(defun vc-git-annotate-time ()
-  (and (re-search-forward "[0-9a-f]+ (.* \\([0-9]+\\)-\\([0-9]+\\)-\\([0-9]+\\) \\([0-9]+\\):\\([0-9]+\\):\\([0-9]+\\) \\([-+0-9]+\\) +[0-9]+)" nil t)
-       (vc-annotate-convert-time
-        (apply #'encode-time (mapcar (lambda (match) (string-to-number (match-string match))) '(6 5 4 3 2 1 7))))))
-
-;; Not really useful since we can't do anything with the revision yet
-;;(defun vc-annotate-extract-revision-at-line ()
-;;  (save-excursion
-;;    (move-beginning-of-line 1)
-;;    (and (looking-at "[0-9a-f]+")
-;;         (buffer-substring (match-beginning 0) (match-end 0)))))
-
-(provide 'vc-git)
index 28a3c0e46ecf9951f3f42a025a288a65c70e0424..60cbab65d3f8230be3041a13fac2fd9f9b3018d5 100644 (file)
@@ -615,7 +615,9 @@ show_new_revisions()
                revspec=$oldrev..$newrev
        fi
 
-       git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
+       other_branches=$(git for-each-ref --format='%(refname)' refs/heads/ |
+           grep -F -v $refname)
+       git rev-parse --not $other_branches |
        if [ -z "$custom_showrev" ]
        then
                git rev-list --pretty --stdin $revspec
index a41e1ec07ccd707969aa51768dac3e2b6356ddc6..79d06068344f5a602f6c8799f6671ccbf98cf49c 100644 (file)
@@ -513,3 +513,18 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
                exit(128);
        return 0;
 }
+
+int index_differs_from(const char *def, int diff_flags)
+{
+       struct rev_info rev;
+
+       init_revisions(&rev, NULL);
+       setup_revisions(0, NULL, &rev, def);
+       DIFF_OPT_SET(&rev.diffopt, QUIET);
+       DIFF_OPT_SET(&rev.diffopt, EXIT_WITH_STATUS);
+       rev.diffopt.flags |= diff_flags;
+       run_diff_index(&rev, 1);
+       if (rev.pending.alloc)
+               free(rev.pending.objects);
+       return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
+}
diff --git a/diff.c b/diff.c
index a5a540fd381495d2a75527b0f4fbe23ad668708f..006aa017e28dd217d07bb2c48d932e026175f98d 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -184,11 +184,11 @@ static int remove_tempfile_installed;
 static void remove_tempfile(void)
 {
        int i;
-       for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
-               if (diff_temp[i].name == diff_temp[i].tmp_path) {
+       for (i = 0; i < ARRAY_SIZE(diff_temp); i++) {
+               if (diff_temp[i].name == diff_temp[i].tmp_path)
                        unlink(diff_temp[i].name);
-                       diff_temp[i].name = NULL;
-               }
+               diff_temp[i].name = NULL;
+       }
 }
 
 static void remove_tempfile_on_signal(int signo)
@@ -2326,15 +2326,12 @@ void diff_setup(struct diff_options *options)
        options->break_opt = -1;
        options->rename_limit = -1;
        options->dirstat_percent = 3;
-       DIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);
        options->context = 3;
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
        if (diff_use_color_default > 0)
                DIFF_OPT_SET(options, COLOR_DIFF);
-       else
-               DIFF_OPT_CLR(options, COLOR_DIFF);
        options->detect_rename = diff_detect_rename_default;
 
        if (!diff_mnemonic_prefix) {
diff --git a/diff.h b/diff.h
index 23cd90c2e64cf8be44999be812a0765cbe36c9f8..6703a4fb4f0302f4adf1065e91cd1bb27e5c973a 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -265,4 +265,6 @@ extern int diff_result_code(struct diff_options *, int);
 
 extern void diff_no_index(struct rev_info *, int, const char **, int, const char *);
 
+extern int index_differs_from(const char *def, int diff_flags);
+
 #endif /* DIFF_H */
index 0edae21e74d53b89b55cd987ed5a6f6ff053e8ee..e278bce0ea5f1ddda2dab9012663c6d1d4c6bd89 100644 (file)
@@ -45,7 +45,6 @@ enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
-char *notes_ref_name;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
index 1935206be04f3ecc52512958879734b0895d1953..3ef3413e69896d45012ea94b3678959d5d2cceb0 100644 (file)
@@ -869,7 +869,7 @@ static char *create_index(void)
        /* Generate the fan-out array. */
        c = idx;
        for (i = 0; i < 256; i++) {
-               struct object_entry **next = c;;
+               struct object_entry **next = c;
                while (next < last) {
                        if ((*next)->sha1[0] != i)
                                break;
@@ -945,6 +945,7 @@ static void end_packfile(void)
 {
        struct packed_git *old_p = pack_data, *new_p;
 
+       clear_delta_base_cache();
        if (object_count) {
                char *idx_name;
                int i;
index 3bf0cda4eef6714b09df0360d48c12796598071a..5f129a42030917bc5dcab67aeaf67a5f00c17677 100755 (executable)
                $repo->get_color('color.interactive.header', 'bold'),
                $repo->get_color('color.interactive.help', 'red bold'),
        ) : ();
+my $error_color = ();
+if ($menu_use_color) {
+       my $help_color_spec = ($repo->config('color.interactive.help') or
+                               'red bold');
+       $error_color = $repo->get_color('color.interactive.error',
+                                       $help_color_spec);
+}
 
 my $diff_use_color = $repo->get_colorbool('color.diff');
 my ($fraginfo_color) =
 
 my $normal_color = $repo->get_color("", "reset");
 
+my $use_readkey = 0;
+sub ReadMode;
+sub ReadKey;
+if ($repo->config_bool("interactive.singlekey")) {
+       eval {
+               require Term::ReadKey;
+               Term::ReadKey->import;
+               $use_readkey = 1;
+       };
+}
+
 sub colored {
        my $color = shift;
        my $string = join("", @_);
@@ -325,6 +343,10 @@ sub highlight_prefix {
        return "$prompt_color$prefix$normal_color$remainder";
 }
 
+sub error_msg {
+       print STDERR colored $error_color, @_;
+}
+
 sub list_and_choose {
        my ($opts, @stuff) = @_;
        my (@chosen, @return);
@@ -420,12 +442,12 @@ sub list_and_choose {
                        else {
                                $bottom = $top = find_unique($choice, @stuff);
                                if (!defined $bottom) {
-                                       print "Huh ($choice)?\n";
+                                       error_msg "Huh ($choice)?\n";
                                        next TOPLOOP;
                                }
                        }
                        if ($opts->{SINGLETON} && $bottom != $top) {
-                               print "Huh ($choice)?\n";
+                               error_msg "Huh ($choice)?\n";
                                next TOPLOOP;
                        }
                        for ($i = $bottom-1; $i <= $top-1; $i++) {
@@ -758,11 +780,32 @@ sub diff_applies {
        return close $fh;
 }
 
+sub _restore_terminal_and_die {
+       ReadMode 'restore';
+       print "\n";
+       exit 1;
+}
+
+sub prompt_single_character {
+       if ($use_readkey) {
+               local $SIG{TERM} = \&_restore_terminal_and_die;
+               local $SIG{INT} = \&_restore_terminal_and_die;
+               ReadMode 'cbreak';
+               my $key = ReadKey 0;
+               ReadMode 'restore';
+               print "$key" if defined $key;
+               print "\n";
+               return $key;
+       } else {
+               return <STDIN>;
+       }
+}
+
 sub prompt_yesno {
        my ($prompt) = @_;
        while (1) {
                print colored $prompt_color, $prompt;
-               my $line = <STDIN>;
+               my $line = prompt_single_character;
                return 0 if $line =~ /^n/i;
                return 1 if $line =~ /^y/i;
        }
@@ -893,7 +936,7 @@ sub patch_update_file {
                        print @{$mode->{DISPLAY}};
                        print colored $prompt_color,
                                "Stage mode change [y/n/a/d/?]? ";
-                       my $line = <STDIN>;
+                       my $line = prompt_single_character;
                        if ($line =~ /^y/i) {
                                $mode->{USE} = 1;
                                last;
@@ -966,7 +1009,7 @@ sub patch_update_file {
                        print;
                }
                print colored $prompt_color, "Stage this hunk [y,n,a,d,/$other,?]? ";
-               my $line = <STDIN>;
+               my $line = prompt_single_character;
                if ($line) {
                        if ($line =~ /^y/i) {
                                $hunk[$ix]{USE} = 1;
@@ -1000,11 +1043,11 @@ sub patch_update_file {
                                        chomp $response;
                                }
                                if ($response !~ /^\s*\d+\s*$/) {
-                                       print STDERR "Invalid number: '$response'\n";
+                                       error_msg "Invalid number: '$response'\n";
                                } elsif (0 < $response && $response <= $num) {
                                        $ix = $response - 1;
                                } else {
-                                       print STDERR "Sorry, only $num hunks available.\n";
+                                       error_msg "Sorry, only $num hunks available.\n";
                                }
                                next;
                        }
@@ -1018,14 +1061,22 @@ sub patch_update_file {
                                next;
                        }
                        elsif ($line =~ m|^/(.*)|) {
+                               my $regex = $1;
+                               if ($1 eq "") {
+                                       print colored $prompt_color, "search for regex? ";
+                                       $regex = <STDIN>;
+                                       if (defined $regex) {
+                                               chomp $regex;
+                                       }
+                               }
                                my $search_string;
                                eval {
-                                       $search_string = qr{$1}m;
+                                       $search_string = qr{$regex}m;
                                };
                                if ($@) {
                                        my ($err,$exp) = ($@, $1);
                                        $err =~ s/ at .*git-add--interactive line \d+, <STDIN> line \d+.*$//;
-                                       print STDERR "Malformed search regexp $exp: $err\n";
+                                       error_msg "Malformed search regexp $exp: $err\n";
                                        next;
                                }
                                my $iy = $ix;
@@ -1035,7 +1086,7 @@ sub patch_update_file {
                                        $iy++;
                                        $iy = 0 if ($iy >= $num);
                                        if ($ix == $iy) {
-                                               print STDERR "No hunk matches the given pattern\n";
+                                               error_msg "No hunk matches the given pattern\n";
                                                last;
                                        }
                                }
@@ -1047,7 +1098,7 @@ sub patch_update_file {
                                        $ix--;
                                }
                                else {
-                                       print STDERR "No previous hunk\n";
+                                       error_msg "No previous hunk\n";
                                }
                                next;
                        }
@@ -1056,7 +1107,7 @@ sub patch_update_file {
                                        $ix++;
                                }
                                else {
-                                       print STDERR "No next hunk\n";
+                                       error_msg "No next hunk\n";
                                }
                                next;
                        }
@@ -1069,13 +1120,13 @@ sub patch_update_file {
                                        }
                                }
                                else {
-                                       print STDERR "No previous hunk\n";
+                                       error_msg "No previous hunk\n";
                                }
                                next;
                        }
                        elsif ($line =~ /^j/) {
                                if ($other !~ /j/) {
-                                       print STDERR "No next hunk\n";
+                                       error_msg "No next hunk\n";
                                        next;
                                }
                        }
index eb62f719b0ad1d015bb116e8447971c5fe7fdba6..27b57b826a26d80551169b355a5e8fc1a35de994 100755 (executable)
@@ -108,7 +108,7 @@ OPTIONS_SPEC=
 . git-sh-setup
 
 if [ "$(is_bare_repository)" = false ]; then
-       git diff-files --quiet &&
+       git diff-files --ignore-submodules --quiet &&
        git diff-index --cached --quiet HEAD -- ||
        die "Cannot rewrite branch(es) with a dirty working directory."
 fi
@@ -221,7 +221,7 @@ die ""
 trap 'cd ../..; rm -rf "$tempdir"' 0
 
 # Make sure refs/original is empty
-git for-each-ref > "$tempdir"/backup-refs
+git for-each-ref > "$tempdir"/backup-refs || exit
 while read sha1 type name
 do
        case "$force,$name" in
@@ -241,8 +241,9 @@ GIT_WORK_TREE=.
 export GIT_DIR GIT_WORK_TREE
 
 # The refs should be updated if their heads were rewritten
-git rev-parse --no-flags --revs-only --symbolic-full-name --default HEAD "$@" |
-sed -e '/^^/d' >"$tempdir"/heads
+git rev-parse --no-flags --revs-only --symbolic-full-name \
+       --default HEAD "$@" > "$tempdir"/raw-heads || exit
+sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads
 
 test -s "$tempdir"/heads ||
        die "Which ref do you want to rewrite?"
@@ -251,8 +252,6 @@ GIT_INDEX_FILE="$(pwd)/../index"
 export GIT_INDEX_FILE
 git read-tree || die "Could not seed the index"
 
-ret=0
-
 # map old->new commit ids for rewriting parents
 mkdir ../map || die "Could not create map/ directory"
 
@@ -315,10 +314,11 @@ while read commit parents; do
                        die "tree filter failed: $filter_tree"
 
                (
-                       git diff-index -r --name-only $commit
+                       git diff-index -r --name-only $commit &&
                        git ls-files --others
-               ) |
-               git update-index --add --replace --remove --stdin
+               ) > "$tempdir"/tree-state || exit
+               git update-index --add --replace --remove --stdin \
+                       < "$tempdir"/tree-state || exit
        fi
 
        eval "$filter_index" < /dev/null ||
@@ -339,7 +339,8 @@ while read commit parents; do
                eval "$filter_msg" > ../message ||
                        die "msg filter failed: $filter_msg"
        @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
-               $(git write-tree) $parentstr < ../message > ../map/$commit
+               $(git write-tree) $parentstr < ../message > ../map/$commit ||
+                       die "could not write rewritten commit"
 done <../revs
 
 # In case of a subdirectory filter, it is possible that a specified head
@@ -407,7 +408,8 @@ do
                        die "Could not rewrite $ref"
        ;;
        esac
-       git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1
+       git update-ref -m "filter-branch: backup" "$orig_namespace$ref" $sha1 ||
+                exit
 done < "$tempdir"/heads
 
 # TODO: This should possibly go, with the semantics that all positive given
@@ -469,20 +471,21 @@ rm -rf "$tempdir"
 
 trap - 0
 
+unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
+test -z "$ORIG_GIT_DIR" || {
+       GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
+}
+test -z "$ORIG_GIT_WORK_TREE" || {
+       GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
+       export GIT_WORK_TREE
+}
+test -z "$ORIG_GIT_INDEX_FILE" || {
+       GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
+       export GIT_INDEX_FILE
+}
+
 if [ "$(is_bare_repository)" = false ]; then
-       unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
-       test -z "$ORIG_GIT_DIR" || {
-               GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
-       }
-       test -z "$ORIG_GIT_WORK_TREE" || {
-               GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
-               export GIT_WORK_TREE
-       }
-       test -z "$ORIG_GIT_INDEX_FILE" || {
-               GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
-               export GIT_INDEX_FILE
-       }
-       git read-tree -u -m HEAD
+       git read-tree -u -m HEAD || exit
 fi
 
-exit $ret
+exit 0
diff --git a/git-notes.sh b/git-notes.sh
deleted file mode 100755 (executable)
index bfdbaa8..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#!/bin/sh
-
-USAGE="(edit | show) [commit]"
-. git-sh-setup
-
-test -n "$3" && usage
-
-test -z "$1" && usage
-ACTION="$1"; shift
-
-test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="$(git config core.notesref)"
-test -z "$GIT_NOTES_REF" && GIT_NOTES_REF="refs/notes/commits"
-
-COMMIT=$(git rev-parse --verify --default HEAD "$@") ||
-die "Invalid commit: $@"
-
-MESSAGE="$GIT_DIR"/new-notes-$COMMIT
-trap '
-       test -f "$MESSAGE" && rm "$MESSAGE"
-' 0
-
-case "$ACTION" in
-edit)
-       GIT_NOTES_REF= git log -1 $COMMIT | sed "s/^/#/" > "$MESSAGE"
-
-       GIT_INDEX_FILE="$MESSAGE".idx
-       export GIT_INDEX_FILE
-
-       CURRENT_HEAD=$(git show-ref "$GIT_NOTES_REF" | cut -f 1 -d ' ')
-       if [ -z "$CURRENT_HEAD" ]; then
-               PARENT=
-       else
-               PARENT="-p $CURRENT_HEAD"
-               git read-tree "$GIT_NOTES_REF" || die "Could not read index"
-               git cat-file blob :$COMMIT >> "$MESSAGE" 2> /dev/null
-       fi
-
-       ${VISUAL:-${EDITOR:-vi}} "$MESSAGE"
-
-       grep -v ^# < "$MESSAGE" | git stripspace > "$MESSAGE".processed
-       mv "$MESSAGE".processed "$MESSAGE"
-       if [ -s "$MESSAGE" ]; then
-               BLOB=$(git hash-object -w "$MESSAGE") ||
-                       die "Could not write into object database"
-               git update-index --add --cacheinfo 0644 $BLOB $COMMIT ||
-                       die "Could not write index"
-       else
-               test -z "$CURRENT_HEAD" &&
-                       die "Will not initialise with empty tree"
-               git update-index --force-remove $COMMIT ||
-                       die "Could not update index"
-       fi
-
-       TREE=$(git write-tree) || die "Could not write tree"
-       NEW_HEAD=$(echo Annotate $COMMIT | git commit-tree $TREE $PARENT) ||
-               die "Could not annotate"
-       git update-ref -m "Annotate $COMMIT" \
-               "$GIT_NOTES_REF" $NEW_HEAD $CURRENT_HEAD
-;;
-show)
-       git show "$GIT_NOTES_REF":$COMMIT
-;;
-*)
-       usage
-esac
index 458a497af810c7bb188a5aafb80c32aa0bc05264..be6db5e805d62e0440f7de3c1f6ee3fcd16a2ed3 100755 (executable)
@@ -88,32 +88,79 @@ if [ -z "$names" ]; then
                echo Nothing new to pack.
        fi
 fi
-for name in $names ; do
-       fullbases="$fullbases pack-$name"
-       chmod a-w "$PACKTMP-$name.pack"
-       chmod a-w "$PACKTMP-$name.idx"
-       mkdir -p "$PACKDIR" || exit
 
+# Ok we have prepared all new packfiles.
+mkdir -p "$PACKDIR" || exit
+
+# First see if there are packs of the same name and if so
+# if we can move them out of the way (this can happen if we
+# repacked immediately after packing fully.
+rollback=
+failed=
+for name in $names
+do
        for sfx in pack idx
        do
-               if test -f "$PACKDIR/pack-$name.$sfx"
-               then
-                       mv -f "$PACKDIR/pack-$name.$sfx" \
-                               "$PACKDIR/old-pack-$name.$sfx"
-               fi
-       done &&
+               file=pack-$name.$sfx
+               test -f "$PACKDIR/$file" || continue
+               rm -f "$PACKDIR/old-$file" &&
+               mv "$PACKDIR/$file" "$PACKDIR/old-$file" || {
+                       failed=t
+                       break
+               }
+               rollback="$rollback $file"
+       done
+       test -z "$failed" || break
+done
+
+# If renaming failed for any of them, roll the ones we have
+# already renamed back to their original names.
+if test -n "$failed"
+then
+       rollback_failure=
+       for file in $rollback
+       do
+               mv "$PACKDIR/old-$file" "$PACKDIR/$file" ||
+               rollback_failure="$rollback_failure $file"
+       done
+       if test -n "$rollback_failure"
+       then
+               echo >&2 "WARNING: Some packs in use have been renamed by"
+               echo >&2 "WARNING: prefixing old- to their name, in order to"
+               echo >&2 "WARNING: replace them with the new version of the"
+               echo >&2 "WARNING: file.  But the operation failed, and"
+               echo >&2 "WARNING: attempt to rename them back to their"
+               echo >&2 "WARNING: original names also failed."
+               echo >&2 "WARNING: Please rename them in $PACKDIR manually:"
+               for file in $rollback_failure
+               do
+                       echo >&2 "WARNING:   old-$file -> $file"
+               done
+       fi
+       exit 1
+fi
+
+# Now the ones with the same name are out of the way...
+fullbases=
+for name in $names
+do
+       fullbases="$fullbases pack-$name"
+       chmod a-w "$PACKTMP-$name.pack"
+       chmod a-w "$PACKTMP-$name.idx"
        mv -f "$PACKTMP-$name.pack" "$PACKDIR/pack-$name.pack" &&
-       mv -f "$PACKTMP-$name.idx"  "$PACKDIR/pack-$name.idx" &&
-       test -f "$PACKDIR/pack-$name.pack" &&
-       test -f "$PACKDIR/pack-$name.idx" || {
-               echo >&2 "Couldn't replace the existing pack with updated one."
-               echo >&2 "The original set of packs have been saved as"
-               echo >&2 "old-pack-$name.{pack,idx} in $PACKDIR."
-               exit 1
-       }
-       rm -f "$PACKDIR/old-pack-$name.pack" "$PACKDIR/old-pack-$name.idx"
+       mv -f "$PACKTMP-$name.idx"  "$PACKDIR/pack-$name.idx" ||
+       exit
+done
+
+# Remove the "old-" files
+for name in $names
+do
+       rm -f "$PACKDIR/old-pack-$name.idx"
+       rm -f "$PACKDIR/old-pack-$name.pack"
 done
 
+# End of pack replacement.
+
 if test "$remove_redundant" = t
 then
        # We know $existing are all redundant.
index af8d10ca8380afb129a60e5622b4e1a148cea515..204aab671ef78edc24acff4019a2f40a71a59020 100755 (executable)
@@ -60,7 +60,7 @@ resolve_relative_url ()
 #
 module_list()
 {
-       git ls-files --stage -- "$@" | grep '^160000 '
+       git ls-files --error-unmatch --stage -- "$@" | grep '^160000 '
 }
 
 #
index 79888a05c400ef495157a39aad1de12cf16ea7d2..001a1d8efffe804898ddc31e1f64924a429e4c0e 100755 (executable)
@@ -2389,22 +2389,8 @@ sub find_parent_branch {
        print STDERR  "Found possible branch point: ",
                      "$new_url => ", $self->full_url, ", $r\n";
        $branch_from =~ s#^/##;
-       my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
-       unless ($gs) {
-               my $ref_id = $self->{ref_id};
-               $ref_id =~ s/\@\d+$//;
-               $ref_id .= "\@$r";
-               # just grow a tail if we're not unique enough :x
-               $ref_id .= '-' while find_ref($ref_id);
-               print STDERR "Initializing parent: $ref_id\n";
-               my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
-               if ($u =~ s#^\Q$url\E(/|$)##) {
-                       $p = $u;
-                       $u = $url;
-                       $repo_id = $self->{repo_id};
-               }
-               $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
-       }
+       my $gs = $self->other_gs($new_url, $url, $repos_root,
+                                $branch_from, $r, $self->{ref_id});
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        {
                my ($base, $head);
@@ -2431,7 +2417,7 @@ sub find_parent_branch {
                        # is not included with SVN 1.4.3 (the latest version
                        # at the moment), so we can't rely on it
                        $self->{last_commit} = $parent;
-                       $ed = SVN::Git::Fetcher->new($self);
+                       $ed = SVN::Git::Fetcher->new($self, $gs->{path});
                        $gs->ra->gs_do_switch($r0, $rev, $gs,
                                              $self->full_url, $ed)
                          or die "SVN connection failed somewhere...\n";
@@ -2586,6 +2572,28 @@ sub parse_svn_date {
        return $parsed_date;
 }
 
+sub other_gs {
+       my ($self, $new_url, $url, $repos_root,
+           $branch_from, $r, $old_ref_id) = @_;
+       my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
+       unless ($gs) {
+               my $ref_id = $old_ref_id;
+               $ref_id =~ s/\@\d+$//;
+               $ref_id .= "\@$r";
+               # just grow a tail if we're not unique enough :x
+               $ref_id .= '-' while find_ref($ref_id);
+               print STDERR "Initializing parent: $ref_id\n";
+               my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
+               if ($u =~ s#^\Q$url\E(/|$)##) {
+                       $p = $u;
+                       $u = $url;
+                       $repo_id = $self->{repo_id};
+               }
+               $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+       }
+       $gs
+}
+
 sub check_author {
        my ($author) = @_;
        if (!defined $author || length $author == 0) {
@@ -3250,12 +3258,13 @@ package SVN::Git::Fetcher;
 
 # file baton members: path, mode_a, mode_b, pool, fh, blob, base
 sub new {
-       my ($class, $git_svn) = @_;
+       my ($class, $git_svn, $switch_path) = @_;
        my $self = SVN::Delta::Editor->new;
        bless $self, $class;
        if (exists $git_svn->{last_commit}) {
                $self->{c} = $git_svn->{last_commit};
-               $self->{empty_symlinks} = _mark_empty_symlinks($git_svn);
+               $self->{empty_symlinks} =
+                                 _mark_empty_symlinks($git_svn, $switch_path);
        }
        $self->{empty} = {};
        $self->{dir_prop} = {};
@@ -3270,19 +3279,39 @@ sub new {
 # not inside them (when the Git::SVN::Fetcher object is passed) to
 # do_{switch,update}
 sub _mark_empty_symlinks {
-       my ($git_svn) = @_;
+       my ($git_svn, $switch_path) = @_;
+       my $bool = Git::config_bool('svn.brokenSymlinkWorkaround');
+       return {} if (defined($bool) && ! $bool);
+
        my %ret;
        my ($rev, $cmt) = $git_svn->last_rev_commit;
        return {} unless ($rev && $cmt);
 
+       # allow the warning to be printed for each revision we fetch to
+       # ensure the user sees it.  The user can also disable the workaround
+       # on the repository even while git svn is running and the next
+       # revision fetched will skip this expensive function.
+       my $printed_warning;
        chomp(my $empty_blob = `git hash-object -t blob --stdin < /dev/null`);
        my ($ls, $ctx) = command_output_pipe(qw/ls-tree -r -z/, $cmt);
        local $/ = "\0";
-       my $pfx = $git_svn->{path};
+       my $pfx = defined($switch_path) ? $switch_path : $git_svn->{path};
        $pfx .= '/' if length($pfx);
        while (<$ls>) {
                chomp;
                s/\A100644 blob $empty_blob\t//o or next;
+               unless ($printed_warning) {
+                       print STDERR "Scanning for empty symlinks, ",
+                                    "this may take a while if you have ",
+                                    "many empty files\n",
+                                    "You may disable this with `",
+                                    "git config svn.brokenSymlinkWorkaround ",
+                                    "false'.\n",
+                                    "This may be done in a different ",
+                                    "terminal without restarting ",
+                                    "git svn\n";
+                       $printed_warning = 1;
+               }
                my $path = $_;
                my (undef, $props) =
                               $git_svn->ra->get_file($pfx.$path, $rev, undef);
@@ -4348,6 +4377,9 @@ sub gs_fetch_loop_common {
                }
                $self->get_log([$longest_path], $min, $max, 0, 1, 1,
                               sub { $revs{$_[1]} = _cb(@_) });
+               if ($err) {
+                       print "Checked through r$max\r";
+               }
                if ($err && $max >= $head) {
                        print STDERR "Path '$longest_path' ",
                                     "was probably deleted:\n",
index 78d236b77f6e2b52d89bf7b579762723ede86d15..7ed0faddcd75e024ce18d8b80994d41a9207a7ca 100755 (executable)
@@ -115,7 +115,7 @@ if test -z "$browser" ; then
        browser_candidates="open $browser_candidates"
     fi
     # /bin/start indicates MinGW
-    if test -n /bin/start; then
+    if test -x /bin/start; then
        browser_candidates="start $browser_candidates"
     fi
 
index a9dc2e57d9bd79a505ca165c693ce0f3ccd2f333..8433dd1d45780b3947cc4a0b31ee2059ea6e65bf 100644 (file)
@@ -212,6 +212,11 @@ not include variables usually directly set during build):
    Rename detection options for git-diff and git-diff-tree. By default
    ('-M'); set it to ('-C') or ('-C', '-C') to also detect copies, or
    set it to () if you don't want to have renames detection.
+ * $prevent_xss
+   If true, some gitweb features are disabled to prevent content in
+   repositories from launching cross-site scripting (XSS) attacks.  Set this
+   to true if you don't trust the content of your repositories. The default
+   is false.
 
 
 Projects list file format
@@ -258,7 +263,9 @@ You can use the following files in repository:
    A .html file (HTML fragment) which is included on the gitweb project
    summary page inside <div> block element. You can use it for longer
    description of a project, to provide links (for example to project's
-   homepage), etc.
+   homepage), etc. This is recognized only if XSS prevention is off
+   ($prevent_xss is false); a way to include a readme safely when XSS
+   prevention is on may be worked out in the future.
  * description (or gitweb.description)
    Short (shortened by default to 25 characters in the projects list page)
    single line description of a project (of a repository). Plain text file;
index f27dbb6bf4acfe6f5381d7c86760b1170c8a10ff..8dffa3fd538361c8989ca27ca3842ad6bfc64f6e 100755 (executable)
@@ -132,6 +132,10 @@ BEGIN
 # - one might want to include '-B' option, e.g. '-B', '-M'
 our @diff_opts = ('-M'); # taken from git_commit
 
+# Disables features that would allow repository owners to inject script into
+# the gitweb domain.
+our $prevent_xss = 0;
+
 # information about snapshot formats that gitweb is capable of serving
 our %known_snapshot_formats = (
        # name => {
@@ -1364,7 +1368,7 @@ sub format_log_line_html {
        my $line = shift;
 
        $line = esc_html($line, -nbsp=>1);
-       if ($line =~ m/([0-9a-fA-F]{8,40})/) {
+       if ($line =~ m/\b([0-9a-fA-F]{8,40})\b/) {
                my $hash_text = $1;
                my $link =
                        $cgi->a({-href => href(action=>"object", hash=>$hash_text),
@@ -4503,7 +4507,9 @@ sub git_summary {
 
        print "</table>\n";
 
-       if (-s "$projectroot/$project/README.html") {
+       # If XSS prevention is on, we don't include README.html.
+       # TODO: Allow a readme in some safe format.
+       if (!$prevent_xss && -s "$projectroot/$project/README.html") {
                print "<div class=\"title\">readme</div>\n" .
                      "<div class=\"readme\">\n";
                insert_file("$projectroot/$project/README.html");
@@ -4764,10 +4770,21 @@ sub git_blob_plain {
                $save_as .= '.txt';
        }
 
+       # With XSS prevention on, blobs of all types except a few known safe
+       # ones are served with "Content-Disposition: attachment" to make sure
+       # they don't run in our security domain.  For certain image types,
+       # blob view writes an <img> tag referring to blob_plain view, and we
+       # want to be sure not to break that by serving the image as an
+       # attachment (though Firefox 3 doesn't seem to care).
+       my $sandbox = $prevent_xss &&
+               $type !~ m!^(?:text/plain|image/(?:gif|png|jpeg))$!;
+
        print $cgi->header(
                -type => $type,
                -expires => $expires,
-               -content_disposition => 'inline; filename="' . $save_as . '"');
+               -content_disposition =>
+                       ($sandbox ? 'attachment' : 'inline')
+                       . '; filename="' . $save_as . '"');
        undef $/;
        binmode STDOUT, ':raw';
        print <$fd>;
index a8ae545dfb952b9108ef679a176d57eb2216d622..30d2d340418f7f40b77823b1b58b307985347bdf 100644 (file)
@@ -153,6 +153,7 @@ struct remote_lock
        char *url;
        char *owner;
        char *token;
+       char tmpfile_suffix[41];
        time_t start_time;
        long timeout;
        int refreshing;
@@ -557,8 +558,7 @@ static void start_put(struct transfer_request *request)
        request->dest = strbuf_detach(&buf, NULL);
 
        append_remote_object_url(&buf, remote->url, hex, 0);
-       strbuf_addstr(&buf, "_");
-       strbuf_addstr(&buf, request->lock->token);
+       strbuf_add(&buf, request->lock->tmpfile_suffix, 41);
        request->url = strbuf_detach(&buf, NULL);
 
        slot = get_active_slot();
@@ -1130,6 +1130,8 @@ static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
 static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
 {
        struct remote_lock *lock = (struct remote_lock *)ctx->userData;
+       git_SHA_CTX sha_ctx;
+       unsigned char lock_token_sha1[20];
 
        if (tag_closed && ctx->cdata) {
                if (!strcmp(ctx->name, DAV_ACTIVELOCK_OWNER)) {
@@ -1142,6 +1144,13 @@ static void handle_new_lock_ctx(struct xml_ctx *ctx, int tag_closed)
                } else if (!strcmp(ctx->name, DAV_ACTIVELOCK_TOKEN)) {
                        lock->token = xmalloc(strlen(ctx->cdata) + 1);
                        strcpy(lock->token, ctx->cdata);
+
+                       git_SHA1_Init(&sha_ctx);
+                       git_SHA1_Update(&sha_ctx, lock->token, strlen(lock->token));
+                       git_SHA1_Final(lock_token_sha1, &sha_ctx);
+
+                       lock->tmpfile_suffix[0] = '_';
+                       memcpy(lock->tmpfile_suffix + 1, sha1_to_hex(lock_token_sha1), 40);
                }
        }
 }
index 194ddb13da09668334cd9e1f6eeef517c3346ee9..84a74e544b7bcc20c887f321e389ecf3cfb560d6 100644 (file)
@@ -48,7 +48,7 @@ static void show_parents(struct commit *commit, int abbrev)
        struct commit_list *p;
        for (p = commit->parents; p ; p = p->next) {
                struct commit *parent = p->item;
-               printf(" %s", diff_unique_abbrev(parent->object.sha1, abbrev));
+               printf(" %s", find_unique_abbrev(parent->object.sha1, abbrev));
        }
 }
 
@@ -280,7 +280,7 @@ void show_log(struct rev_info *opt)
                                        putchar('>');
                        }
                }
-               fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
+               fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
                show_decorations(opt, commit);
@@ -348,13 +348,13 @@ void show_log(struct rev_info *opt)
                                        putchar('>');
                        }
                }
-               fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit),
+               fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
                      stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
                if (parent)
                        printf(" (from %s)",
-                              diff_unique_abbrev(parent->object.sha1,
+                              find_unique_abbrev(parent->object.sha1,
                                                  abbrev_commit));
                show_decorations(opt, commit);
                printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
index 88fc6f394684436967002ca477eac1e084537348..f12bb45a3f734e48f003a7a2943d168c26778603 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
 #include "string-list.h"
 #include "mailmap.h"
 
-int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
+#define DEBUG_MAILMAP 0
+#if DEBUG_MAILMAP
+#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#else
+static inline void debug_mm(const char *format, ...) {}
+#endif
+
+const char *git_mailmap_file;
+
+struct mailmap_info {
+       char *name;
+       char *email;
+};
+
+struct mailmap_entry {
+       /* name and email for the simple mail-only case */
+       char *name;
+       char *email;
+
+       /* name and email for the complex mail and name matching case */
+       struct string_list namemap;
+};
+
+static void free_mailmap_info(void *p, const char *s)
+{
+       struct mailmap_info *mi = (struct mailmap_info *)p;
+       debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email);
+       free(mi->name);
+       free(mi->email);
+}
+
+static void free_mailmap_entry(void *p, const char *s)
+{
+       struct mailmap_entry *me = (struct mailmap_entry *)p;
+       debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr);
+       debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email);
+       free(me->name);
+       free(me->email);
+
+       me->namemap.strdup_strings = 1;
+       string_list_clear_func(&me->namemap, free_mailmap_info);
+}
+
+static void add_mapping(struct string_list *map,
+                       char *new_name, char *new_email, char *old_name, char *old_email)
+{
+       struct mailmap_entry *me;
+       int index;
+       if (old_email == NULL) {
+               old_email = new_email;
+               new_email = NULL;
+       }
+
+       if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
+               /* mailmap entry exists, invert index value */
+               index = -1 - index;
+       } else {
+               /* create mailmap entry */
+               struct string_list_item *item = string_list_insert_at_index(index, old_email, map);
+               item->util = xmalloc(sizeof(struct mailmap_entry));
+               memset(item->util, 0, sizeof(struct mailmap_entry));
+               ((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
+       }
+       me = (struct mailmap_entry *)map->items[index].util;
+
+       if (old_name == NULL) {
+               debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
+               /* Replace current name and new email for simple entry */
+               free(me->name);
+               free(me->email);
+               if (new_name)
+                       me->name = xstrdup(new_name);
+               if (new_email)
+                       me->email = xstrdup(new_email);
+       } else {
+               struct mailmap_info *mi = xmalloc(sizeof(struct mailmap_info));
+               debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
+               if (new_name)
+                       mi->name = xstrdup(new_name);
+               if (new_email)
+                       mi->email = xstrdup(new_email);
+               string_list_insert(old_name, &me->namemap)->util = mi;
+       }
+
+       debug_mm("mailmap:  '%s' <%s> -> '%s' <%s>\n",
+                old_name, old_email, new_name, new_email);
+}
+
+static char *parse_name_and_email(char *buffer, char **name, char **email)
+{
+       char *left, *right, *nstart, *nend;
+       *name = *email = 0;
+
+       if ((left = strchr(buffer, '<')) == NULL)
+               return NULL;
+       if ((right = strchr(left+1, '>')) == NULL)
+               return NULL;
+       if (left+1 == right)
+               return NULL;
+
+       /* remove whitespace from beginning and end of name */
+       nstart = buffer;
+       while (isspace(*nstart) && nstart < left)
+               ++nstart;
+       nend = left-1;
+       while (isspace(*nend) && nend > nstart)
+               --nend;
+
+       *name = (nstart < nend ? nstart : NULL);
+       *email = left+1;
+       *(nend+1) = '\0';
+       *right++ = '\0';
+
+       return (*right == '\0' ? NULL : right);
+}
+
+static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
 {
        char buffer[1024];
-       FILE *f = fopen(filename, "r");
+       FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
 
        if (f == NULL)
                return 1;
        while (fgets(buffer, sizeof(buffer), f) != NULL) {
-               char *end_of_name, *left_bracket, *right_bracket;
-               char *name, *email;
-               int i;
+               char *name1 = 0, *email1 = 0, *name2 = 0, *email2 = 0;
                if (buffer[0] == '#') {
                        static const char abbrev[] = "# repo-abbrev:";
                        int abblen = sizeof(abbrev) - 1;
@@ -36,41 +150,49 @@ int read_mailmap(struct string_list *map, const char *filename, char **repo_abbr
                        }
                        continue;
                }
-               if ((left_bracket = strchr(buffer, '<')) == NULL)
-                       continue;
-               if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
-                       continue;
-               if (right_bracket == left_bracket + 1)
-                       continue;
-               for (end_of_name = left_bracket;
-                    end_of_name != buffer && isspace(end_of_name[-1]);
-                    end_of_name--)
-                       ; /* keep on looking */
-               if (end_of_name == buffer)
-                       continue;
-               name = xmalloc(end_of_name - buffer + 1);
-               strlcpy(name, buffer, end_of_name - buffer + 1);
-               email = xmalloc(right_bracket - left_bracket);
-               for (i = 0; i < right_bracket - left_bracket - 1; i++)
-                       email[i] = tolower(left_bracket[i + 1]);
-               email[right_bracket - left_bracket - 1] = '\0';
-               string_list_insert(email, map)->util = name;
+               if ((name2 = parse_name_and_email(buffer, &name1, &email1)) != NULL)
+                       parse_name_and_email(name2, &name2, &email2);
+
+               if (email1)
+                       add_mapping(map, name1, email1, name2, email2);
        }
        fclose(f);
        return 0;
 }
 
-int map_email(struct string_list *map, const char *email, char *name, int maxlen)
+int read_mailmap(struct string_list *map, char **repo_abbrev)
+{
+       map->strdup_strings = 1;
+       /* each failure returns 1, so >1 means both calls failed */
+       return read_single_mailmap(map, ".mailmap", repo_abbrev) +
+              read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;
+}
+
+void clear_mailmap(struct string_list *map)
+{
+       debug_mm("mailmap: clearing %d entries...\n", map->nr);
+       map->strdup_strings = 1;
+       string_list_clear_func(map, free_mailmap_entry);
+       debug_mm("mailmap: cleared\n");
+}
+
+int map_user(struct string_list *map,
+            char *email, int maxlen_email, char *name, int maxlen_name)
 {
        char *p;
        struct string_list_item *item;
+       struct mailmap_entry *me;
        char buf[1024], *mailbuf;
        int i;
 
-       /* autocomplete common developers */
+       /* figure out space requirement for email */
        p = strchr(email, '>');
-       if (!p)
-               return 0;
+       if (!p) {
+               /* email passed in might not be wrapped in <>, but end with a \0 */
+               p = memchr(email, '\0', maxlen_email);
+               if (p == 0)
+                       return 0;
+       }
        if (p - email + 1 < sizeof(buf))
                mailbuf = buf;
        else
@@ -80,13 +202,39 @@ int map_email(struct string_list *map, const char *email, char *name, int maxlen
        for (i = 0; i < p - email; i++)
                mailbuf[i] = tolower(email[i]);
        mailbuf[i] = 0;
+
+       debug_mm("map_user: map '%s' <%s>\n", name, mailbuf);
        item = string_list_lookup(mailbuf, map);
+       if (item != NULL) {
+               me = (struct mailmap_entry *)item->util;
+               if (me->namemap.nr) {
+                       /* The item has multiple items, so we'll look up on name too */
+                       /* If the name is not found, we choose the simple entry      */
+                       struct string_list_item *subitem = string_list_lookup(name, &me->namemap);
+                       if (subitem)
+                               item = subitem;
+               }
+       }
        if (mailbuf != buf)
                free(mailbuf);
        if (item != NULL) {
-               const char *realname = (const char *)item->util;
-               strlcpy(name, realname, maxlen);
+               struct mailmap_info *mi = (struct mailmap_info *)item->util;
+               if (mi->name == NULL && (mi->email == NULL || maxlen_email == 0)) {
+                       debug_mm("map_user:  -- (no simple mapping)\n");
+                       return 0;
+               }
+               if (maxlen_email && mi->email)
+                       strlcpy(email, mi->email, maxlen_email);
+               if (maxlen_name && mi->name)
+                       strlcpy(name, mi->name, maxlen_name);
+               debug_mm("map_user:  to '%s' <%s>\n", name, mi->email ? mi->email : "");
                return 1;
        }
+       debug_mm("map_user:  --\n");
        return 0;
 }
+
+int map_email(struct string_list *map, const char *email, char *name, int maxlen)
+{
+       return map_user(map, (char *)email, 0, name, maxlen);
+}
index 6e48f83cedd13e24d50cddf47f037791ddc5ad4b..4b2ca3a7de972c10f214b38a25be522abcbbafd0 100644 (file)
--- a/mailmap.h
+++ b/mailmap.h
@@ -1,7 +1,11 @@
 #ifndef MAILMAP_H
 #define MAILMAP_H
 
-int read_mailmap(struct string_list *map, const char *filename, char **repo_abbrev);
+int read_mailmap(struct string_list *map, char **repo_abbrev);
+void clear_mailmap(struct string_list *map);
+
 int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen);
+int map_user(struct string_list *mailmap,
+            char *email, int maxlen_email, char *name, int maxlen_name);
 
 #endif
index b97026bd5cc1d2ef1b46a9ef3dcd7562ad52c377..ee853b990d8bfb15e0058fefbbd267bd58ed40fc 100644 (file)
@@ -237,7 +237,7 @@ static int save_files_dirs(const unsigned char *sha1,
                string_list_insert(newpath, &o->current_file_set);
        free(newpath);
 
-       return READ_TREE_RECURSIVE;
+       return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
 }
 
 static int get_files_dirs(struct merge_options *o, struct tree *tree)
diff --git a/notes.c b/notes.c
deleted file mode 100644 (file)
index bd73784..0000000
--- a/notes.c
+++ /dev/null
@@ -1,160 +0,0 @@
-#include "cache.h"
-#include "commit.h"
-#include "notes.h"
-#include "refs.h"
-#include "utf8.h"
-#include "strbuf.h"
-#include "tree-walk.h"
-
-struct entry {
-       unsigned char commit_sha1[20];
-       unsigned char notes_sha1[20];
-};
-
-struct hash_map {
-       struct entry *entries;
-       off_t count, size;
-};
-
-static int initialized;
-static struct hash_map hash_map;
-
-static int hash_index(struct hash_map *map, const unsigned char *sha1)
-{
-       int i = ((*(unsigned int *)sha1) % map->size);
-
-       for (;;) {
-               unsigned char *current = map->entries[i].commit_sha1;
-
-               if (!hashcmp(sha1, current))
-                       return i;
-
-               if (is_null_sha1(current))
-                       return -1 - i;
-
-               if (++i == map->size)
-                       i = 0;
-       }
-}
-
-static void add_entry(const unsigned char *commit_sha1,
-               const unsigned char *notes_sha1)
-{
-       int index;
-
-       if (hash_map.count + 1 > hash_map.size >> 1) {
-               int i, old_size = hash_map.size;
-               struct entry *old = hash_map.entries;
-
-               hash_map.size = old_size ? old_size << 1 : 64;
-               hash_map.entries = (struct entry *)
-                       xcalloc(sizeof(struct entry), hash_map.size);
-
-               for (i = 0; i < old_size; i++)
-                       if (!is_null_sha1(old[i].commit_sha1)) {
-                               index = -1 - hash_index(&hash_map,
-                                               old[i].commit_sha1);
-                               memcpy(hash_map.entries + index, old + i,
-                                       sizeof(struct entry));
-                       }
-               free(old);
-       }
-
-       index = hash_index(&hash_map, commit_sha1);
-       if (index < 0) {
-               index = -1 - index;
-               hash_map.count++;
-       }
-
-       hashcpy(hash_map.entries[index].commit_sha1, commit_sha1);
-       hashcpy(hash_map.entries[index].notes_sha1, notes_sha1);
-}
-
-static void initialize_hash_map(const char *notes_ref_name)
-{
-       unsigned char sha1[20], commit_sha1[20];
-       unsigned mode;
-       struct tree_desc desc;
-       struct name_entry entry;
-       void *buf;
-
-       if (!notes_ref_name || read_ref(notes_ref_name, commit_sha1) ||
-           get_tree_entry(commit_sha1, "", sha1, &mode))
-               return;
-
-       buf = fill_tree_descriptor(&desc, sha1);
-       if (!buf)
-               die("Could not read %s for notes-index", sha1_to_hex(sha1));
-
-       while (tree_entry(&desc, &entry))
-               if (!get_sha1(entry.path, commit_sha1))
-                       add_entry(commit_sha1, entry.sha1);
-       free(buf);
-}
-
-static unsigned char *lookup_notes(const unsigned char *commit_sha1)
-{
-       int index;
-
-       if (!hash_map.size)
-               return NULL;
-
-       index = hash_index(&hash_map, commit_sha1);
-       if (index < 0)
-               return NULL;
-       return hash_map.entries[index].notes_sha1;
-}
-
-void get_commit_notes(const struct commit *commit, struct strbuf *sb,
-               const char *output_encoding)
-{
-       static const char *utf8 = "utf-8";
-       unsigned char *sha1;
-       char *msg, *msg_p;
-       unsigned long linelen, msglen;
-       enum object_type type;
-
-       if (!initialized) {
-               const char *env = getenv(GIT_NOTES_REF_ENVIRONMENT);
-               if (env)
-                       notes_ref_name = getenv(GIT_NOTES_REF_ENVIRONMENT);
-               else if (!notes_ref_name)
-                       notes_ref_name = GIT_NOTES_DEFAULT_REF;
-               initialize_hash_map(notes_ref_name);
-               initialized = 1;
-       }
-
-       sha1 = lookup_notes(commit->object.sha1);
-       if (!sha1)
-               return;
-
-       if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen ||
-                       type != OBJ_BLOB)
-               return;
-
-       if (output_encoding && *output_encoding &&
-                       strcmp(utf8, output_encoding)) {
-               char *reencoded = reencode_string(msg, output_encoding, utf8);
-               if (reencoded) {
-                       free(msg);
-                       msg = reencoded;
-                       msglen = strlen(msg);
-               }
-       }
-
-       /* we will end the annotation by a newline anyway */
-       if (msglen && msg[msglen - 1] == '\n')
-               msglen--;
-
-       strbuf_addstr(sb, "\nNotes:\n");
-
-       for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) {
-               linelen = strchrnul(msg_p, '\n') - msg_p;
-
-               strbuf_addstr(sb, "    ");
-               strbuf_add(sb, msg_p, linelen);
-               strbuf_addch(sb, '\n');
-       }
-
-       free(msg);
-}
diff --git a/notes.h b/notes.h
deleted file mode 100644 (file)
index 79d21b6..0000000
--- a/notes.h
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef NOTES_H
-#define NOTES_H
-
-void get_commit_notes(const struct commit *commit, struct strbuf *sb,
-               const char *output_encoding);
-
-#endif
diff --git a/path.c b/path.c
index 108d9e9599d059a06fd0621188f45a716346ca07..4b9107fed10c1f3551acf1f14d2ba5d1ba8a0b84 100644 (file)
--- a/path.c
+++ b/path.c
@@ -154,7 +154,7 @@ int validate_headref(const char *path)
        /* Make sure it is a "refs/.." symlink */
        if (S_ISLNK(st.st_mode)) {
                len = readlink(path, buffer, sizeof(buffer)-1);
-               if (len >= 11 && !memcmp("refs/heads/", buffer, 11))
+               if (len >= 5 && !memcmp("refs/", buffer, 5))
                        return 0;
                return -1;
        }
@@ -178,7 +178,7 @@ int validate_headref(const char *path)
                len -= 4;
                while (len && isspace(*buf))
                        buf++, len--;
-               if (len >= 11 && !memcmp("refs/heads/", buf, 11))
+               if (len >= 5 && !memcmp("refs/", buf, 5))
                        return 0;
        }
 
@@ -363,56 +363,97 @@ const char *make_relative_path(const char *abs, const char *base)
 }
 
 /*
- * path = absolute path
- * buf = buffer of at least max(2, strlen(path)+1) bytes
- * It is okay if buf == path, but they should not overlap otherwise.
+ * It is okay if dst == src, but they should not overlap otherwise.
  *
- * Performs the following normalizations on path, storing the result in buf:
- * - Removes trailing slashes.
- * - Removes empty components.
+ * Performs the following normalizations on src, storing the result in dst:
+ * - Ensures that components are separated by '/' (Windows only)
+ * - Squashes sequences of '/'.
  * - Removes "." components.
  * - Removes ".." components, and the components the precede them.
- * "" and paths that contain only slashes are normalized to "/".
- * Returns the length of the output.
+ * Returns failure (non-zero) if a ".." component appears as first path
+ * component anytime during the normalization. Otherwise, returns success (0).
  *
  * Note that this function is purely textual.  It does not follow symlinks,
  * verify the existence of the path, or make any system calls.
  */
-int normalize_absolute_path(char *buf, const char *path)
+int normalize_path_copy(char *dst, const char *src)
 {
-       const char *comp_start = path, *comp_end = path;
-       char *dst = buf;
-       int comp_len;
-       assert(buf);
-       assert(path);
-
-       while (*comp_start) {
-               assert(*comp_start == '/');
-               while (*++comp_end && *comp_end != '/')
-                       ; /* nothing */
-               comp_len = comp_end - comp_start;
-
-               if (!strncmp("/",  comp_start, comp_len) ||
-                   !strncmp("/.", comp_start, comp_len))
-                       goto next;
-
-               if (!strncmp("/..", comp_start, comp_len)) {
-                       while (dst > buf && *--dst != '/')
-                               ; /* nothing */
-                       goto next;
-               }
+       char *dst0;
 
-               memmove(dst, comp_start, comp_len);
-               dst += comp_len;
-       next:
-               comp_start = comp_end;
+       if (has_dos_drive_prefix(src)) {
+               *dst++ = *src++;
+               *dst++ = *src++;
        }
+       dst0 = dst;
 
-       if (dst == buf)
+       if (is_dir_sep(*src)) {
                *dst++ = '/';
+               while (is_dir_sep(*src))
+                       src++;
+       }
+
+       for (;;) {
+               char c = *src;
+
+               /*
+                * A path component that begins with . could be
+                * special:
+                * (1) "." and ends   -- ignore and terminate.
+                * (2) "./"           -- ignore them, eat slash and continue.
+                * (3) ".." and ends  -- strip one and terminate.
+                * (4) "../"          -- strip one, eat slash and continue.
+                */
+               if (c == '.') {
+                       if (!src[1]) {
+                               /* (1) */
+                               src++;
+                       } else if (is_dir_sep(src[1])) {
+                               /* (2) */
+                               src += 2;
+                               while (is_dir_sep(*src))
+                                       src++;
+                               continue;
+                       } else if (src[1] == '.') {
+                               if (!src[2]) {
+                                       /* (3) */
+                                       src += 2;
+                                       goto up_one;
+                               } else if (is_dir_sep(src[2])) {
+                                       /* (4) */
+                                       src += 3;
+                                       while (is_dir_sep(*src))
+                                               src++;
+                                       goto up_one;
+                               }
+                       }
+               }
 
+               /* copy up to the next '/', and eat all '/' */
+               while ((c = *src++) != '\0' && !is_dir_sep(c))
+                       *dst++ = c;
+               if (is_dir_sep(c)) {
+                       *dst++ = '/';
+                       while (is_dir_sep(c))
+                               c = *src++;
+                       src--;
+               } else if (!c)
+                       break;
+               continue;
+
+       up_one:
+               /*
+                * dst0..dst is prefix portion, and dst[-1] is '/';
+                * go up one level.
+                */
+               dst--;  /* go to trailing '/' */
+               if (dst <= dst0)
+                       return -1;
+               /* Windows: dst[-1] cannot be backslash anymore */
+               while (dst0 < dst && dst[-1] != '/')
+                       dst--;
+       }
        *dst = '\0';
-       return dst - buf;
+       return 0;
 }
 
 /*
@@ -438,15 +479,16 @@ int longest_ancestor_length(const char *path, const char *prefix_list)
                return -1;
 
        for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
-               for (colon = ceil; *colon && *colon != ':'; colon++);
+               for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
                len = colon - ceil;
                if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
                        continue;
                strlcpy(buf, ceil, len+1);
-               len = normalize_absolute_path(buf, buf);
-               /* Strip "trailing slashes" from "/". */
-               if (len == 1)
-                       len = 0;
+               if (normalize_path_copy(buf, buf) < 0)
+                       continue;
+               len = strlen(buf);
+               if (len > 0 && buf[len-1] == '/')
+                       buf[--len] = '\0';
 
                if (!strncmp(path, buf, len) &&
                    path[len] == '/' &&
index 8d4dbc9fbbcffb73e8669ac08a1292ad2a0f0919..6cd91491d348b9aabea902c0ea60465074e9c06e 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -6,7 +6,6 @@
 #include "string-list.h"
 #include "mailmap.h"
 #include "log-tree.h"
-#include "notes.h"
 #include "color.h"
 
 static char *user_format;
@@ -211,15 +210,13 @@ static void add_merge_info(enum cmit_fmt fmt, struct strbuf *sb,
        while (parent) {
                struct commit *p = parent->item;
                const char *hex = NULL;
-               const char *dots;
                if (abbrev)
                        hex = find_unique_abbrev(p->object.sha1, abbrev);
                if (!hex)
                        hex = sha1_to_hex(p->object.sha1);
-               dots = (abbrev && strlen(hex) != 40) ?  "..." : "";
                parent = parent->next;
 
-               strbuf_addf(sb, " %s%s", hex, dots);
+               strbuf_addf(sb, " %s", hex);
        }
        strbuf_addch(sb, '\n');
 }
@@ -306,23 +303,14 @@ static char *logmsg_reencode(const struct commit *commit,
        return out;
 }
 
-static int mailmap_name(struct strbuf *sb, const char *email)
+static int mailmap_name(char *email, int email_len, char *name, int name_len)
 {
        static struct string_list *mail_map;
-       char buffer[1024];
-
        if (!mail_map) {
                mail_map = xcalloc(1, sizeof(*mail_map));
-               read_mailmap(mail_map, ".mailmap", NULL);
+               read_mailmap(mail_map, NULL);
        }
-
-       if (!mail_map->nr)
-               return -1;
-
-       if (!map_email(mail_map, email, buffer, sizeof(buffer)))
-               return -1;
-       strbuf_addstr(sb, buffer);
-       return 0;
+       return mail_map->nr && map_user(mail_map, email, email_len, name, name_len);
 }
 
 static size_t format_person_part(struct strbuf *sb, char part,
@@ -333,6 +321,9 @@ static size_t format_person_part(struct strbuf *sb, char part,
        int start, end, tz = 0;
        unsigned long date = 0;
        char *ep;
+       const char *name_start, *name_end, *mail_start, *mail_end, *msg_end = msg+len;
+       char person_name[1024];
+       char person_mail[1024];
 
        /* advance 'end' to point to email start delimiter */
        for (end = 0; end < len && msg[end] != '<'; end++)
@@ -346,25 +337,34 @@ static size_t format_person_part(struct strbuf *sb, char part,
        if (end >= len - 2)
                goto skip;
 
+       /* Seek for both name and email part */
+       name_start = msg;
+       name_end = msg+end;
+       while (name_end > name_start && isspace(*(name_end-1)))
+               name_end--;
+       mail_start = msg+end+1;
+       mail_end = mail_start;
+       while (mail_end < msg_end && *mail_end != '>')
+               mail_end++;
+       if (mail_end == msg_end)
+               goto skip;
+       end = mail_end-msg;
+
+       if (part == 'N' || part == 'E') { /* mailmap lookup */
+               strlcpy(person_name, name_start, name_end-name_start+1);
+               strlcpy(person_mail, mail_start, mail_end-mail_start+1);
+               mailmap_name(person_mail, sizeof(person_mail), person_name, sizeof(person_name));
+               name_start = person_name;
+               name_end = name_start + strlen(person_name);
+               mail_start = person_mail;
+               mail_end = mail_start +  strlen(person_mail);
+       }
        if (part == 'n' || part == 'N') {       /* name */
-               while (end > 0 && isspace(msg[end - 1]))
-                       end--;
-               if (part != 'N' || !msg[end] || !msg[end + 1] ||
-                   mailmap_name(sb, msg + end + 2) < 0)
-                       strbuf_add(sb, msg, end);
+               strbuf_add(sb, name_start, name_end-name_start);
                return placeholder_len;
        }
-       start = ++end; /* save email start position */
-
-       /* advance 'end' to point to email end delimiter */
-       for ( ; end < len && msg[end] != '>'; end++)
-               ; /* do nothing */
-
-       if (end >= len)
-               goto skip;
-
-       if (part == 'e') {      /* email */
-               strbuf_add(sb, msg + start, end - start);
+       if (part == 'e' || part == 'E') {       /* email */
+               strbuf_add(sb, mail_start, mail_end-mail_start);
                return placeholder_len;
        }
 
@@ -921,9 +921,5 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
         */
        if (fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
-
-       if (fmt != CMIT_FMT_ONELINE)
-               get_commit_notes(commit, sb, encoding);
-
        free(reencoded);
 }
diff --git a/refs.c b/refs.c
index 024211d72b5e21e1c87e98b0f7e3a6982525d82d..6eb5f5384611bb5d159d892a1bfd120d72e54b9b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -275,10 +275,8 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                                list = get_ref_dir(ref, list);
                                continue;
                        }
-                       if (!resolve_ref(ref, sha1, 1, &flag)) {
-                               error("%s points nowhere!", ref);
-                               continue;
-                       }
+                       if (!resolve_ref(ref, sha1, 1, &flag))
+                               hashclr(sha1);
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
@@ -287,6 +285,35 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
        return sort_ref_list(list);
 }
 
+struct warn_if_dangling_data {
+       const char *refname;
+       const char *msg_fmt;
+};
+
+static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
+                                  int flags, void *cb_data)
+{
+       struct warn_if_dangling_data *d = cb_data;
+       const char *resolves_to;
+       unsigned char junk[20];
+
+       if (!(flags & REF_ISSYMREF))
+               return 0;
+
+       resolves_to = resolve_ref(refname, junk, 0, NULL);
+       if (!resolves_to || strcmp(resolves_to, d->refname))
+               return 0;
+
+       printf(d->msg_fmt, refname);
+       return 0;
+}
+
+void warn_dangling_symref(const char *msg_fmt, const char *refname)
+{
+       struct warn_if_dangling_data data = { refname, msg_fmt };
+       for_each_rawref(warn_if_dangling_symref, &data);
+}
+
 static struct ref_list *get_loose_refs(void)
 {
        if (!cached_refs.did_loose) {
@@ -498,16 +525,19 @@ int read_ref(const char *ref, unsigned char *sha1)
        return -1;
 }
 
+#define DO_FOR_EACH_INCLUDE_BROKEN 01
 static int do_one_ref(const char *base, each_ref_fn fn, int trim,
-                     void *cb_data, struct ref_list *entry)
+                     int flags, void *cb_data, struct ref_list *entry)
 {
        if (strncmp(base, entry->name, trim))
                return 0;
-       if (is_null_sha1(entry->sha1))
-               return 0;
-       if (!has_sha1_file(entry->sha1)) {
-               error("%s does not point to a valid object!", entry->name);
-               return 0;
+       if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
+               if (is_null_sha1(entry->sha1))
+                       return 0;
+               if (!has_sha1_file(entry->sha1)) {
+                       error("%s does not point to a valid object!", entry->name);
+                       return 0;
+               }
        }
        current_ref = entry;
        return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
@@ -561,7 +591,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
 }
 
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
-                          void *cb_data)
+                          int flags, void *cb_data)
 {
        int retval = 0;
        struct ref_list *packed = get_packed_refs();
@@ -570,7 +600,7 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
        struct ref_list *extra;
 
        for (extra = extra_refs; extra; extra = extra->next)
-               retval = do_one_ref(base, fn, trim, cb_data, extra);
+               retval = do_one_ref(base, fn, trim, flags, cb_data, extra);
 
        while (packed && loose) {
                struct ref_list *entry;
@@ -586,13 +616,13 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
                        entry = packed;
                        packed = packed->next;
                }
-               retval = do_one_ref(base, fn, trim, cb_data, entry);
+               retval = do_one_ref(base, fn, trim, flags, cb_data, entry);
                if (retval)
                        goto end_each;
        }
 
        for (packed = packed ? packed : loose; packed; packed = packed->next) {
-               retval = do_one_ref(base, fn, trim, cb_data, packed);
+               retval = do_one_ref(base, fn, trim, flags, cb_data, packed);
                if (retval)
                        goto end_each;
        }
@@ -614,22 +644,28 @@ int head_ref(each_ref_fn fn, void *cb_data)
 
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0, cb_data);
+       return do_for_each_ref("refs/", fn, 0, 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/tags/", fn, 10, cb_data);
+       return do_for_each_ref("refs/tags/", fn, 10, 0, cb_data);
 }
 
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/heads/", fn, 11, cb_data);
+       return do_for_each_ref("refs/heads/", fn, 11, 0, cb_data);
 }
 
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/remotes/", fn, 13, cb_data);
+       return do_for_each_ref("refs/remotes/", fn, 13, 0, cb_data);
+}
+
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref("refs/", fn, 0,
+                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
 /*
diff --git a/refs.h b/refs.h
index 3bb529d3870012feda9a0398cba15c09aee10cf9..29bdcecd4edb5e7281a4da36a06aa05e025f38a7 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,11 @@ extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 
+/* can be used to learn about broken ref and symref */
+extern int for_each_rawref(each_ref_fn, void *);
+
+extern void warn_dangling_symref(const char *msg_fmt, const char *refname);
+
 /*
  * Extra refs will be listed by for_each_ref() before any actual refs
  * for the duration of this process or until clear_extra_refs() is
index 718fb526dd1fbbc5de18784586d37d6069807b77..3518207c178904b91ce28f8d3bf2ba0ee560d0e7 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -290,7 +290,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
                        hex = xstrdup(sha1_to_hex(sha1));
                        string_list_insert(path, rr)->util = hex;
                        if (mkdir(git_path("rr-cache/%s", hex), 0755))
-                               continue;;
+                               continue;
                        handle_file(path, NULL, rr_path(hex, "preimage"));
                        fprintf(stderr, "Recorded preimage for '%s'\n", path);
                }
index 8603c145817488a4ce80111051129d4d715c597b..286e416b757fa8df731330992fca96773082f75d 100644 (file)
@@ -1738,14 +1738,16 @@ static struct commit *get_revision_1(struct rev_info *revs)
                            (commit->date < revs->max_age))
                                continue;
                        if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0)
-                               return NULL;
+                               die("Failed to traverse parents of commit %s",
+                                   sha1_to_hex(commit->object.sha1));
                }
 
                switch (simplify_commit(revs, commit)) {
                case commit_ignore:
                        continue;
                case commit_error:
-                       return NULL;
+                       die("Failed to simplify parents of commit %s",
+                           sha1_to_hex(commit->object.sha1));
                default:
                        return commit;
                }
diff --git a/setup.c b/setup.c
index dfda532adc16f5e6d25d7cfc5add3e0e2b6a5209..6c2deda18492acb5a8597563d6843f9d0dd232c0 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -4,92 +4,6 @@
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
 
-static int sanitary_path_copy(char *dst, const char *src)
-{
-       char *dst0;
-
-       if (has_dos_drive_prefix(src)) {
-               *dst++ = *src++;
-               *dst++ = *src++;
-       }
-       dst0 = dst;
-
-       if (is_dir_sep(*src)) {
-               *dst++ = '/';
-               while (is_dir_sep(*src))
-                       src++;
-       }
-
-       for (;;) {
-               char c = *src;
-
-               /*
-                * A path component that begins with . could be
-                * special:
-                * (1) "." and ends   -- ignore and terminate.
-                * (2) "./"           -- ignore them, eat slash and continue.
-                * (3) ".." and ends  -- strip one and terminate.
-                * (4) "../"          -- strip one, eat slash and continue.
-                */
-               if (c == '.') {
-                       if (!src[1]) {
-                               /* (1) */
-                               src++;
-                       } else if (is_dir_sep(src[1])) {
-                               /* (2) */
-                               src += 2;
-                               while (is_dir_sep(*src))
-                                       src++;
-                               continue;
-                       } else if (src[1] == '.') {
-                               if (!src[2]) {
-                                       /* (3) */
-                                       src += 2;
-                                       goto up_one;
-                               } else if (is_dir_sep(src[2])) {
-                                       /* (4) */
-                                       src += 3;
-                                       while (is_dir_sep(*src))
-                                               src++;
-                                       goto up_one;
-                               }
-                       }
-               }
-
-               /* copy up to the next '/', and eat all '/' */
-               while ((c = *src++) != '\0' && !is_dir_sep(c))
-                       *dst++ = c;
-               if (is_dir_sep(c)) {
-                       *dst++ = '/';
-                       while (is_dir_sep(c))
-                               c = *src++;
-                       src--;
-               } else if (!c)
-                       break;
-               continue;
-
-       up_one:
-               /*
-                * dst0..dst is prefix portion, and dst[-1] is '/';
-                * go up one level.
-                */
-               dst -= 2; /* go past trailing '/' if any */
-               if (dst < dst0)
-                       return -1;
-               while (1) {
-                       if (dst <= dst0)
-                               break;
-                       c = *dst--;
-                       if (c == '/') { /* MinGW: cannot be '\\' anymore */
-                               dst += 2;
-                               break;
-                       }
-               }
-       }
-       *dst = '\0';
-       return 0;
-}
-
 const char *prefix_path(const char *prefix, int len, const char *path)
 {
        const char *orig = path;
@@ -101,7 +15,7 @@ const char *prefix_path(const char *prefix, int len, const char *path)
                        memcpy(sanitized, prefix, len);
                strcpy(sanitized + len, path);
        }
-       if (sanitary_path_copy(sanitized, sanitized))
+       if (normalize_path_copy(sanitized, sanitized))
                goto error_out;
        if (is_absolute_path(orig)) {
                const char *work_tree = get_git_work_tree();
index 8868b800cbb9259f97b8acac3c8701153118b657..5b6e0f61faa9d0ab93507e59b1e4704df97d9b79 100644 (file)
@@ -689,6 +689,7 @@ void free_pack_by_name(const char *pack_name)
        while (*pp) {
                p = *pp;
                if (strcmp(pack_name, p->pack_name) == 0) {
+                       clear_delta_base_cache();
                        close_pack_windows(p);
                        if (p->pack_fd != -1)
                                close(p->pack_fd);
@@ -1663,6 +1664,13 @@ static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
        }
 }
 
+void clear_delta_base_cache(void)
+{
+       unsigned long p;
+       for (p = 0; p < MAX_DELTA_CACHE; p++)
+               release_delta_base_cache(&delta_base_cache[p]);
+}
+
 static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
        void *base, unsigned long base_size, enum object_type type)
 {
index 5d0ac0263d04d7ec72a3b7dec4aaf47aec80da5e..2f75179f4c6c1d05bdd7594b23dcf77007c26751 100644 (file)
@@ -268,16 +268,19 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
                char fullref[PATH_MAX];
                unsigned char sha1_from_ref[20];
                unsigned char *this_result;
+               int flag;
 
                this_result = refs_found ? sha1_from_ref : sha1;
                mksnpath(fullref, sizeof(fullref), *p, len, str);
-               r = resolve_ref(fullref, this_result, 1, NULL);
+               r = resolve_ref(fullref, this_result, 1, &flag);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
                        if (!warn_ambiguous_refs)
                                break;
-               }
+               } else if ((flag & REF_ISSYMREF) &&
+                          (len != 4 || strcmp(str, "HEAD")))
+                       warning("ignoring dangling symref %s.", fullref);
        }
        free(last_branch);
        return refs_found;
index ddd83c8c76112cecd5d23668aaca467601855a72..15e14cf47a586613a62970b393c269728ffbef93 100644 (file)
@@ -26,10 +26,10 @@ static int get_entry_index(const struct string_list *list, const char *string,
 }
 
 /* returns -1-index if already exists */
-static int add_entry(struct string_list *list, const char *string)
+static int add_entry(int insert_at, struct string_list *list, const char *string)
 {
-       int exact_match;
-       int index = get_entry_index(list, string, &exact_match);
+       int exact_match = 0;
+       int index = insert_at != -1 ? insert_at : get_entry_index(list, string, &exact_match);
 
        if (exact_match)
                return -1 - index;
@@ -53,7 +53,13 @@ static int add_entry(struct string_list *list, const char *string)
 
 struct string_list_item *string_list_insert(const char *string, struct string_list *list)
 {
-       int index = add_entry(list, string);
+       return string_list_insert_at_index(-1, string, list);
+}
+
+struct string_list_item *string_list_insert_at_index(int insert_at,
+                                                    const char *string, struct string_list *list)
+{
+       int index = add_entry(insert_at, list, string);
 
        if (index < 0)
                index = -1 - index;
@@ -68,6 +74,16 @@ int string_list_has_string(const struct string_list *list, const char *string)
        return exact_match;
 }
 
+int string_list_find_insert_index(const struct string_list *list, const char *string,
+                                 int negative_existing_index)
+{
+       int exact_match;
+       int index = get_entry_index(list, string, &exact_match);
+       if (exact_match)
+               index = -1 - (negative_existing_index ? index : 0);
+       return index;
+}
+
 struct string_list_item *string_list_lookup(const char *string, struct string_list *list)
 {
        int exact_match, i = get_entry_index(list, string, &exact_match);
@@ -94,6 +110,25 @@ void string_list_clear(struct string_list *list, int free_util)
        list->nr = list->alloc = 0;
 }
 
+void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc)
+{
+       if (list->items) {
+               int i;
+               if (clearfunc) {
+                       for (i = 0; i < list->nr; i++)
+                               clearfunc(list->items[i].util, list->items[i].string);
+               }
+               if (list->strdup_strings) {
+                       for (i = 0; i < list->nr; i++)
+                               free(list->items[i].string);
+               }
+               free(list->items);
+       }
+       list->items = NULL;
+       list->nr = list->alloc = 0;
+}
+
+
 void print_string_list(const char *text, const struct string_list *p)
 {
        int i;
index 4d6a7051fe5bccf04a0d0c32a90e5cf9c00dba3c..d32ba05202880733dd76f5465a0ae16753d1fba6 100644 (file)
@@ -15,9 +15,18 @@ struct string_list
 void print_string_list(const char *text, const struct string_list *p);
 void string_list_clear(struct string_list *list, int free_util);
 
+/* Use this function to call a custom clear function on each util pointer */
+/* The string associated with the util pointer is passed as the second argument */
+typedef void (*string_list_clear_func_t)(void *p, const char *str);
+void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
+
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
+int string_list_find_insert_index(const struct string_list *list, const char *string,
+                                 int negative_existing_index);
 struct string_list_item *string_list_insert(const char *string, struct string_list *list);
+struct string_list_item *string_list_insert_at_index(int insert_at,
+                                                    const char *string, struct string_list *list);
 struct string_list_item *string_list_lookup(const char *string, struct string_list *list);
 
 /* Use these functions only on unsorted lists: */
index 6e7501f352ee97636280357da54c50d73ceb0138..4ed1f0b4dde45e679c04df4b3d833982a0a5b74c 100755 (executable)
@@ -8,36 +8,37 @@ test_description='Test various path utilities'
 . ./test-lib.sh
 
 norm_abs() {
-       test_expect_success "normalize absolute" \
-       "test \$(test-path-utils normalize_absolute_path '$1') = '$2'"
+       test_expect_success "normalize absolute: $1 => $2" \
+       "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
 }
 
 ancestor() {
-       test_expect_success "longest ancestor" \
-       "test \$(test-path-utils longest_ancestor_length '$1' '$2') = '$3'"
+       test_expect_success "longest ancestor: $1 $2 => $3" \
+       "test \"\$(test-path-utils longest_ancestor_length '$1' '$2')\" = '$3'"
 }
 
-norm_abs "" /
+norm_abs "" ""
 norm_abs / /
 norm_abs // /
 norm_abs /// /
 norm_abs /. /
 norm_abs /./ /
-norm_abs /./.. /
-norm_abs /../. /
-norm_abs /./../.// /
+norm_abs /./.. ++failed++
+norm_abs /../. ++failed++
+norm_abs /./../.// ++failed++
 norm_abs /dir/.. /
 norm_abs /dir/sub/../.. /
+norm_abs /dir/sub/../../.. ++failed++
 norm_abs /dir /dir
-norm_abs /dir// /dir
+norm_abs /dir// /dir/
 norm_abs /./dir /dir
-norm_abs /dir/. /dir
-norm_abs /dir///./ /dir
-norm_abs /dir//sub/.. /dir
-norm_abs /dir/sub/../ /dir
-norm_abs //dir/sub/../. /dir
-norm_abs /dir/s1/../s2/ /dir/s2
-norm_abs /d1/s1///s2/..//../s3/ /d1/s3
+norm_abs /dir/. /dir/
+norm_abs /dir///./ /dir/
+norm_abs /dir//sub/.. /dir/
+norm_abs /dir/sub/../ /dir/
+norm_abs //dir/sub/../. /dir/
+norm_abs /dir/s1/../s2/ /dir/s2/
+norm_abs /d1/s1///s2/..//../s3/ /d1/s3/
 norm_abs /d1/s1//../s2/../../d2 /d2
 norm_abs /d1/.../d2 /d1/.../d2
 norm_abs /d1/..././../d2 /d1/d2
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
new file mode 100755 (executable)
index 0000000..315b9b3
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='previous branch syntax @{-n}'
+
+. ./test-lib.sh
+
+test_expect_success 'branch -d @{-1}' '
+       test_commit A &&
+       git checkout -b junk &&
+       git checkout - &&
+       test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+       git branch -d @{-1} &&
+       test_must_fail git rev-parse --verify refs/heads/junk
+'
+
+test_expect_success 'branch -d @{-12} when there is not enough switches yet' '
+       git reflog expire --expire=now &&
+       git checkout -b junk2 &&
+       git checkout - &&
+       test "$(git symbolic-ref HEAD)" = refs/heads/master &&
+       test_must_fail git branch -d @{-12} &&
+       git rev-parse --verify refs/heads/master
+'
+
+test_expect_success 'merge @{-1}' '
+       git checkout A &&
+       test_commit B &&
+       git checkout A &&
+       test_commit C &&
+       git branch -f master B &&
+       git branch -f other &&
+       git checkout other &&
+       git checkout master &&
+       git merge @{-1} &&
+       git cat-file commit HEAD | grep "Merge branch '\''other'\''"
+'
+
+test_expect_success 'merge @{-1} when there is not enough switches yet' '
+       git reflog expire --expire=now &&
+       git checkout -f master &&
+       git reset --hard B &&
+       git branch -f other C &&
+       git checkout other &&
+       git checkout master &&
+       test_must_fail git merge @{-12}
+'
+
+test_done
+
index 569f34177d972e01f9ea567c4d65d70935e25052..7fa5f5b22a28f108b3063ff9920cffb530d950e6 100755 (executable)
@@ -27,11 +27,6 @@ test_expect_success 'symbolic-ref refuses non-ref for HEAD' '
 '
 reset_to_sane
 
-test_expect_success 'symbolic-ref refuses non-branch for HEAD' '
-       test_must_fail git symbolic-ref HEAD refs/foo
-'
-reset_to_sane
-
 test_expect_success 'symbolic-ref refuses bare sha1' '
        echo content >file && git add file && git commit -m one
        test_must_fail git symbolic-ref HEAD `git rev-parse HEAD`
index 85da4caa7ed1b8bcaca7b21e218f2d1839d2db82..48ee07779d64147c3eb8325a3b3f9579f8ec41d8 100755 (executable)
@@ -26,21 +26,28 @@ test_rev_parse() {
        "test '$1' = \"\$(git rev-parse --show-prefix)\""
        shift
        [ $# -eq 0 ] && return
+
+       test_expect_success "$name: git-dir" \
+       "test '$1' = \"\$(git rev-parse --git-dir)\""
+       shift
+       [ $# -eq 0 ] && return
 }
 
-# label is-bare is-inside-git is-inside-work prefix
+# label is-bare is-inside-git is-inside-work prefix git-dir
+
+ROOT=$(pwd)
 
-test_rev_parse toplevel false false true ''
+test_rev_parse toplevel false false true '' .git
 
 cd .git || exit 1
-test_rev_parse .git/ false true false ''
+test_rev_parse .git/ false true false '' .
 cd objects || exit 1
-test_rev_parse .git/objects/ false true false ''
+test_rev_parse .git/objects/ false true false '' "$ROOT/.git"
 cd ../.. || exit 1
 
 mkdir -p sub/dir || exit 1
 cd sub/dir || exit 1
-test_rev_parse subdirectory false false true sub/dir/
+test_rev_parse subdirectory false false true sub/dir/ "$ROOT/.git"
 cd ../.. || exit 1
 
 git config core.bare true
index 27dc6c55d5f50a7fd30388b60230482bad6be2d8..f6a6f839a18de4c3775ea965f164d0d20f2bbe9b 100755 (executable)
@@ -92,13 +92,6 @@ cd sub/dir || exit 1
 test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
 cd ../../../.. || exit 1
 
-test_expect_success 'detecting gitdir when cwd is in a subdir of gitdir' '
-       (expected=$(pwd)/repo.git &&
-        cd repo.git/refs &&
-        unset GIT_DIR &&
-        test "$expected" = "$(git rev-parse --git-dir)")
-'
-
 test_expect_success 'repo finds its work tree' '
        (cd repo.git &&
         : > work/sub/dir/untracked &&
index 91b704a3a4ce6771071d19bd84aa228856fe6875..e377d48902cd5fd539a0ce4cb1fb32ceb8732632 100755 (executable)
@@ -93,13 +93,13 @@ GIT_CEILING_DIRECTORIES="$TRASH_ROOT/subdi"
 test_prefix subdir_ceil_at_subdi_slash "sub/dir/"
 
 
-GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub"
+GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub"
 test_fail second_of_two
 
-GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:bar"
+GIT_CEILING_DIRECTORIES="$TRASH_ROOT/sub:/bar"
 test_fail first_of_two
 
-GIT_CEILING_DIRECTORIES="foo:$TRASH_ROOT/sub:bar"
+GIT_CEILING_DIRECTORIES="/foo:$TRASH_ROOT/sub:/bar"
 test_fail second_of_three
 
 
diff --git a/t/t3301-notes.sh b/t/t3301-notes.sh
deleted file mode 100755 (executable)
index 9393a25..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2007 Johannes E. Schindelin
-#
-
-test_description='Test commit notes'
-
-. ./test-lib.sh
-
-cat > fake_editor.sh << \EOF
-echo "$MSG" > "$1"
-echo "$MSG" >& 2
-EOF
-chmod a+x fake_editor.sh
-VISUAL=./fake_editor.sh
-export VISUAL
-
-test_expect_success 'cannot annotate non-existing HEAD' '
-       ! MSG=3 git notes edit
-'
-
-test_expect_success setup '
-       : > a1 &&
-       git add a1 &&
-       test_tick &&
-       git commit -m 1st &&
-       : > a2 &&
-       git add a2 &&
-       test_tick &&
-       git commit -m 2nd
-'
-
-test_expect_success 'need valid notes ref' '
-       ! MSG=1 GIT_NOTES_REF='/' git notes edit &&
-       ! MSG=2 GIT_NOTES_REF='/' git notes show
-'
-
-test_expect_success 'create notes' '
-       git config core.notesRef refs/notes/commits &&
-       MSG=b1 git notes edit &&
-       test ! -f .git/new-notes &&
-       test 1 = $(git ls-tree refs/notes/commits | wc -l) &&
-       test b1 = $(git notes show) &&
-       git show HEAD^ &&
-       ! git notes show HEAD^
-'
-
-cat > expect << EOF
-commit 268048bfb8a1fb38e703baceb8ab235421bf80c5
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:14:13 2005 -0700
-
-    2nd
-
-Notes:
-    b1
-EOF
-
-test_expect_success 'show notes' '
-       ! (git cat-file commit HEAD | grep b1) &&
-       git log -1 > output &&
-       test_cmp expect output
-'
-test_expect_success 'create multi-line notes (setup)' '
-       : > a3 &&
-       git add a3 &&
-       test_tick &&
-       git commit -m 3rd &&
-       MSG="b3
-c3c3c3c3
-d3d3d3" git notes edit
-'
-
-cat > expect-multiline << EOF
-commit 1584215f1d29c65e99c6c6848626553fdd07fd75
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:15:13 2005 -0700
-
-    3rd
-
-Notes:
-    b3
-    c3c3c3c3
-    d3d3d3
-EOF
-
-printf "\n" >> expect-multiline
-cat expect >> expect-multiline
-
-test_expect_success 'show multi-line notes' '
-       git log -2 > output &&
-       test_cmp expect-multiline output
-'
-
-test_done
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
deleted file mode 100755 (executable)
index 00d27bf..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2007 Johannes E. Schindelin
-#
-
-test_description='Test commit notes index (expensive!)'
-
-. ./test-lib.sh
-
-test -z "$GIT_NOTES_TIMING_TESTS" && {
-       say Skipping timing tests
-       test_done
-       exit
-}
-
-create_repo () {
-       number_of_commits=$1
-       nr=0
-       parent=
-       test -d .git || {
-       git init &&
-       tree=$(git write-tree) &&
-       while [ $nr -lt $number_of_commits ]; do
-               test_tick &&
-               commit=$(echo $nr | git commit-tree $tree $parent) ||
-                       return
-               parent="-p $commit"
-               nr=$(($nr+1))
-       done &&
-       git update-ref refs/heads/master $commit &&
-       {
-               export GIT_INDEX_FILE=.git/temp;
-               git rev-list HEAD | cat -n | sed "s/^[  ][      ]*/ /g" |
-               while read nr sha1; do
-                       blob=$(echo note $nr | git hash-object -w --stdin) &&
-                       echo $sha1 | sed "s/^/0644 $blob 0      /"
-               done | git update-index --index-info &&
-               tree=$(git write-tree) &&
-               test_tick &&
-               commit=$(echo notes | git commit-tree $tree) &&
-               git update-ref refs/notes/commits $commit
-       } &&
-       git config core.notesRef refs/notes/commits
-       }
-}
-
-test_notes () {
-       count=$1 &&
-       git config core.notesRef refs/notes/commits &&
-       git log | grep "^    " > output &&
-       i=1 &&
-       while [ $i -le $count ]; do
-               echo "    $(($count-$i))" &&
-               echo "    note $i" &&
-               i=$(($i+1));
-       done > expect &&
-       git diff expect output
-}
-
-cat > time_notes << \EOF
-       mode=$1
-       i=1
-       while [ $i -lt $2 ]; do
-               case $1 in
-               no-notes)
-                       export GIT_NOTES_REF=non-existing
-               ;;
-               notes)
-                       unset GIT_NOTES_REF
-               ;;
-               esac
-               git log >/dev/null
-               i=$(($i+1))
-       done
-EOF
-
-time_notes () {
-       for mode in no-notes notes
-       do
-               echo $mode
-               /usr/bin/time sh ../time_notes $mode $1
-       done
-}
-
-for count in 10 100 1000 10000; do
-
-       mkdir $count
-       (cd $count;
-
-       test_expect_success "setup $count" "create_repo $count"
-
-       test_expect_success 'notes work' "test_notes $count"
-
-       test_expect_success 'notes timing' "time_notes 100"
-       )
-done
-
-test_done
index b7a670ef401429a50eb97e5f874c9e2c3897fd7d..8c0c5f59827544b47cc7a75f0c3f39cfcecf0a7e 100755 (executable)
@@ -14,7 +14,8 @@ export GIT_AUTHOR_EMAIL
 
 test_expect_success \
     'prepare repository with topic branches' \
-    'echo First > A &&
+    'git config core.logAllRefUpdates true &&
+     echo First > A &&
      git update-index --add A &&
      git commit -m "Add A." &&
      git checkout -b my-topic-branch &&
@@ -84,4 +85,14 @@ test_expect_success 'rebase a single mode change' '
      GIT_TRACE=1 git rebase master
 '
 
+test_expect_success 'HEAD was detached during rebase' '
+     test $(git rev-parse HEAD@{1}) != $(git rev-parse modechange@{1})
+'
+
+test_expect_success 'Show verbose error when HEAD could not be detached' '
+     : > B &&
+     test_must_fail git rebase topic 2> output.err > output.out &&
+     grep "Untracked working tree file .B. would be overwritten" output.err
+'
+
 test_done
index 3ceb8e73c5d4e963a2c08eddf41197431ad52178..bd7f5c0f70571d0aa0f9b9c50a343a322831976f 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat --summary master -- dir/
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 43d77761f988b7f43f46a5d8fd85d406ef9caa42..14595a614c362da4515f8d5b783bb0e0f079c72b 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 5187a26816723476b049c44bb3801816e8672cdf..5a4e72765d316b855a8c2beee8f417cd050de323 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat master -- dir/
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index c9640976a8f3bd724004f945b00d71f43f00c8ea..df0aaa9f2ca780ce62951a5667ed9fe3aa005e41 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --root --cc --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index ad050af55ffa6983bc08ef5919dd20229584fd59..c11b5f2c7f3e6846643b7d7e0e214993f4a2cf0d 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --root --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 628c6c03bc6195268c3e7e003478fd042ef36db2..5f0c98f9ce3d9d067cfcb662084b975a19c90f03 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --root --patch-with-stat master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 5d4e0f13b59652b170c0b2498319f970d071f774..e62c368dc64dae87c6c168462b175712f197e7af 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --root -c --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 217a2eb203718b91ccd79a04e0369f4c5d2905aa..b42c334439b71cdb391d832d498b90a22aad2656 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --root -p master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index e17ccfc2340eadbb8c393de0aa2793f6f899ba56..e8f46159da1e5ca68524f5657010d06667a9c94b 100644 (file)
@@ -1,6 +1,6 @@
 $ git log --root master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index f8fefef2c3869337ba048d5cd6d95169dc60cf00..bf1326dc36629096fc4e4102375c8a9c550391aa 100644 (file)
@@ -1,6 +1,6 @@
 $ git log -p master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index e9d9e7b40a08d803901ac001e4bf9f0c37ad8b15..a8f6ce5abd642e51672eb3e0ae5ea66a08aa74d2 100644 (file)
@@ -1,6 +1,6 @@
 $ git log master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 9e6e1f27105ca50262e5eb3f2ff132b46c12fe45..fb08ce0e46d16d223269754295cc4a8dc4364268 100644 (file)
@@ -1,6 +1,6 @@
 $ git show master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 5facf2543db0135ceb3aafdc5ffe0395b429f44c..e96ff1fb8c11ff8c6774cb249f45744ed05a97c3 100644 (file)
@@ -1,6 +1,6 @@
 $ git whatchanged --root --cc --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index 10f6767e498b0d5bdadd73067e4ef2dd5f028e89..c0aff68ef68c2d956492e7f9817b9bf5afe58005 100644 (file)
@@ -1,6 +1,6 @@
 $ git whatchanged --root -c --patch-with-stat --summary master
 commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
-Merge: 9a6d494... c7a2ab9...
+Merge: 9a6d494 c7a2ab9
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
index caea292f15437f50fd324496ff511971cc9d1d0c..281680d95afacb291d56d9d7f23efed1f3169e93 100755 (executable)
@@ -128,4 +128,12 @@ test_expect_success 'force diff with "diff"' '
        test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
 '
 
+test_expect_success 'GIT_EXTERNAL_DIFF with more than one changed files' '
+       echo anotherfile > file2 &&
+       git add file2 &&
+       git commit -m "added 2nd file" &&
+       echo modified >file2 &&
+       GIT_EXTERNAL_DIFF=echo git diff
+'
+
 test_done
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
new file mode 100755 (executable)
index 0000000..9a7d1b4
--- /dev/null
@@ -0,0 +1,215 @@
+#!/bin/sh
+
+test_description='.mailmap configurations'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       echo one >one &&
+       git add one &&
+       test_tick &&
+       git commit -m initial &&
+       echo two >>one &&
+       git add one &&
+       git commit --author "nick1 <bugs@company.xx>" -m second
+'
+
+cat >expect <<\EOF
+A U Thor (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+
+test_expect_success 'No mailmap' '
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Repo Guy (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+
+test_expect_success 'default .mailmap' '
+       echo "Repo Guy <author@example.com>" > .mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+# Using a mailmap file in a subdirectory of the repo here, but
+# could just as well have been a file outside of the repository
+cat >expect <<\EOF
+Internal Guy (1):
+      second
+
+Repo Guy (1):
+      initial
+
+EOF
+test_expect_success 'mailmap.file set' '
+       mkdir internal_mailmap &&
+       echo "Internal Guy <bugs@company.xx>" > internal_mailmap/.mailmap &&
+       git config mailmap.file internal_mailmap/.mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+External Guy (1):
+      initial
+
+Internal Guy (1):
+      second
+
+EOF
+test_expect_success 'mailmap.file override' '
+       echo "External Guy <author@example.com>" >> internal_mailmap/.mailmap &&
+       git config mailmap.file internal_mailmap/.mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+Repo Guy (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+
+test_expect_success 'mailmap.file non-existant' '
+       rm internal_mailmap/.mailmap &&
+       rmdir internal_mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+cat >expect <<\EOF
+A U Thor (1):
+      initial
+
+nick1 (1):
+      second
+
+EOF
+test_expect_success 'No mailmap files, but configured' '
+       rm .mailmap &&
+       git shortlog HEAD >actual &&
+       test_cmp expect actual
+'
+
+# Extended mailmap configurations should give us the following output for shortlog
+cat >expect <<\EOF
+A U Thor <author@example.com> (1):
+      initial
+
+CTO <cto@company.xx> (1):
+      seventh
+
+Other Author <other@author.xx> (2):
+      third
+      fourth
+
+Santa Claus <santa.claus@northpole.xx> (2):
+      fifth
+      sixth
+
+Some Dude <some@dude.xx> (1):
+      second
+
+EOF
+
+test_expect_success 'Shortlog output (complex mapping)' '
+       echo three >>one &&
+       git add one &&
+       test_tick &&
+       git commit --author "nick2 <bugs@company.xx>" -m third &&
+
+       echo four >>one &&
+       git add one &&
+       test_tick &&
+       git commit --author "nick2 <nick2@company.xx>" -m fourth &&
+
+       echo five >>one &&
+       git add one &&
+       test_tick &&
+       git commit --author "santa <me@company.xx>" -m fifth &&
+
+       echo six >>one &&
+       git add one &&
+       test_tick &&
+       git commit --author "claus <me@company.xx>" -m sixth &&
+
+       echo seven >>one &&
+       git add one &&
+       test_tick &&
+       git commit --author "CTO <cto@coompany.xx>" -m seventh &&
+
+       mkdir internal_mailmap &&
+       echo "Committed <committer@example.com>" > internal_mailmap/.mailmap &&
+       echo "<cto@company.xx>                       <cto@coompany.xx>" >> internal_mailmap/.mailmap &&
+       echo "Some Dude <some@dude.xx>         nick1 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+       echo "Other Author <other@author.xx>   nick2 <bugs@company.xx>" >> internal_mailmap/.mailmap &&
+       echo "Other Author <other@author.xx>         <nick2@company.xx>" >> internal_mailmap/.mailmap &&
+       echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+       echo "Santa Claus <santa.claus@northpole.xx> <me@company.xx>" >> internal_mailmap/.mailmap &&
+
+       git shortlog -e HEAD >actual &&
+       test_cmp expect actual
+
+'
+
+# git log with --pretty format which uses the name and email mailmap placemarkers
+cat >expect <<\EOF
+Author CTO <cto@coompany.xx> maps to CTO <cto@company.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author claus <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author santa <me@company.xx> maps to Santa Claus <santa.claus@northpole.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <nick2@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick2 <bugs@company.xx> maps to Other Author <other@author.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author nick1 <bugs@company.xx> maps to Some Dude <some@dude.xx>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+
+Author A U Thor <author@example.com> maps to A U Thor <author@example.com>
+Committer C O Mitter <committer@example.com> maps to Committed <committer@example.com>
+EOF
+
+test_expect_success 'Log output (complex mapping)' '
+       git log --pretty=format:"Author %an <%ae> maps to %aN <%aE>%nCommitter %cn <%ce> maps to %cN <%cE>%n" >actual &&
+       test_cmp expect actual
+'
+
+# git blame
+cat >expect <<\EOF
+^3a2fdcb (A U Thor     2005-04-07 15:13:13 -0700 1) one
+7de6f99b (Some Dude    2005-04-07 15:13:13 -0700 2) two
+5815879d (Other Author 2005-04-07 15:14:13 -0700 3) three
+ff859d96 (Other Author 2005-04-07 15:15:13 -0700 4) four
+5ab6d4fa (Santa Claus  2005-04-07 15:16:13 -0700 5) five
+38a42d8b (Santa Claus  2005-04-07 15:17:13 -0700 6) six
+8ddc0386 (CTO          2005-04-07 15:18:13 -0700 7) seven
+EOF
+
+test_expect_success 'Blame output (complex mapping)' '
+       git blame one >actual &&
+       test_cmp expect actual
+'
+
+test_done
index 771c0a06a4bd01cf40628ffae379b5d992802ef6..55ed7c7935c007573761842cea5978a784c865b3 100755 (executable)
@@ -112,4 +112,42 @@ test_expect_success 'prune: do not prune heads listed as an argument' '
 
 '
 
+test_expect_success 'gc --no-prune' '
+
+       before=$(git count-objects | sed "s/ .*//") &&
+       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+       test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE &&
+       test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+       git config gc.pruneExpire 2.days.ago &&
+       git gc --no-prune &&
+       test 1 = $(git count-objects | sed "s/ .*//") &&
+       test -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc respects gc.pruneExpire' '
+
+       git config gc.pruneExpire 5002.days.ago &&
+       git gc &&
+       test -f $BLOB_FILE &&
+       git config gc.pruneExpire 5000.days.ago &&
+       git gc &&
+       test ! -f $BLOB_FILE
+
+'
+
+test_expect_success 'gc --prune=<date>' '
+
+       BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+       BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+       test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+       git gc --prune=5002.days.ago &&
+       test -f $BLOB_FILE &&
+       git gc --prune=5000.days.ago &&
+       test ! -f $BLOB_FILE
+
+'
+
 test_done
diff --git a/t/t5307-pack-missing-commit.sh b/t/t5307-pack-missing-commit.sh
new file mode 100755 (executable)
index 0000000..ae52a18
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+test_description='pack should notice missing commit objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       for i in 1 2 3 4 5
+       do
+               echo "$i" >"file$i" &&
+               git add "file$i" &&
+               test_tick &&
+               git commit -m "$i" &&
+               git tag "tag$i"
+       done &&
+       obj=$(git rev-parse --verify tag3) &&
+       fanout=$(expr "$obj" : "\(..\)") &&
+       remainder=$(expr "$obj" : "..\(.*\)") &&
+       rm -f ".git/objects/$fanout/$remainder"
+'
+
+test_expect_success 'check corruption' '
+       test_must_fail git fsck
+'
+
+test_expect_success 'rev-list notices corruption (1)' '
+       test_must_fail git rev-list HEAD
+'
+
+test_expect_success 'rev-list notices corruption (2)' '
+       test_must_fail git rev-list --objects HEAD
+'
+
+test_expect_success 'pack-objects notices corruption' '
+       echo HEAD |
+       test_must_fail git pack-objects --revs pack
+'
+
+test_done
index b21317d68527988d0c2939e7173098b08bfbb64f..f2d5581b12f7d70c9f346da75dced81e12bd4c7f 100755 (executable)
@@ -32,9 +32,7 @@ test_expect_success setup '
        done &&
        git update-ref HEAD "$commit" &&
        git clone ./. victim &&
-       cd victim &&
-       git log &&
-       cd .. &&
+       ( cd victim && git log ) &&
        git update-ref HEAD "$zero" &&
        parent=$zero &&
        i=0 &&
@@ -59,88 +57,84 @@ test_expect_success 'pack the source repository' '
 '
 
 test_expect_success 'pack the destination repository' '
+    (
        cd victim &&
        git repack -a -d &&
-       git prune &&
-       cd ..
+       git prune
+    )
 '
 
-test_expect_success \
-        'pushing rewound head should not barf but require --force' '
-       # should not fail but refuse to update.
-       if git send-pack ./victim/.git/ master
-       then
-               # now it should fail with Pasky patch
-               echo >&2 Gaah, it should have failed.
-               false
-       else
-               echo >&2 Thanks, it correctly failed.
-               true
-       fi &&
-       if cmp victim/.git/refs/heads/master .git/refs/heads/master
-       then
-               # should have been left as it was!
-               false
-       else
-               true
-       fi &&
+test_expect_success 'refuse pushing rewound head without --force' '
+       pushed_head=$(git rev-parse --verify master) &&
+       victim_orig=$(cd victim && git rev-parse --verify master) &&
+       test_must_fail git send-pack ./victim master &&
+       victim_head=$(cd victim && git rev-parse --verify master) &&
+       test "$victim_head" = "$victim_orig" &&
        # this should update
-       git send-pack --force ./victim/.git/ master &&
-       cmp victim/.git/refs/heads/master .git/refs/heads/master
+       git send-pack --force ./victim master &&
+       victim_head=$(cd victim && git rev-parse --verify master) &&
+       test "$victim_head" = "$pushed_head"
 '
 
 test_expect_success \
         'push can be used to delete a ref' '
-       cd victim &&
-       git branch extra master &&
-       cd .. &&
-       test -f victim/.git/refs/heads/extra &&
-       git send-pack ./victim/.git/ :extra master &&
-       ! test -f victim/.git/refs/heads/extra
+       ( cd victim && git branch extra master ) &&
+       git send-pack ./victim :extra master &&
+       ( cd victim &&
+         test_must_fail git rev-parse --verify extra )
 '
 
-unset GIT_CONFIG
-HOME=`pwd`/no-such-directory
-export HOME ;# this way we force the victim/.git/config to be used.
-
-test_expect_success \
-       'pushing a delete should be denied with denyDeletes' '
-       cd victim &&
-       git config receive.denyDeletes true &&
-       git branch extra master &&
-       cd .. &&
-       test -f victim/.git/refs/heads/extra &&
-       test_must_fail git send-pack ./victim/.git/ :extra master
+test_expect_success 'refuse deleting push with denyDeletes' '
+       (
+           cd victim &&
+           ( git branch -D extra || : ) &&
+           git config receive.denyDeletes true &&
+           git branch extra master
+       ) &&
+       test_must_fail git send-pack ./victim :extra master
 '
-rm -f victim/.git/refs/heads/extra
 
-test_expect_success \
-        'pushing with --force should be denied with denyNonFastforwards' '
-       cd victim &&
-       git config receive.denyNonFastforwards true &&
-       cd .. &&
-       git update-ref refs/heads/master master^ || return 1
-       git send-pack --force ./victim/.git/ master && return 1
-       ! test_cmp .git/refs/heads/master victim/.git/refs/heads/master
+test_expect_success 'denyNonFastforwards trumps --force' '
+       (
+           cd victim &&
+           ( git branch -D extra || : ) &&
+           git config receive.denyNonFastforwards true
+       ) &&
+       victim_orig=$(cd victim && git rev-parse --verify master) &&
+       test_must_fail git send-pack --force ./victim master^:master &&
+       victim_head=$(cd victim && git rev-parse --verify master) &&
+       test "$victim_orig" = "$victim_head"
 '
 
-test_expect_success \
-       'pushing does not include non-head refs' '
-       mkdir parent && cd parent &&
-       git init && touch file && git add file && git commit -m add &&
-       cd .. &&
-       git clone parent child && cd child && git push --all &&
-       cd ../parent &&
-       git branch -a >branches && ! grep origin/master branches
+test_expect_success 'push --all excludes remote tracking hierarchy' '
+       mkdir parent &&
+       (
+           cd parent &&
+           git init && : >file && git add file && git commit -m add
+       ) &&
+       git clone parent child &&
+       (
+           cd child && git push --all
+       ) &&
+       (
+           cd parent &&
+           test -z "$(git for-each-ref refs/remotes/origin)"
+       )
 '
 
 rewound_push_setup() {
        rm -rf parent child &&
-       mkdir parent && cd parent &&
-       git init && echo one >file && git add file && git commit -m one &&
-       echo two >file && git commit -a -m two &&
-       cd .. &&
-       git clone parent child && cd child && git reset --hard HEAD^
+       mkdir parent &&
+       (
+           cd parent &&
+           git init &&
+           echo one >file && git add file && git commit -m one &&
+           echo two >file && git commit -a -m two
+       ) &&
+       git clone parent child &&
+       (
+           cd child && git reset --hard HEAD^
+       )
 }
 
 rewound_push_succeeded() {
@@ -156,30 +150,57 @@ rewound_push_failed() {
        fi
 }
 
-test_expect_success \
-       'pushing explicit refspecs respects forcing' '
+test_expect_success 'pushing explicit refspecs respects forcing' '
        rewound_push_setup &&
-       if git send-pack ../parent/.git refs/heads/master:refs/heads/master
-       then
-               false
-       else
-               true
-       fi && rewound_push_failed &&
-       git send-pack ../parent/.git +refs/heads/master:refs/heads/master &&
-       rewound_push_succeeded
+       parent_orig=$(cd parent && git rev-parse --verify master) &&
+       (
+           cd child &&
+           test_must_fail git send-pack ../parent \
+               refs/heads/master:refs/heads/master
+       ) &&
+       parent_head=$(cd parent && git rev-parse --verify master) &&
+       test "$parent_orig" = "$parent_head" &&
+       (
+           cd child &&
+           git send-pack ../parent \
+               +refs/heads/master:refs/heads/master
+       ) &&
+       parent_head=$(cd parent && git rev-parse --verify master) &&
+       child_head=$(cd parent && git rev-parse --verify master) &&
+       test "$parent_head" = "$child_head"
 '
 
-test_expect_success \
-       'pushing wildcard refspecs respects forcing' '
+test_expect_success 'pushing wildcard refspecs respects forcing' '
        rewound_push_setup &&
-       if git send-pack ../parent/.git refs/heads/*:refs/heads/*
-       then
-               false
-       else
-               true
-       fi && rewound_push_failed &&
-       git send-pack ../parent/.git +refs/heads/*:refs/heads/* &&
-       rewound_push_succeeded
+       parent_orig=$(cd parent && git rev-parse --verify master) &&
+       (
+           cd child &&
+           test_must_fail git send-pack ../parent \
+               "refs/heads/*:refs/heads/*"
+       ) &&
+       parent_head=$(cd parent && git rev-parse --verify master) &&
+       test "$parent_orig" = "$parent_head" &&
+       (
+           cd child &&
+           git send-pack ../parent \
+               "+refs/heads/*:refs/heads/*"
+       ) &&
+       parent_head=$(cd parent && git rev-parse --verify master) &&
+       child_head=$(cd parent && git rev-parse --verify master) &&
+       test "$parent_head" = "$child_head"
+'
+
+test_expect_success 'warn pushing to delete current branch' '
+       rewound_push_setup &&
+       (
+           cd child &&
+           git send-pack ../parent :refs/heads/master 2>errs
+       ) &&
+       grep "warning: to refuse deleting" child/errs &&
+       (
+               cd parent &&
+               test_must_fail git rev-parse --verify master
+       )
 '
 
 test_done
index bc5b7ce4a6b5fd898d1ea5a9f3e6a148d45f0535..eb637184a00007c61e6d92f7b5546eed6ec5a0ae 100755 (executable)
@@ -402,4 +402,31 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
         test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
 '
 
+test_expect_success 'remote prune to cause a dangling symref' '
+       git clone one seven &&
+       (
+               cd one &&
+               git checkout side2 &&
+               git branch -D master
+       ) &&
+       (
+               cd seven &&
+               git remote prune origin
+       ) 2>err &&
+       grep "has become dangling" err &&
+
+       : And the dangling symref will not cause other annoying errors
+       (
+               cd seven &&
+               git branch -a
+       ) 2>err &&
+       ! grep "points nowhere" err
+       (
+               cd seven &&
+               test_must_fail git branch nomore origin
+       ) 2>err &&
+       grep "dangling symref" err
+'
+
 test_done
+
index c236b5e83beed8997e8f6c3a50f0bb74f73f3f33..11b343274ff36247f45de6eefbdbebce511915be 100755 (executable)
@@ -94,6 +94,13 @@ test_expect_success 'MKCOL sends directory names with trailing slashes' '
 
 '
 
+test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
+
+       grep -P "\"(?:PUT|MOVE) .+objects/[\da-z]{2}/[\da-z]{38}_[\da-z\-]{40} HTTP/[0-9.]+\" 20\d" \
+               < "$HTTPD_ROOT_PATH"/access.log
+
+'
+
 stop_httpd
 
 test_done
index fe287d31fb98237b11ccc1d1209001dec3fb0a8e..44793f2eee0e0c14802ecedfe0637cc6855f51b6 100755 (executable)
@@ -144,4 +144,19 @@ test_expect_success 'clone to an existing path' '
        test_must_fail git clone src target-5
 '
 
+test_expect_success 'clone a void' '
+       mkdir src-0 &&
+       (
+               cd src-0 && git init
+       ) &&
+       git clone src-0 target-6 &&
+       (
+               cd src-0 && test_commit A
+       ) &&
+       git clone src-0 target-7 &&
+       # There is no reason to insist they are bit-for-bit
+       # identical, but this test should suffice for now.
+       test_cmp target-6/.git/config target-7/.git/config
+'
+
 test_done
index 8537bf91606282161ab92b6f2f7367c9a3c016fc..56b5eccdb4a0563b4df22e2af679b08b66ef6ae1 100755 (executable)
@@ -39,13 +39,19 @@ test_expect_success 'result is really identical' '
 '
 
 test_expect_success 'rewrite bare repository identically' '
-       (git config core.bare true && cd .git && git filter-branch branch)
+       (git config core.bare true && cd .git &&
+        git filter-branch branch > filter-output 2>&1 &&
+       ! fgrep fatal filter-output)
 '
 git config core.bare false
 test_expect_success 'result is really identical' '
        test $H = $(git rev-parse HEAD)
 '
 
+test_expect_success 'Fail if commit filter fails' '
+       test_must_fail git filter-branch -f --commit-filter "exit 1" HEAD
+'
+
 test_expect_success 'rewrite, renaming a specific file' '
        git filter-branch -f --tree-filter "mv d doh || :" HEAD
 '
index 2ec7ac6a510c5b83bc1ee6ce428379beb7a8b5ef..b8cb2df6670a18ebf54fecbb97a48d0c07a06e2b 100755 (executable)
@@ -234,4 +234,17 @@ test_expect_success 'gracefully add submodule with a trailing slash' '
 
 '
 
+test_expect_success 'ls-files gracefully handles trailing slash' '
+
+       test "init" = "$(git ls-files init/)"
+
+'
+
+test_expect_success 'submodule <invalid-path> warns' '
+
+       git submodule no-such-submodule 2> output.err &&
+       grep "^error: .*no-such-submodule" output.err
+
+'
+
 test_done
index 704a4f8574eaeaa7256d901b09f36810b37cec57..20529a878c6b33f2eebed393520c90c98f562df4 100755 (executable)
@@ -87,4 +87,14 @@ test_expect_success '"bar" is an empty file' 'test -f x/bar && ! test -s x/bar'
 test_expect_success 'get "bar" => symlink fix from svn' \
                '(cd x && git svn rebase)'
 test_expect_success '"bar" becomes a symlink' 'test -L x/bar'
+
+
+test_expect_success 'clone using git svn' 'git svn clone -r1 "$svnrepo" y'
+test_expect_success 'disable broken symlink workaround' \
+  '(cd y && git config svn.brokenSymlinkWorkaround false)'
+test_expect_success '"bar" is an empty file' 'test -f y/bar && ! test -s y/bar'
+test_expect_success 'get "bar" => symlink fix from svn' \
+               '(cd y && git svn rebase)'
+test_expect_success '"bar" does not become a symlink' '! test -L y/bar'
+
 test_done
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
new file mode 100755 (executable)
index 0000000..03705fa
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+test_description='test moved svn branch with missing empty files'
+
+. ./lib-git-svn.sh
+test_expect_success 'load svn dumpfile'  '
+       svnadmin load "$rawsvnrepo" < "${TEST_DIRECTORY}/t9135/svn.dump"
+       '
+
+test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
+
+test_expect_success 'test that b1 exists and is empty' '
+       (cd x && test -f b1 && ! test -s b1)
+       '
+
+test_done
diff --git a/t/t9135/svn.dump b/t/t9135/svn.dump
new file mode 100644 (file)
index 0000000..b51c0cc
--- /dev/null
@@ -0,0 +1,192 @@
+SVN-fs-dump-format-version: 2
+
+UUID: 1f80e919-e9e3-4d80-a3ae-d9f21095e27b
+
+Revision-number: 0
+Prop-content-length: 56
+Content-length: 56
+
+K 8
+svn:date
+V 27
+2009-02-10T19:23:16.424027Z
+PROPS-END
+
+Revision-number: 1
+Prop-content-length: 123
+Content-length: 123
+
+K 7
+svn:log
+V 20
+init standard layout
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:17.195072Z
+PROPS-END
+
+Node-path: branches
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Node-path: trunk
+Node-kind: dir
+Node-action: add
+Prop-content-length: 10
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 2
+Prop-content-length: 121
+Content-length: 121
+
+K 7
+svn:log
+V 18
+branch-b off trunk
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:19.160095Z
+PROPS-END
+
+Node-path: branches/branch-b
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 1
+Node-copyfrom-path: trunk
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
+Revision-number: 3
+Prop-content-length: 120
+Content-length: 120
+
+K 7
+svn:log
+V 17
+add empty file b1
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:20.194568Z
+PROPS-END
+
+Node-path: branches/branch-b/b1
+Node-kind: file
+Node-action: add
+Prop-content-length: 10
+Text-content-length: 0
+Text-content-md5: d41d8cd98f00b204e9800998ecf8427e
+Content-length: 10
+
+PROPS-END
+
+
+Revision-number: 4
+Prop-content-length: 110
+Content-length: 110
+
+K 7
+svn:log
+V 8
+branch-c
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.169100Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 3
+Node-copyfrom-path: trunk
+
+
+Revision-number: 5
+Prop-content-length: 126
+Content-length: 126
+
+K 7
+svn:log
+V 23
+oops, wrong branchpoint
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.253557Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-action: delete
+
+
+Revision-number: 6
+Prop-content-length: 127
+Content-length: 127
+
+K 7
+svn:log
+V 24
+branch-c off of branch-b
+K 10
+svn:author
+V 8
+john.doe
+K 8
+svn:date
+V 27
+2009-02-10T19:23:21.314659Z
+PROPS-END
+
+Node-path: branches/branch-c
+Node-kind: dir
+Node-action: add
+Node-copyfrom-rev: 5
+Node-copyfrom-path: branches/branch-b
+Prop-content-length: 34
+Content-length: 34
+
+K 13
+svn:mergeinfo
+V 0
+
+PROPS-END
+
+
index 2c0f5a37e8b9051b1db80aa04e9ea763fb8d772b..5168a8e3dfd5150de8def87980118e31e81296e7 100644 (file)
@@ -2,11 +2,13 @@
 
 int main(int argc, char **argv)
 {
-       if (argc == 3 && !strcmp(argv[1], "normalize_absolute_path")) {
+       if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
                char *buf = xmalloc(PATH_MAX + 1);
-               int rv = normalize_absolute_path(buf, argv[2]);
-               assert(strlen(buf) == rv);
+               int rv = normalize_path_copy(buf, argv[2]);
+               if (rv)
+                       buf = "++failed++";
                puts(buf);
+               return 0;
        }
 
        if (argc >= 2 && !strcmp(argv[1], "make_absolute_path")) {
@@ -15,12 +17,16 @@ int main(int argc, char **argv)
                        argc--;
                        argv++;
                }
+               return 0;
        }
 
        if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
                int len = longest_ancestor_length(argv[2], argv[3]);
                printf("%d\n", len);
+               return 0;
        }
 
-       return 0;
+       fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
+               argv[1] ? argv[1] : "(there was none)");
+       return 1;
 }
diff --git a/tree.c b/tree.c
index 03e782a9cabc0a12ed5baec0ef59c99f19dbc843..25d2e29fa8b1dfb964b97a10898c77d8fe86ef78 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -110,7 +110,7 @@ int read_tree_recursive(struct tree *tree,
                case 0:
                        continue;
                case READ_TREE_RECURSIVE:
-                       break;;
+                       break;
                default:
                        return -1;
                }
@@ -131,6 +131,34 @@ int read_tree_recursive(struct tree *tree,
                        if (retval)
                                return -1;
                        continue;
+               } else if (S_ISGITLINK(entry.mode)) {
+                       int retval;
+                       struct strbuf path;
+                       unsigned int entrylen;
+                       struct commit *commit;
+
+                       entrylen = tree_entry_len(entry.path, entry.sha1);
+                       strbuf_init(&path, baselen + entrylen + 1);
+                       strbuf_add(&path, base, baselen);
+                       strbuf_add(&path, entry.path, entrylen);
+                       strbuf_addch(&path, '/');
+
+                       commit = lookup_commit(entry.sha1);
+                       if (!commit)
+                               die("Commit %s in submodule path %s not found",
+                                   sha1_to_hex(entry.sha1), path.buf);
+
+                       if (parse_commit(commit))
+                               die("Invalid commit %s in submodule path %s",
+                                   sha1_to_hex(entry.sha1), path.buf);
+
+                       retval = read_tree_recursive(commit->tree,
+                                                    path.buf, path.len,
+                                                    stage, match, fn, context);
+                       strbuf_release(&path);
+                       if (retval)
+                               return -1;
+                       continue;
                }
        }
        return 0;
index 679adab6a0fccef460acaf90b116b8c6cb9bd460..e57630e983488e5c0222dc47a5e32d6efb184762 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -18,7 +18,7 @@ void walker_say(struct walker *walker, const char *fmt, const char *hex)
 static void report_missing(const struct object *obj)
 {
        char missing_hex[41];
-       strcpy(missing_hex, sha1_to_hex(obj->sha1));;
+       strcpy(missing_hex, sha1_to_hex(obj->sha1));
        fprintf(stderr, "Cannot obtain needed %s %s\n",
                obj->type ? typename(obj->type): "object", missing_hex);
        if (!is_null_sha1(current_commit_sha1))