summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Langhoff <martin@catalyst.net.nz>2006-04-30 22:20:56 (GMT)
committerMartin Langhoff <martin@catalyst.net.nz>2006-04-30 22:20:56 (GMT)
commite660e3997fbad830e5723336d61883f3a50dbc92 (patch)
tree0c02dced98df9982415c8328e8c07c1001e3dc31
parent992793c832acfd98107068d90b886643f0344d04 (diff)
parent66ae0c7702e68765dad17aa05c5d9255d999216c (diff)
downloadgit-e660e3997fbad830e5723336d61883f3a50dbc92.zip
git-e660e3997fbad830e5723336d61883f3a50dbc92.tar.gz
git-e660e3997fbad830e5723336d61883f3a50dbc92.tar.bz2
Merge with git://kernel.org/pub/scm/git/git.git
-rw-r--r--Documentation/Makefile2
-rw-r--r--Documentation/callouts.xsl16
-rw-r--r--Documentation/everyday.txt45
-rw-r--r--Documentation/git-branch.txt57
-rw-r--r--Documentation/git-checkout.txt18
-rw-r--r--Documentation/git-diff.txt38
-rw-r--r--Documentation/git-init-db.txt8
-rw-r--r--Documentation/git-log.txt7
-rw-r--r--Documentation/git-repo-config.txt4
-rw-r--r--Documentation/git-reset.txt72
-rw-r--r--Documentation/git-update-index.txt31
-rw-r--r--Documentation/git-var.txt3
-rw-r--r--Makefile6
-rw-r--r--builtin-help.c1
-rw-r--r--cache.h1
-rw-r--r--commit-tree.c13
-rw-r--r--diff-files.c212
-rw-r--r--diff-index.c251
-rw-r--r--diff-lib.c1963
-rw-r--r--diff.c1805
-rw-r--r--diff.h9
-rwxr-xr-xgit-am.sh7
-rwxr-xr-xgit-annotate.perl10
-rwxr-xr-xgit-branch.sh2
-rwxr-xr-xgit-cvsserver.perl20
-rwxr-xr-xgit-format-patch.sh9
-rwxr-xr-xgit-rebase.sh2
-rw-r--r--git.c1
-rw-r--r--pack-check.c6
-rw-r--r--repo-config.c16
-rw-r--r--rev-parse.c15
-rw-r--r--revision.c8
-rw-r--r--setup.c23
-rwxr-xr-xt/t0000-basic.sh49
-rwxr-xr-xt/t1001-read-tree-m-2way.sh2
-rwxr-xr-xt/t1002-read-tree-m-u-2way.sh2
-rwxr-xr-xt/t4010-diff-pathspec.sh10
37 files changed, 2423 insertions, 2321 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
index f4cbf7e..c1af22c 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -79,7 +79,7 @@ clean:
asciidoc -b xhtml11 -d manpage -f asciidoc.conf $<
%.1 %.7 : %.xml
- xmlto man $<
+ xmlto -m callouts.xsl man $<
%.xml : %.txt
asciidoc -b docbook -d manpage -f asciidoc.conf $<
diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl
new file mode 100644
index 0000000..ad03755
--- /dev/null
+++ b/Documentation/callouts.xsl
@@ -0,0 +1,16 @@
+<!-- callout.xsl: converts asciidoc callouts to man page format -->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:template match="co">
+ <xsl:value-of select="concat('\fB(',substring-after(@id,'-'),')\fR')"/>
+</xsl:template>
+<xsl:template match="calloutlist">
+ <xsl:text>.sp&#10;</xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;</xsl:text>
+</xsl:template>
+<xsl:template match="callout">
+ <xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
+ <xsl:apply-templates/>
+ <xsl:text>.br&#10;</xsl:text>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index 3ab9b91..4b56370 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -61,7 +61,8 @@ $ git prune
$ git count-objects <2>
$ git repack <3>
$ git prune <4>
-
+------------
++
<1> running without "--full" is usually cheap and assures the
repository health reasonably well.
<2> check how many loose objects there are and how much
@@ -69,17 +70,16 @@ diskspace is wasted by not repacking.
<3> without "-a" repacks incrementally. repacking every 4-5MB
of loose objects accumulation may be a good rule of thumb.
<4> after repack, prune removes the duplicate loose objects.
-------------
Repack a small project into single pack.::
+
------------
$ git repack -a -d <1>
$ git prune
-
+------------
++
<1> pack all the objects reachable from the refs into one pack
and remove unneeded other packs
-------------
Individual Developer (Standalone)[[Individual Developer (Standalone)]]
@@ -129,10 +129,10 @@ $ git-init-db
$ git add . <1>
$ git commit -m 'import of frotz source tree.'
$ git tag v2.43 <2>
-
+------------
++
<1> add everything under the current directory.
<2> make a lightweight, unannotated tag.
-------------
Create a topic branch and develop.::
+
@@ -153,7 +153,8 @@ $ git checkout master <9>
$ git pull . alsa-audio <10>
$ git log --since='3 days ago' <11>
$ git log v2.43.. curses/ <12>
-
+------------
++
<1> create a new topic branch.
<2> revert your botched changes in "curses/ux_audio_oss.c".
<3> you need to tell git if you added a new file; removal and
@@ -170,7 +171,6 @@ you originally wrote.
combined and include --max-count=10 (show 10 commits), --until='2005-12-10'.
<12> view only the changes that touch what's in curses/
directory, since v2.43 tag.
-------------
Individual Developer (Participant)[[Individual Developer (Participant)]]
@@ -208,7 +208,8 @@ $ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5>
$ git reset --hard ORIG_HEAD <6>
$ git prune <7>
$ git fetch --tags <8>
-
+------------
++
<1> repeat as needed.
<2> extract patches from your branch for e-mail submission.
<3> "pull" fetches from "origin" by default and merges into the
@@ -221,7 +222,6 @@ area we are interested in.
<7> garbage collect leftover objects from reverted pull.
<8> from time to time, obtain official tags from the "origin"
and store them under .git/refs/tags/.
-------------
Push into another repository.::
@@ -239,7 +239,8 @@ satellite$ git push origin <4>
mothership$ cd frotz
mothership$ git checkout master
mothership$ git pull . satellite <5>
-
+------------
++
<1> mothership machine has a frotz repository under your home
directory; clone from it to start a repository on the satellite
machine.
@@ -252,7 +253,6 @@ to local "origin" branch.
mothership machine. You could use this as a back-up method.
<5> on mothership machine, merge the work done on the satellite
machine into the master branch.
-------------
Branch off of a specific tag.::
+
@@ -262,12 +262,12 @@ $ edit/compile/test; git commit -a
$ git checkout master
$ git format-patch -k -m --stdout v2.6.14..private2.6.14 |
git am -3 -k <2>
-
+------------
++
<1> create a private branch based on a well known (but somewhat behind)
tag.
<2> forward port all changes in private2.6.14 branch to master branch
without a formal "merging".
-------------
Integrator[[Integrator]]
@@ -317,7 +317,8 @@ $ git tag -s -m 'GIT 0.99.9x' v0.99.9x <10>
$ git fetch ko && git show-branch master maint 'tags/ko-*' <11>
$ git push ko <12>
$ git push ko v0.99.9x <13>
-
+------------
++
<1> see what I was in the middle of doing, if any.
<2> see what topic branches I have and think about how ready
they are.
@@ -346,7 +347,6 @@ In the output from "git show-branch", "master" should have
everything "ko-master" has.
<12> push out the bleeding edge.
<13> push the tag out, too.
-------------
Repository Administration[[Repository Administration]]
@@ -367,7 +367,6 @@ example of managing a shared central repository.
Examples
~~~~~~~~
-
Run git-daemon to serve /pub/scm from inetd.::
+
------------
@@ -388,13 +387,13 @@ cindy:x:1002:1002::/home/cindy:/usr/bin/git-shell
david:x:1003:1003::/home/david:/usr/bin/git-shell
$ grep git /etc/shells <2>
/usr/bin/git-shell
-
+------------
++
<1> log-in shell is set to /usr/bin/git-shell, which does not
allow anything but "git push" and "git pull". The users should
get an ssh access to the machine.
<2> in many distributions /etc/shells needs to list what is used
as the login shell.
-------------
CVS-style shared repository.::
+
@@ -419,7 +418,8 @@ $ cat info/allowed-users <4>
refs/heads/master alice\|cindy
refs/heads/doc-update bob
refs/tags/v[0-9]* david
-
+------------
++
<1> place the developers into the same git group.
<2> and make the shared repository writable by the group.
<3> use update-hook example by Carl from Documentation/howto/
@@ -427,7 +427,6 @@ for branch policy control.
<4> alice and cindy can push into master, only bob can push into doc-update.
david is the release manager and is the only person who can
create and push version tags.
-------------
HTTP server to support dumb protocol transfer.::
+
@@ -435,7 +434,7 @@ HTTP server to support dumb protocol transfer.::
dev$ git update-server-info <1>
dev$ ftp user@isp.example.com <2>
ftp> cp -r .git /home/user/myproject.git
-
+------------
++
<1> make sure your info/refs and objects/info/packs are up-to-date
<2> upload to public HTTP server hosted by your ISP.
-------------
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 71ecd85..72fb2f8 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -3,22 +3,27 @@ git-branch(1)
NAME
----
-git-branch - Create a new branch, or remove an old one
+git-branch - List, create, or delete branches.
SYNOPSIS
--------
[verse]
-'git-branch' [[-f] <branchname> [<start-point>]]
-'git-branch' (-d | -D) <branchname>
+'git-branch' [-r]
+'git-branch' [-f] <branchname> [<start-point>]
+'git-branch' (-d | -D) <branchname>...
DESCRIPTION
-----------
-If no argument is provided, show available branches and mark current
-branch with star. Otherwise, create a new branch of name <branchname>.
-If a starting point is also specified, that will be where the branch is
-created, otherwise it will be created at the current HEAD.
+With no arguments given (or just `-r`) a list of available branches
+will be shown, the current branch will be highlighted with an asterisk.
-With a `-d` or `-D` option, `<branchname>` will be deleted.
+In its second form, a new branch named <branchname> will be created.
+It will start out with a head equal to the one given as <start-point>.
+If no <start-point> is given, the branch will be created with a head
+equal to that of the currently checked out branch.
+
+With a `-d` or `-D` option, `<branchname>` will be deleted. You may
+specify more than one branch for deletion.
OPTIONS
@@ -30,40 +35,56 @@ OPTIONS
Delete a branch irrespective of its index status.
-f::
- Force a reset of <branchname> to <start-point> (or current head).
+ Force the creation of a new branch even if it means deleting
+ a branch that already exists with the same name.
+
+-r::
+ List only the "remote" branches.
<branchname>::
The name of the branch to create or delete.
<start-point>::
- Where to create the branch; defaults to HEAD. This
- option has no meaning with -d and -D.
+ The new branch will be created with a HEAD equal to this. It may
+ be given as a branch name, a commit-id, or a tag. If this option
+ is omitted, the current branch is assumed.
+
Examples
-~~~~~~~~
+--------
Start development off of a known tag::
+
------------
$ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
$ cd my2.6
-$ git branch my2.6.14 v2.6.14 <1>
+$ git branch my2.6.14 v2.6.14 <1>
$ git checkout my2.6.14
-
-<1> These two steps are the same as "checkout -b my2.6.14 v2.6.14".
------------
++
+<1> This step and the next one could be combined into a single step with
+"checkout -b my2.6.14 v2.6.14".
Delete unneeded branch::
+
------------
$ git clone git://git.kernel.org/.../git.git my.git
$ cd my.git
-$ git branch -D todo <1>
-
+$ git branch -D todo <1>
+------------
++
<1> delete todo branch even if the "master" branch does not have all
commits from todo branch.
-------------
+
+
+Notes
+-----
+
+If you are creating a branch that you want to immediately checkout, it's
+easier to use the git checkout command with its `-b` option to create
+a branch and check it out with a single command.
+
Author
------
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 985bb2f..0951289 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -66,19 +66,19 @@ the `Makefile` to two revisions back, deletes hello.c by
mistake, and gets it back from the index.
+
------------
-$ git checkout master <1>
-$ git checkout master~2 Makefile <2>
+$ git checkout master <1>
+$ git checkout master~2 Makefile <2>
$ rm -f hello.c
-$ git checkout hello.c <3>
-
+$ git checkout hello.c <3>
+------------
++
<1> switch branch
<2> take out a file out of other commit
-<3> or "git checkout -- hello.c", as in the next example.
-------------
+<3> restore hello.c from HEAD of current branch
+
-If you have an unfortunate branch that is named `hello.c`, the
-last step above would be confused as an instruction to switch to
-that branch. You should instead write:
+If you have an unfortunate branch that is named `hello.c`, this
+step would be confused as an instruction to switch to that branch.
+You should instead write:
+
------------
$ git checkout -- hello.c
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 890931c..7267bcd 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -46,40 +46,41 @@ EXAMPLES
Various ways to check your working tree::
+
------------
-$ git diff <1>
-$ git diff --cached <2>
-$ git diff HEAD <3>
-
+$ git diff <1>
+$ git diff --cached <2>
+$ git diff HEAD <3>
+------------
++
<1> changes in the working tree since your last git-update-index.
<2> changes between the index and your last commit; what you
would be committing if you run "git commit" without "-a" option.
<3> changes in the working tree since your last commit; what you
would be committing if you run "git commit -a"
-------------
Comparing with arbitrary commits::
+
------------
-$ git diff test <1>
-$ git diff HEAD -- ./test <2>
-$ git diff HEAD^ HEAD <3>
-
+$ git diff test <1>
+$ git diff HEAD -- ./test <2>
+$ git diff HEAD^ HEAD <3>
+------------
++
<1> instead of using the tip of the current branch, compare with the
tip of "test" branch.
<2> instead of comparing with the tip of "test" branch, compare with
the tip of the current branch, but limit the comparison to the
file "test".
<3> compare the version before the last commit and the last commit.
-------------
Limiting the diff output::
+
------------
-$ git diff --diff-filter=MRC <1>
-$ git diff --name-status -r <2>
-$ git diff arch/i386 include/asm-i386 <3>
-
+$ git diff --diff-filter=MRC <1>
+$ git diff --name-status -r <2>
+$ git diff arch/i386 include/asm-i386 <3>
+------------
++
<1> show only modification, rename and copy, but not addition
nor deletion.
<2> show only names and the nature of change, but not actual
@@ -88,18 +89,17 @@ which in turn also disables recursive behaviour, so without -r
you would only see the directory name if there is a change in a
file in a subdirectory.
<3> limit diff output to named subtrees.
-------------
Munging the diff output::
+
------------
-$ git diff --find-copies-harder -B -C <1>
-$ git diff -R <2>
-
+$ git diff --find-copies-harder -B -C <1>
+$ git diff -R <2>
+------------
++
<1> spend extra cycles to find renames, copies and complete
rewrites (very expensive).
<2> output diff in reverse.
-------------
Author
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index aeb1115..8a150d8 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -60,12 +60,12 @@ Start a new git repository for an existing code base::
+
----------------
$ cd /path/to/my/codebase
-$ git-init-db <1>
-$ git-add . <2>
-
+$ git-init-db <1>
+$ git-add . <2>
+----------------
++
<1> prepare /path/to/my/codebase/.git directory
<2> add all existing file to the index
-----------------
Author
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 76cb894..af378ff 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -14,13 +14,12 @@ DESCRIPTION
-----------
Shows the commit logs.
-The command takes options applicable to the gitlink::git-rev-list[1]
+The command takes options applicable to the gitlink:git-rev-list[1]
command to control what is shown and how, and options applicable to
-the gitlink::git-diff-tree[1] commands to control how the change
+the gitlink:git-diff-tree[1] commands to control how the change
each commit introduces are shown.
-This manual page describes only the most frequently used
-options.
+This manual page describes only the most frequently used options.
OPTIONS
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index 71f96bd..566cfa1 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -15,6 +15,7 @@ SYNOPSIS
'git-repo-config' [type] --get-all name [value_regex]
'git-repo-config' [type] --unset name [value_regex]
'git-repo-config' [type] --unset-all name [value_regex]
+'git-repo-config' -l | --list
DESCRIPTION
-----------
@@ -64,6 +65,9 @@ OPTIONS
--unset-all::
Remove all matching lines from .git/config.
+-l, --list::
+ List all variables set in .git/config.
+
EXAMPLE
-------
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index b7b9798..ebcfe5e 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -49,10 +49,11 @@ Undo a commit and redo::
+
------------
$ git commit ...
-$ git reset --soft HEAD^ <1>
-$ edit <2>
-$ git commit -a -c ORIG_HEAD <3>
-
+$ git reset --soft HEAD^ <1>
+$ edit <2>
+$ git commit -a -c ORIG_HEAD <3>
+------------
++
<1> This is most often done when you remembered what you
just committed is incomplete, or you misspelled your commit
message, or both. Leaves working tree as it was before "reset".
@@ -60,43 +61,43 @@ message, or both. Leaves working tree as it was before "reset".
<3> "reset" copies the old head to .git/ORIG_HEAD; redo the
commit by starting with its log message. If you do not need to
edit the message further, you can give -C option instead.
-------------
Undo commits permanently::
+
------------
$ git commit ...
-$ git reset --hard HEAD~3 <1>
-
+$ git reset --hard HEAD~3 <1>
+------------
++
<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
and you do not want to ever see them again. Do *not* do this if
you have already given these commits to somebody else.
-------------
Undo a commit, making it a topic branch::
+
------------
-$ git branch topic/wip <1>
-$ git reset --hard HEAD~3 <2>
-$ git checkout topic/wip <3>
-
+$ git branch topic/wip <1>
+$ git reset --hard HEAD~3 <2>
+$ git checkout topic/wip <3>
+------------
++
<1> You have made some commits, but realize they were premature
to be in the "master" branch. You want to continue polishing
them in a topic branch, so create "topic/wip" branch off of the
current HEAD.
<2> Rewind the master branch to get rid of those three commits.
<3> Switch to "topic/wip" branch and keep working.
-------------
Undo update-index::
+
------------
-$ edit <1>
+$ edit <1>
$ git-update-index frotz.c filfre.c
-$ mailx <2>
-$ git reset <3>
-$ git pull git://info.example.com/ nitfol <4>
-
+$ mailx <2>
+$ git reset <3>
+$ git pull git://info.example.com/ nitfol <4>
+------------
++
<1> you are happily working on something, and find the changes
in these files are in good order. You do not want to see them
when you run "git diff", because you plan to work on other files
@@ -109,12 +110,11 @@ index changes for these two files. Your changes in working tree
remain there.
<4> then you can pull and merge, leaving frotz.c and filfre.c
changes still in the working tree.
-------------
Undo a merge or pull::
+
------------
-$ git pull <1>
+$ git pull <1>
Trying really trivial in-index merge...
fatal: Merge requires file-level merging
Nope.
@@ -122,20 +122,19 @@ Nope.
Auto-merging nitfol
CONFLICT (content): Merge conflict in nitfol
Automatic merge failed/prevented; fix up by hand
-$ git reset --hard <2>
-
+$ git reset --hard <2>
+$ git pull . topic/branch <3>
+Updating from 41223... to 13134...
+Fast forward
+$ git reset --hard ORIG_HEAD <4>
+------------
++
<1> try to update from the upstream resulted in a lot of
conflicts; you were not ready to spend a lot of time merging
right now, so you decide to do that later.
<2> "pull" has not made merge commit, so "git reset --hard"
which is a synonym for "git reset --hard HEAD" clears the mess
from the index file and the working tree.
-
-$ git pull . topic/branch <3>
-Updating from 41223... to 13134...
-Fast forward
-$ git reset --hard ORIG_HEAD <4>
-
<3> merge a topic branch into the current branch, which resulted
in a fast forward.
<4> but you decided that the topic branch is not ready for public
@@ -143,7 +142,6 @@ consumption yet. "pull" or "merge" always leaves the original
tip of the current branch in ORIG_HEAD, so resetting hard to it
brings your index file and the working tree back to that state,
and resets the tip of the branch to that commit.
-------------
Interrupted workflow::
+
@@ -155,21 +153,21 @@ need to get to the other branch for a quick bugfix.
------------
$ git checkout feature ;# you were working in "feature" branch and
$ work work work ;# got interrupted
-$ git commit -a -m 'snapshot WIP' <1>
+$ git commit -a -m 'snapshot WIP' <1>
$ git checkout master
$ fix fix fix
$ git commit ;# commit with real log
$ git checkout feature
-$ git reset --soft HEAD^ ;# go back to WIP state <2>
-$ git reset <3>
-
+$ git reset --soft HEAD^ ;# go back to WIP state <2>
+$ git reset <3>
+------------
++
<1> This commit will get blown away so a throw-away log message is OK.
<2> This removes the 'WIP' commit from the commit history, and sets
your working tree to the state just before you made that snapshot.
-<3> After <2>, the index file still has all the WIP changes you
- committed in <1>. This sets it to the last commit you were
- basing the WIP changes on.
-------------
+<3> At this point the index file still has all the WIP changes you
+ committed as 'snapshot WIP'. This updates the index to show your
+ WIP files as uncommitted.
Author
------
diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt
index 0a1b0ad..d4137fc 100644
--- a/Documentation/git-update-index.txt
+++ b/Documentation/git-update-index.txt
@@ -247,34 +247,33 @@ To update and refresh only the files already checked out:
$ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
----------------
-On an inefficient filesystem with `core.ignorestat` set:
-
+On an inefficient filesystem with `core.ignorestat` set::
++
------------
-$ git update-index --really-refresh <1>
-$ git update-index --no-assume-unchanged foo.c <2>
-$ git diff --name-only <3>
+$ git update-index --really-refresh <1>
+$ git update-index --no-assume-unchanged foo.c <2>
+$ git diff --name-only <3>
$ edit foo.c
-$ git diff --name-only <4>
+$ git diff --name-only <4>
M foo.c
-$ git update-index foo.c <5>
-$ git diff --name-only <6>
+$ git update-index foo.c <5>
+$ git diff --name-only <6>
$ edit foo.c
-$ git diff --name-only <7>
-$ git update-index --no-assume-unchanged foo.c <8>
-$ git diff --name-only <9>
+$ git diff --name-only <7>
+$ git update-index --no-assume-unchanged foo.c <8>
+$ git diff --name-only <9>
M foo.c
-
-<1> forces lstat(2) to set "assume unchanged" bits for paths
- that match index.
+------------
++
+<1> forces lstat(2) to set "assume unchanged" bits for paths that match index.
<2> mark the path to be edited.
<3> this does lstat(2) and finds index matches the path.
-<4> this does lstat(2) and finds index does not match the path.
+<4> this does lstat(2) and finds index does *not* match the path.
<5> registering the new version to index sets "assume unchanged" bit.
<6> and it is assumed unchanged.
<7> even after you edit it.
<8> you can tell about the change after the fact.
<9> now it checks with lstat(2) and finds it has been changed.
-------------
Configuration
diff --git a/Documentation/git-var.txt b/Documentation/git-var.txt
index 379571e..a5b1a0d 100644
--- a/Documentation/git-var.txt
+++ b/Documentation/git-var.txt
@@ -19,7 +19,8 @@ OPTIONS
-l::
Cause the logical variables to be listed. In addition, all the
variables of the git configuration file .git/config are listed
- as well.
+ as well. (However, the configuration variables listing functionality
+ is deprecated in favor of `git-repo-config -l`.)
EXAMPLE
--------
diff --git a/Makefile b/Makefile
index d9a3a82..8ce27a6 100644
--- a/Makefile
+++ b/Makefile
@@ -199,7 +199,7 @@ LIB_H = \
tree-walk.h log-tree.h
DIFF_OBJS = \
- diff-lib.o diffcore-break.o diffcore-order.o \
+ diff.o diff-lib.o diffcore-break.o diffcore-order.o \
diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o \
diffcore-delta.o log-tree.o
@@ -575,12 +575,12 @@ $(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS)
$(DIFF_OBJS): diffcore.h
$(LIB_FILE): $(LIB_OBJS)
- $(AR) rcs $@ $(LIB_OBJS)
+ rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o
$(XDIFF_LIB): $(XDIFF_OBJS)
- $(AR) rcs $@ $(XDIFF_OBJS)
+ rm -f $@ && $(AR) rcs $@ $(XDIFF_OBJS)
doc:
diff --git a/builtin-help.c b/builtin-help.c
index 10a59cc..7470faa 100644
--- a/builtin-help.c
+++ b/builtin-help.c
@@ -3,6 +3,7 @@
*
* Builtin help-related commands (help, usage, version)
*/
+#include <sys/ioctl.h>
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
diff --git a/cache.h b/cache.h
index 69801b0..4d8fabc 100644
--- a/cache.h
+++ b/cache.h
@@ -134,6 +134,7 @@ extern const char *setup_git_directory_gently(int *);
extern const char *setup_git_directory(void);
extern const char *prefix_path(const char *prefix, int len, const char *path);
extern const char *prefix_filename(const char *prefix, int len, const char *path);
+extern void verify_filename(const char *prefix, const char *name);
#define alloc_nr(x) (((x)+16)*3/2)
diff --git a/commit-tree.c b/commit-tree.c
index 2d86518..bad72e8 100644
--- a/commit-tree.c
+++ b/commit-tree.c
@@ -45,14 +45,13 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
static void check_valid(unsigned char *sha1, const char *expect)
{
- void *buf;
char type[20];
- unsigned long size;
- buf = read_sha1_file(sha1, type, &size);
- if (!buf || strcmp(type, expect))
- die("%s is not a valid '%s' object", sha1_to_hex(sha1), expect);
- free(buf);
+ if (sha1_object_info(sha1, type, NULL))
+ die("%s is not a valid object", sha1_to_hex(sha1));
+ if (expect && strcmp(type, expect))
+ die("%s is not a valid '%s' object", sha1_to_hex(sha1),
+ expect);
}
/*
@@ -92,7 +91,7 @@ int main(int argc, char **argv)
git_config(git_default_config);
- if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
+ if (argc < 2 || get_sha1(argv[1], tree_sha1) < 0)
usage(commit_tree_usage);
check_valid(tree_sha1, tree_type);
diff --git a/diff-files.c b/diff-files.c
index ffbef48..b9d193d 100644
--- a/diff-files.c
+++ b/diff-files.c
@@ -12,203 +12,43 @@ static const char diff_files_usage[] =
"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
COMMON_DIFF_OPTIONS_HELP;
-static struct rev_info rev;
-static int silent = 0;
-static int diff_unmerged_stage = 2;
-static int combine_merges = 0;
-static int dense_combined_merges = 0;
-
-static void show_unmerge(const char *path)
-{
- diff_unmerge(&rev.diffopt, path);
-}
-
-static void show_file(int pfx, struct cache_entry *ce)
-{
- diff_addremove(&rev.diffopt, pfx, ntohl(ce->ce_mode),
- ce->sha1, ce->name, NULL);
-}
-
-static void show_modified(int oldmode, int mode,
- const unsigned char *old_sha1, const unsigned char *sha1,
- char *path)
-{
- diff_change(&rev.diffopt, oldmode, mode, old_sha1, sha1, path, NULL);
-}
-
int main(int argc, const char **argv)
{
- const char **pathspec;
- const char *prefix = setup_git_directory();
- int entries, i;
+ struct rev_info rev;
+ int silent = 0;
git_config(git_diff_config);
- diff_setup(&rev.diffopt);
+ init_revisions(&rev);
+ rev.abbrev = 0;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
while (1 < argc && argv[1][0] == '-') {
- if (!strcmp(argv[1], "--")) {
- argv++;
- argc--;
- break;
- }
- if (!strcmp(argv[1], "-0"))
- diff_unmerged_stage = 0;
- else if (!strcmp(argv[1], "-1"))
- diff_unmerged_stage = 1;
- else if (!strcmp(argv[1], "-2"))
- diff_unmerged_stage = 2;
- else if (!strcmp(argv[1], "-3"))
- diff_unmerged_stage = 3;
- else if (!strcmp(argv[1], "--base"))
- diff_unmerged_stage = 1;
+ if (!strcmp(argv[1], "--base"))
+ rev.max_count = 1;
else if (!strcmp(argv[1], "--ours"))
- diff_unmerged_stage = 2;
+ rev.max_count = 2;
else if (!strcmp(argv[1], "--theirs"))
- diff_unmerged_stage = 3;
+ rev.max_count = 3;
else if (!strcmp(argv[1], "-q"))
silent = 1;
- else if (!strcmp(argv[1], "-r"))
- ; /* no-op */
- else if (!strcmp(argv[1], "-s"))
- ; /* no-op */
- else if (!strcmp(argv[1], "-c"))
- combine_merges = 1;
- else if (!strcmp(argv[1], "--cc"))
- dense_combined_merges = combine_merges = 1;
- else {
- int diff_opt_cnt;
- diff_opt_cnt = diff_opt_parse(&rev.diffopt,
- argv+1, argc-1);
- if (diff_opt_cnt < 0)
- usage(diff_files_usage);
- else if (diff_opt_cnt) {
- argv += diff_opt_cnt;
- argc -= diff_opt_cnt;
- continue;
- }
- else
- usage(diff_files_usage);
- }
+ else
+ usage(diff_files_usage);
argv++; argc--;
}
- if (dense_combined_merges)
- rev.diffopt.output_format = DIFF_FORMAT_PATCH;
-
- /* Find the directory, and set up the pathspec */
- pathspec = get_pathspec(prefix, argv + 1);
- entries = read_cache();
-
- if (diff_setup_done(&rev.diffopt) < 0)
+ /*
+ * Make sure there are NO revision (i.e. pending object) parameter,
+ * rev.max_count is reasonable (0 <= n <= 3),
+ * there is no other revision filtering parameters.
+ */
+ if (rev.pending_objects ||
+ rev.min_age != -1 || rev.max_age != -1)
usage(diff_files_usage);
-
- /* At this point, if argc == 1, then we are doing everything.
- * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
+ /*
+ * Backward compatibility wart - "diff-files -s" used to
+ * defeat the common diff option "-s" which asked for
+ * DIFF_FORMAT_NO_OUTPUT.
*/
- if (entries < 0) {
- perror("read_cache");
- exit(1);
- }
-
- for (i = 0; i < entries; i++) {
- struct stat st;
- unsigned int oldmode, newmode;
- struct cache_entry *ce = active_cache[i];
- int changed;
-
- if (!ce_path_match(ce, pathspec))
- continue;
-
- if (ce_stage(ce)) {
- struct {
- struct combine_diff_path p;
- struct combine_diff_parent filler[5];
- } combine;
- int num_compare_stages = 0;
-
- combine.p.next = NULL;
- combine.p.len = ce_namelen(ce);
- combine.p.path = xmalloc(combine.p.len + 1);
- memcpy(combine.p.path, ce->name, combine.p.len);
- combine.p.path[combine.p.len] = 0;
- combine.p.mode = 0;
- memset(combine.p.sha1, 0, 20);
- memset(&combine.p.parent[0], 0,
- sizeof(combine.filler));
-
- while (i < entries) {
- struct cache_entry *nce = active_cache[i];
- int stage;
-
- if (strcmp(ce->name, nce->name))
- break;
-
- /* Stage #2 (ours) is the first parent,
- * stage #3 (theirs) is the second.
- */
- stage = ce_stage(nce);
- if (2 <= stage) {
- int mode = ntohl(nce->ce_mode);
- num_compare_stages++;
- memcpy(combine.p.parent[stage-2].sha1,
- nce->sha1, 20);
- combine.p.parent[stage-2].mode =
- canon_mode(mode);
- combine.p.parent[stage-2].status =
- DIFF_STATUS_MODIFIED;
- }
-
- /* diff against the proper unmerged stage */
- if (stage == diff_unmerged_stage)
- ce = nce;
- i++;
- }
- /*
- * Compensate for loop update
- */
- i--;
-
- if (combine_merges && num_compare_stages == 2) {
- show_combined_diff(&combine.p, 2,
- dense_combined_merges,
- &rev);
- free(combine.p.path);
- continue;
- }
- free(combine.p.path);
-
- /*
- * Show the diff for the 'ce' if we found the one
- * from the desired stage.
- */
- show_unmerge(ce->name);
- if (ce_stage(ce) != diff_unmerged_stage)
- continue;
- }
-
- if (lstat(ce->name, &st) < 0) {
- if (errno != ENOENT && errno != ENOTDIR) {
- perror(ce->name);
- continue;
- }
- if (silent)
- continue;
- show_file('-', ce);
- continue;
- }
- changed = ce_match_stat(ce, &st, 0);
- if (!changed && !rev.diffopt.find_copies_harder)
- continue;
- oldmode = ntohl(ce->ce_mode);
-
- newmode = canon_mode(st.st_mode);
- if (!trust_executable_bit &&
- S_ISREG(newmode) && S_ISREG(oldmode) &&
- ((newmode ^ oldmode) == 0111))
- newmode = oldmode;
- show_modified(oldmode, newmode,
- ce->sha1, (changed ? null_sha1 : ce->sha1),
- ce->name);
- }
- diffcore_std(&rev.diffopt);
- diff_flush(&rev.diffopt);
- return 0;
+ if (rev.diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
+ rev.diffopt.output_format = DIFF_FORMAT_RAW;
+ return run_diff_files(&rev, silent);
}
diff --git a/diff-index.c b/diff-index.c
index e376d65..8c9f601 100644
--- a/diff-index.c
+++ b/diff-index.c
@@ -1,166 +1,7 @@
#include "cache.h"
-#include "tree.h"
#include "diff.h"
-
-static int cached_only = 0;
-static int match_nonexisting = 0;
-static struct diff_options diff_options;
-
-/* A file entry went away or appeared */
-static void show_file(const char *prefix,
- struct cache_entry *ce,
- unsigned char *sha1, unsigned int mode)
-{
- diff_addremove(&diff_options, prefix[0], ntohl(mode),
- sha1, ce->name, NULL);
-}
-
-static int get_stat_data(struct cache_entry *ce,
- unsigned char ** sha1p, unsigned int *modep)
-{
- unsigned char *sha1 = ce->sha1;
- unsigned int mode = ce->ce_mode;
-
- if (!cached_only) {
- static unsigned char no_sha1[20];
- int changed;
- struct stat st;
- if (lstat(ce->name, &st) < 0) {
- if (errno == ENOENT && match_nonexisting) {
- *sha1p = sha1;
- *modep = mode;
- return 0;
- }
- return -1;
- }
- changed = ce_match_stat(ce, &st, 0);
- if (changed) {
- mode = create_ce_mode(st.st_mode);
- if (!trust_executable_bit && S_ISREG(st.st_mode))
- mode = ce->ce_mode;
- sha1 = no_sha1;
- }
- }
-
- *sha1p = sha1;
- *modep = mode;
- return 0;
-}
-
-static void show_new_file(struct cache_entry *new)
-{
- unsigned char *sha1;
- unsigned int mode;
-
- /* New file in the index: it might actually be different in
- * the working copy.
- */
- if (get_stat_data(new, &sha1, &mode) < 0)
- return;
-
- show_file("+", new, sha1, mode);
-}
-
-static int show_modified(struct cache_entry *old,
- struct cache_entry *new,
- int report_missing)
-{
- unsigned int mode, oldmode;
- unsigned char *sha1;
-
- if (get_stat_data(new, &sha1, &mode) < 0) {
- if (report_missing)
- show_file("-", old, old->sha1, old->ce_mode);
- return -1;
- }
-
- oldmode = old->ce_mode;
- if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
- !diff_options.find_copies_harder)
- return 0;
-
- mode = ntohl(mode);
- oldmode = ntohl(oldmode);
-
- diff_change(&diff_options, oldmode, mode,
- old->sha1, sha1, old->name, NULL);
- return 0;
-}
-
-static int diff_cache(struct cache_entry **ac, int entries, const char **pathspec)
-{
- while (entries) {
- struct cache_entry *ce = *ac;
- int same = (entries > 1) && ce_same_name(ce, ac[1]);
-
- if (!ce_path_match(ce, pathspec))
- goto skip_entry;
-
- switch (ce_stage(ce)) {
- case 0:
- /* No stage 1 entry? That means it's a new file */
- if (!same) {
- show_new_file(ce);
- break;
- }
- /* Show difference between old and new */
- show_modified(ac[1], ce, 1);
- break;
- case 1:
- /* No stage 3 (merge) entry? That means it's been deleted */
- if (!same) {
- show_file("-", ce, ce->sha1, ce->ce_mode);
- break;
- }
- /* We come here with ce pointing at stage 1
- * (original tree) and ac[1] pointing at stage
- * 3 (unmerged). show-modified with
- * report-missing set to false does not say the
- * file is deleted but reports true if work
- * tree does not have it, in which case we
- * fall through to report the unmerged state.
- * Otherwise, we show the differences between
- * the original tree and the work tree.
- */
- if (!cached_only && !show_modified(ce, ac[1], 0))
- break;
- /* fallthru */
- case 3:
- diff_unmerge(&diff_options, ce->name);
- break;
-
- default:
- die("impossible cache entry stage");
- }
-
-skip_entry:
- /*
- * Ignore all the different stages for this file,
- * we've handled the relevant cases now.
- */
- do {
- ac++;
- entries--;
- } while (entries && ce_same_name(ce, ac[0]));
- }
- return 0;
-}
-
-/*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
- */
-static void mark_merge_entries(void)
-{
- int i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= htons(CE_STAGEMASK);
- }
-}
+#include "commit.h"
+#include "revision.h"
static const char diff_cache_usage[] =
"git-diff-index [-m] [--cached] "
@@ -169,85 +10,29 @@ COMMON_DIFF_OPTIONS_HELP;
int main(int argc, const char **argv)
{
- const char *tree_name = NULL;
- unsigned char sha1[20];
- const char *prefix = setup_git_directory();
- const char **pathspec = NULL;
- struct tree *tree;
- int ret;
- int allow_options = 1;
+ struct rev_info rev;
+ int cached = 0;
int i;
git_config(git_diff_config);
- diff_setup(&diff_options);
+ init_revisions(&rev);
+ rev.abbrev = 0;
+
+ argc = setup_revisions(argc, argv, &rev, NULL);
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
- int diff_opt_cnt;
-
- if (!allow_options || *arg != '-') {
- if (tree_name)
- break;
- tree_name = arg;
- continue;
- }
- if (!strcmp(arg, "--")) {
- allow_options = 0;
- continue;
- }
- if (!strcmp(arg, "-r")) {
- /* We accept the -r flag just to look like git-diff-tree */
- continue;
- }
- if (!strcmp(arg, "--cc"))
- /*
- * I _think_ "diff-index --cached HEAD" with an
- * unmerged index could show something else
- * later, but pretend --cc is the same as -p for
- * now. "git diff" uses --cc by default.
- */
- argv[i] = arg = "-p";
- diff_opt_cnt = diff_opt_parse(&diff_options, argv + i,
- argc - i);
- if (diff_opt_cnt < 0)
+ if (!strcmp(arg, "--cached"))
+ cached = 1;
+ else
usage(diff_cache_usage);
- else if (diff_opt_cnt) {
- i += diff_opt_cnt - 1;
- continue;
- }
-
- if (!strcmp(arg, "-m")) {
- match_nonexisting = 1;
- continue;
- }
- if (!strcmp(arg, "--cached")) {
- cached_only = 1;
- continue;
- }
- usage(diff_cache_usage);
}
-
- pathspec = get_pathspec(prefix, argv + i);
-
- if (diff_setup_done(&diff_options) < 0)
- usage(diff_cache_usage);
-
- if (!tree_name || get_sha1(tree_name, sha1))
+ /*
+ * Make sure there is one revision (i.e. pending object),
+ * and there is no revision filtering parameters.
+ */
+ if (!rev.pending_objects || rev.pending_objects->next ||
+ rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
usage(diff_cache_usage);
-
- read_cache();
-
- mark_merge_entries();
-
- tree = parse_tree_indirect(sha1);
- if (!tree)
- die("bad tree object %s", tree_name);
- if (read_tree(tree, 1, pathspec))
- die("unable to read tree object %s", tree_name);
-
- ret = diff_cache(active_cache, active_nr, pathspec);
-
- diffcore_std(&diff_options);
- diff_flush(&diff_options);
- return ret;
+ return run_diff_index(&rev, cached);
}
diff --git a/diff-lib.c b/diff-lib.c
index 13b216f..2183b41 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -1,1795 +1,344 @@
/*
* Copyright (C) 2005 Junio C Hamano
*/
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
#include "cache.h"
#include "quote.h"
+#include "commit.h"
#include "diff.h"
#include "diffcore.h"
-#include "xdiff-interface.h"
+#include "revision.h"
-static int use_size_cache;
-
-int diff_rename_limit_default = -1;
-
-int git_diff_config(const char *var, const char *value)
-{
- if (!strcmp(var, "diff.renamelimit")) {
- diff_rename_limit_default = git_config_int(var, value);
- return 0;
- }
-
- return git_default_config(var, value);
-}
-
-static char *quote_one(const char *str)
-{
- int needlen;
- char *xp;
-
- if (!str)
- return NULL;
- needlen = quote_c_style(str, NULL, NULL, 0);
- if (!needlen)
- return strdup(str);
- xp = xmalloc(needlen + 1);
- quote_c_style(str, xp, NULL, 0);
- return xp;
-}
-
-static char *quote_two(const char *one, const char *two)
-{
- int need_one = quote_c_style(one, NULL, NULL, 1);
- int need_two = quote_c_style(two, NULL, NULL, 1);
- char *xp;
-
- if (need_one + need_two) {
- if (!need_one) need_one = strlen(one);
- if (!need_two) need_one = strlen(two);
-
- xp = xmalloc(need_one + need_two + 3);
- xp[0] = '"';
- quote_c_style(one, xp + 1, NULL, 1);
- quote_c_style(two, xp + need_one + 1, NULL, 1);
- strcpy(xp + need_one + need_two + 1, "\"");
- return xp;
- }
- need_one = strlen(one);
- need_two = strlen(two);
- xp = xmalloc(need_one + need_two + 1);
- strcpy(xp, one);
- strcpy(xp + need_one, two);
- return xp;
-}
-
-static const char *external_diff(void)
-{
- static const char *external_diff_cmd = NULL;
- static int done_preparing = 0;
-
- if (done_preparing)
- return external_diff_cmd;
- external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
- done_preparing = 1;
- return external_diff_cmd;
-}
-
-#define TEMPFILE_PATH_LEN 50
-
-static struct diff_tempfile {
- const char *name; /* filename external diff should read from */
- char hex[41];
- char mode[10];
- char tmp_path[TEMPFILE_PATH_LEN];
-} diff_temp[2];
-
-static int count_lines(const char *data, int size)
-{
- int count, ch, completely_empty = 1, nl_just_seen = 0;
- count = 0;
- while (0 < size--) {
- ch = *data++;
- if (ch == '\n') {
- count++;
- nl_just_seen = 1;
- completely_empty = 0;
- }
- else {
- nl_just_seen = 0;
- completely_empty = 0;
- }
- }
- if (completely_empty)
- return 0;
- if (!nl_just_seen)
- count++; /* no trailing newline */
- return count;
-}
-
-static void print_line_count(int count)
-{
- switch (count) {
- case 0:
- printf("0,0");
- break;
- case 1:
- printf("1");
- break;
- default:
- printf("1,%d", count);
- break;
- }
-}
-
-static void copy_file(int prefix, const char *data, int size)
-{
- int ch, nl_just_seen = 1;
- while (0 < size--) {
- ch = *data++;
- if (nl_just_seen)
- putchar(prefix);
- putchar(ch);
- if (ch == '\n')
- nl_just_seen = 1;
- else
- nl_just_seen = 0;
- }
- if (!nl_just_seen)
- printf("\n\\ No newline at end of file\n");
-}
+/*
+ * diff-files
+ */
-static void emit_rewrite_diff(const char *name_a,
- const char *name_b,
- struct diff_filespec *one,
- struct diff_filespec *two)
+int run_diff_files(struct rev_info *revs, int silent_on_removed)
{
- int lc_a, lc_b;
- diff_populate_filespec(one, 0);
- diff_populate_filespec(two, 0);
- lc_a = count_lines(one->data, one->size);
- lc_b = count_lines(two->data, two->size);
- printf("--- %s\n+++ %s\n@@ -", name_a, name_b);
- print_line_count(lc_a);
- printf(" +");
- print_line_count(lc_b);
- printf(" @@\n");
- if (lc_a)
- copy_file('-', one->data, one->size);
- if (lc_b)
- copy_file('+', two->data, two->size);
-}
+ int entries, i;
+ int diff_unmerged_stage = revs->max_count;
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
-{
- if (!DIFF_FILE_VALID(one)) {
- mf->ptr = ""; /* does not matter */
- mf->size = 0;
- return 0;
- }
- else if (diff_populate_filespec(one, 0))
+ if (diff_unmerged_stage < 0)
+ diff_unmerged_stage = 2;
+ entries = read_cache();
+ if (entries < 0) {
+ perror("read_cache");
return -1;
- mf->ptr = one->data;
- mf->size = one->size;
- return 0;
-}
-
-struct emit_callback {
- const char **label_path;
-};
-
-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
-{
- int i;
- struct emit_callback *ecbdata = priv;
-
- if (ecbdata->label_path[0]) {
- printf("--- %s\n", ecbdata->label_path[0]);
- printf("+++ %s\n", ecbdata->label_path[1]);
- ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
}
- for (i = 0; i < nbuf; i++)
- if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
- return -1;
- return 0;
-}
-
-static char *pprint_rename(const char *a, const char *b)
-{
- const char *old = a;
- const char *new = b;
- char *name = NULL;
- int pfx_length, sfx_length;
- int len_a = strlen(a);
- int len_b = strlen(b);
-
- /* Find common prefix */
- pfx_length = 0;
- while (*old && *new && *old == *new) {
- if (*old == '/')
- pfx_length = old - a + 1;
- old++;
- new++;
- }
-
- /* Find common suffix */
- old = a + len_a;
- new = b + len_b;
- sfx_length = 0;
- while (a <= old && b <= new && *old == *new) {
- if (*old == '/')
- sfx_length = len_a - (old - a);
- old--;
- new--;
- }
-
- /*
- * pfx{mid-a => mid-b}sfx
- * {pfx-a => pfx-b}sfx
- * pfx{sfx-a => sfx-b}
- * name-a => name-b
- */
- if (pfx_length + sfx_length) {
- name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
- sprintf(name, "%.*s{%.*s => %.*s}%s",
- pfx_length, a,
- len_a - pfx_length - sfx_length, a + pfx_length,
- len_b - pfx_length - sfx_length, b + pfx_length,
- a + len_a - sfx_length);
- }
- else {
- name = xmalloc(len_a + len_b + 5);
- sprintf(name, "%s => %s", a, b);
- }
- return name;
-}
-
-struct diffstat_t {
- struct xdiff_emit_state xm;
-
- int nr;
- int alloc;
- struct diffstat_file {
- char *name;
- unsigned is_unmerged:1;
- unsigned is_binary:1;
- unsigned is_renamed:1;
- unsigned int added, deleted;
- } **files;
-};
-
-static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
- const char *name_a,
- const char *name_b)
-{
- struct diffstat_file *x;
- x = xcalloc(sizeof (*x), 1);
- if (diffstat->nr == diffstat->alloc) {
- diffstat->alloc = alloc_nr(diffstat->alloc);
- diffstat->files = xrealloc(diffstat->files,
- diffstat->alloc * sizeof(x));
- }
- diffstat->files[diffstat->nr++] = x;
- if (name_b) {
- x->name = pprint_rename(name_a, name_b);
- x->is_renamed = 1;
- }
- else
- x->name = strdup(name_a);
- return x;
-}
-
-static void diffstat_consume(void *priv, char *line, unsigned long len)
-{
- struct diffstat_t *diffstat = priv;
- struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
-
- if (line[0] == '+')
- x->added++;
- else if (line[0] == '-')
- x->deleted++;
-}
-
-static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
-static const char minuses[]= "----------------------------------------------------------------------";
-
-static void show_stats(struct diffstat_t* data)
-{
- char *prefix = "";
- int i, len, add, del, total, adds = 0, dels = 0;
- int max, max_change = 0, max_len = 0;
- int total_files = data->nr;
-
- if (data->nr == 0)
- return;
-
- for (i = 0; i < data->nr; i++) {
- struct diffstat_file *file = data->files[i];
-
- len = strlen(file->name);
- if (max_len < len)
- max_len = len;
+ for (i = 0; i < entries; i++) {
+ struct stat st;
+ unsigned int oldmode, newmode;
+ struct cache_entry *ce = active_cache[i];
+ int changed;
- if (file->is_binary || file->is_unmerged)
+ if (!ce_path_match(ce, revs->prune_data))
continue;
- if (max_change < file->added + file->deleted)
- max_change = file->added + file->deleted;
- }
- for (i = 0; i < data->nr; i++) {
- char *name = data->files[i]->name;
- int added = data->files[i]->added;
- int deleted = data->files[i]->deleted;
+ if (ce_stage(ce)) {
+ struct {
+ struct combine_diff_path p;
+ struct combine_diff_parent filler[5];
+ } combine;
+ int num_compare_stages = 0;
+
+ combine.p.next = NULL;
+ combine.p.len = ce_namelen(ce);
+ combine.p.path = xmalloc(combine.p.len + 1);
+ memcpy(combine.p.path, ce->name, combine.p.len);
+ combine.p.path[combine.p.len] = 0;
+ combine.p.mode = 0;
+ memset(combine.p.sha1, 0, 20);
+ memset(&combine.p.parent[0], 0,
+ sizeof(combine.filler));
+
+ while (i < entries) {
+ struct cache_entry *nce = active_cache[i];
+ int stage;
+
+ if (strcmp(ce->name, nce->name))
+ break;
+
+ /* Stage #2 (ours) is the first parent,
+ * stage #3 (theirs) is the second.
+ */
+ stage = ce_stage(nce);
+ if (2 <= stage) {
+ int mode = ntohl(nce->ce_mode);
+ num_compare_stages++;
+ memcpy(combine.p.parent[stage-2].sha1,
+ nce->sha1, 20);
+ combine.p.parent[stage-2].mode =
+ canon_mode(mode);
+ combine.p.parent[stage-2].status =
+ DIFF_STATUS_MODIFIED;
+ }
+
+ /* diff against the proper unmerged stage */
+ if (stage == diff_unmerged_stage)
+ ce = nce;
+ i++;
+ }
+ /*
+ * Compensate for loop update
+ */
+ i--;
- if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
- char *qname = xmalloc(len + 1);
- quote_c_style(name, qname, NULL, 0);
- free(name);
- data->files[i]->name = name = qname;
- }
+ if (revs->combine_merges && num_compare_stages == 2) {
+ show_combined_diff(&combine.p, 2,
+ revs->dense_combined_merges,
+ revs);
+ free(combine.p.path);
+ continue;
+ }
+ free(combine.p.path);
- /*
- * "scale" the filename
- */
- len = strlen(name);
- max = max_len;
- if (max > 50)
- max = 50;
- if (len > max) {
- char *slash;
- prefix = "...";
- max -= 3;
- name += len - max;
- slash = strchr(name, '/');
- if (slash)
- name = slash;
+ /*
+ * Show the diff for the 'ce' if we found the one
+ * from the desired stage.
+ */
+ diff_unmerge(&revs->diffopt, ce->name);
+ if (ce_stage(ce) != diff_unmerged_stage)
+ continue;
}
- len = max;
- /*
- * scale the add/delete
- */
- max = max_change;
- if (max + len > 70)
- max = 70 - len;
-
- if (data->files[i]->is_binary) {
- printf(" %s%-*s | Bin\n", prefix, len, name);
- goto free_diffstat_file;
- }
- else if (data->files[i]->is_unmerged) {
- printf(" %s%-*s | Unmerged\n", prefix, len, name);
- goto free_diffstat_file;
- }
- else if (!data->files[i]->is_renamed &&
- (added + deleted == 0)) {
- total_files--;
- goto free_diffstat_file;
+ if (lstat(ce->name, &st) < 0) {
+ if (errno != ENOENT && errno != ENOTDIR) {
+ perror(ce->name);
+ continue;
+ }
+ if (silent_on_removed)
+ continue;
+ diff_addremove(&revs->diffopt, '-', ntohl(ce->ce_mode),
+ ce->sha1, ce->name, NULL);
+ continue;
}
+ changed = ce_match_stat(ce, &st, 0);
+ if (!changed && !revs->diffopt.find_copies_harder)
+ continue;
+ oldmode = ntohl(ce->ce_mode);
- add = added;
- del = deleted;
- total = add + del;
- adds += add;
- dels += del;
+ newmode = canon_mode(st.st_mode);
+ if (!trust_executable_bit &&
+ S_ISREG(newmode) && S_ISREG(oldmode) &&
+ ((newmode ^ oldmode) == 0111))
+ newmode = oldmode;
+ diff_change(&revs->diffopt, oldmode, newmode,
+ ce->sha1, (changed ? null_sha1 : ce->sha1),
+ ce->name, NULL);
- if (max_change > 0) {
- total = (total * max + max_change / 2) / max_change;
- add = (add * max + max_change / 2) / max_change;
- del = total - add;
- }
- printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
- len, name, added + deleted,
- add, pluses, del, minuses);
- free_diffstat_file:
- free(data->files[i]->name);
- free(data->files[i]);
}
- free(data->files);
- printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
- total_files, adds, dels);
-}
-
-#define FIRST_FEW_BYTES 8000
-static int mmfile_is_binary(mmfile_t *mf)
-{
- long sz = mf->size;
- if (FIRST_FEW_BYTES < sz)
- sz = FIRST_FEW_BYTES;
- if (memchr(mf->ptr, 0, sz))
- return 1;
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
return 0;
}
-static void builtin_diff(const char *name_a,
- const char *name_b,
- struct diff_filespec *one,
- struct diff_filespec *two,
- const char *xfrm_msg,
- int complete_rewrite)
-{
- mmfile_t mf1, mf2;
- const char *lbl[2];
- char *a_one, *b_two;
-
- a_one = quote_two("a/", name_a);
- b_two = quote_two("b/", name_b);
- lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
- lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
- printf("diff --git %s %s\n", a_one, b_two);
- if (lbl[0][0] == '/') {
- /* /dev/null */
- printf("new file mode %06o\n", two->mode);
- if (xfrm_msg && xfrm_msg[0])
- puts(xfrm_msg);
- }
- else if (lbl[1][0] == '/') {
- printf("deleted file mode %06o\n", one->mode);
- if (xfrm_msg && xfrm_msg[0])
- puts(xfrm_msg);
- }
- else {
- if (one->mode != two->mode) {
- printf("old mode %06o\n", one->mode);
- printf("new mode %06o\n", two->mode);
- }
- if (xfrm_msg && xfrm_msg[0])
- puts(xfrm_msg);
- /*
- * we do not run diff between different kind
- * of objects.
- */
- if ((one->mode ^ two->mode) & S_IFMT)
- goto free_ab_and_return;
- if (complete_rewrite) {
- emit_rewrite_diff(name_a, name_b, one, two);
- goto free_ab_and_return;
- }
- }
-
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
- die("unable to read files to diff");
-
- if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
- printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
- else {
- /* Crazy xdl interfaces.. */
- const char *diffopts = getenv("GIT_DIFF_OPTS");
- xpparam_t xpp;
- xdemitconf_t xecfg;
- xdemitcb_t ecb;
- struct emit_callback ecbdata;
-
- ecbdata.label_path = lbl;
- xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 3;
- xecfg.flags = XDL_EMIT_FUNCNAMES;
- if (!diffopts)
- ;
- else if (!strncmp(diffopts, "--unified=", 10))
- xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
- else if (!strncmp(diffopts, "-u", 2))
- xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
- ecb.outf = fn_out;
- ecb.priv = &ecbdata;
- xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
- }
-
- free_ab_and_return:
- free(a_one);
- free(b_two);
- return;
-}
-
-static void builtin_diffstat(const char *name_a, const char *name_b,
- struct diff_filespec *one,
- struct diff_filespec *two,
- struct diffstat_t *diffstat)
-{
- mmfile_t mf1, mf2;
- struct diffstat_file *data;
-
- data = diffstat_add(diffstat, name_a, name_b);
-
- if (!one || !two) {
- data->is_unmerged = 1;
- return;
- }
-
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
- die("unable to read files to diff");
-
- if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
- data->is_binary = 1;
- else {
- /* Crazy xdl interfaces.. */
- xpparam_t xpp;
- xdemitconf_t xecfg;
- xdemitcb_t ecb;
-
- xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = 0;
- xecfg.flags = 0;
- ecb.outf = xdiff_outf;
- ecb.priv = diffstat;
- xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
- }
-}
-
-struct diff_filespec *alloc_filespec(const char *path)
-{
- int namelen = strlen(path);
- struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
-
- memset(spec, 0, sizeof(*spec));
- spec->path = (char *)(spec + 1);
- memcpy(spec->path, path, namelen+1);
- return spec;
-}
-
-void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
- unsigned short mode)
-{
- if (mode) {
- spec->mode = canon_mode(mode);
- memcpy(spec->sha1, sha1, 20);
- spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
- }
-}
-
/*
- * Given a name and sha1 pair, if the dircache tells us the file in
- * the work tree has that object contents, return true, so that
- * prepare_temp_file() does not have to inflate and extract.
+ * diff-index
*/
-static int work_tree_matches(const char *name, const unsigned char *sha1)
-{
- struct cache_entry *ce;
- struct stat st;
- int pos, len;
-
- /* We do not read the cache ourselves here, because the
- * benchmark with my previous version that always reads cache
- * shows that it makes things worse for diff-tree comparing
- * two linux-2.6 kernel trees in an already checked out work
- * tree. This is because most diff-tree comparisons deal with
- * only a small number of files, while reading the cache is
- * expensive for a large project, and its cost outweighs the
- * savings we get by not inflating the object to a temporary
- * file. Practically, this code only helps when we are used
- * by diff-cache --cached, which does read the cache before
- * calling us.
- */
- if (!active_cache)
- return 0;
- len = strlen(name);
- pos = cache_name_pos(name, len);
- if (pos < 0)
- return 0;
- ce = active_cache[pos];
- if ((lstat(name, &st) < 0) ||
- !S_ISREG(st.st_mode) || /* careful! */
- ce_match_stat(ce, &st, 0) ||
- memcmp(sha1, ce->sha1, 20))
- return 0;
- /* we return 1 only when we can stat, it is a regular file,
- * stat information matches, and sha1 recorded in the cache
- * matches. I.e. we know the file in the work tree really is
- * the same as the <name, sha1> pair.
- */
- return 1;
-}
-
-static struct sha1_size_cache {
- unsigned char sha1[20];
- unsigned long size;
-} **sha1_size_cache;
-static int sha1_size_cache_nr, sha1_size_cache_alloc;
-
-static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
- int find_only,
- unsigned long size)
+/* A file entry went away or appeared */
+static void diff_index_show_file(struct rev_info *revs,
+ const char *prefix,
+ struct cache_entry *ce,
+ unsigned char *sha1, unsigned int mode)
{
- int first, last;
- struct sha1_size_cache *e;
-
- first = 0;
- last = sha1_size_cache_nr;
- while (last > first) {
- int cmp, next = (last + first) >> 1;
- e = sha1_size_cache[next];
- cmp = memcmp(e->sha1, sha1, 20);
- if (!cmp)
- return e;
- if (cmp < 0) {
- last = next;
- continue;
- }
- first = next+1;
- }
- /* not found */
- if (find_only)
- return NULL;
- /* insert to make it at "first" */
- if (sha1_size_cache_alloc <= sha1_size_cache_nr) {
- sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);
- sha1_size_cache = xrealloc(sha1_size_cache,
- sha1_size_cache_alloc *
- sizeof(*sha1_size_cache));
- }
- sha1_size_cache_nr++;
- if (first < sha1_size_cache_nr)
- memmove(sha1_size_cache + first + 1, sha1_size_cache + first,
- (sha1_size_cache_nr - first - 1) *
- sizeof(*sha1_size_cache));
- e = xmalloc(sizeof(struct sha1_size_cache));
- sha1_size_cache[first] = e;
- memcpy(e->sha1, sha1, 20);
- e->size = size;
- return e;
+ diff_addremove(&revs->diffopt, prefix[0], ntohl(mode),
+ sha1, ce->name, NULL);
}
-/*
- * While doing rename detection and pickaxe operation, we may need to
- * grab the data for the blob (or file) for our own in-core comparison.
- * diff_filespec has data and size fields for this purpose.
- */
-int diff_populate_filespec(struct diff_filespec *s, int size_only)
+static int get_stat_data(struct cache_entry *ce,
+ unsigned char **sha1p,
+ unsigned int *modep,
+ int cached, int match_missing)
{
- int err = 0;
- if (!DIFF_FILE_VALID(s))
- die("internal error: asking to populate invalid file.");
- if (S_ISDIR(s->mode))
- return -1;
-
- if (!use_size_cache)
- size_only = 0;
+ unsigned char *sha1 = ce->sha1;
+ unsigned int mode = ce->ce_mode;
- if (s->data)
- return err;
- if (!s->sha1_valid ||
- work_tree_matches(s->path, s->sha1)) {
+ if (!cached) {
+ static unsigned char no_sha1[20];
+ int changed;
struct stat st;
- int fd;
- if (lstat(s->path, &st) < 0) {
- if (errno == ENOENT) {
- err_empty:
- err = -1;
- empty:
- s->data = "";
- s->size = 0;
- return err;
- }
- }
- s->size = st.st_size;
- if (!s->size)
- goto empty;
- if (size_only)
- return 0;
- if (S_ISLNK(st.st_mode)) {
- int ret;
- s->data = xmalloc(s->size);
- s->should_free = 1;
- ret = readlink(s->path, s->data, s->size);
- if (ret < 0) {
- free(s->data);
- goto err_empty;
- }
- return 0;
- }
- fd = open(s->path, O_RDONLY);
- if (fd < 0)
- goto err_empty;
- s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
- close(fd);
- if (s->data == MAP_FAILED)
- goto err_empty;
- s->should_munmap = 1;
- }
- else {
- char type[20];
- struct sha1_size_cache *e;
-
- if (size_only) {
- e = locate_size_cache(s->sha1, 1, 0);
- if (e) {
- s->size = e->size;
+ if (lstat(ce->name, &st) < 0) {
+ if (errno == ENOENT && match_missing) {
+ *sha1p = sha1;
+ *modep = mode;
return 0;
}
- if (!sha1_object_info(s->sha1, type, &s->size))
- locate_size_cache(s->sha1, 0, s->size);
- }
- else {
- s->data = read_sha1_file(s->sha1, type, &s->size);
- s->should_free = 1;
- }
- }
- return 0;
-}
-
-void diff_free_filespec_data(struct diff_filespec *s)
-{
- if (s->should_free)
- free(s->data);
- else if (s->should_munmap)
- munmap(s->data, s->size);
- s->should_free = s->should_munmap = 0;
- s->data = NULL;
- free(s->cnt_data);
- s->cnt_data = NULL;
-}
-
-static void prep_temp_blob(struct diff_tempfile *temp,
- void *blob,
- unsigned long size,
- const unsigned char *sha1,
- int mode)
-{
- int fd;
-
- fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX");
- if (fd < 0)
- die("unable to create temp-file");
- if (write(fd, blob, size) != size)
- die("unable to write temp-file");
- close(fd);
- temp->name = temp->tmp_path;
- strcpy(temp->hex, sha1_to_hex(sha1));
- temp->hex[40] = 0;
- sprintf(temp->mode, "%06o", mode);
-}
-
-static void prepare_temp_file(const char *name,
- struct diff_tempfile *temp,
- struct diff_filespec *one)
-{
- if (!DIFF_FILE_VALID(one)) {
- not_a_valid_file:
- /* A '-' entry produces this for file-2, and
- * a '+' entry produces this for file-1.
- */
- temp->name = "/dev/null";
- strcpy(temp->hex, ".");
- strcpy(temp->mode, ".");
- return;
- }
-
- if (!one->sha1_valid ||
- work_tree_matches(name, one->sha1)) {
- struct stat st;
- if (lstat(name, &st) < 0) {
- if (errno == ENOENT)
- goto not_a_valid_file;
- die("stat(%s): %s", name, strerror(errno));
- }
- if (S_ISLNK(st.st_mode)) {
- int ret;
- char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
- if (sizeof(buf) <= st.st_size)
- die("symlink too long: %s", name);
- ret = readlink(name, buf, st.st_size);
- if (ret < 0)
- die("readlink(%s)", name);
- prep_temp_blob(temp, buf, st.st_size,
- (one->sha1_valid ?
- one->sha1 : null_sha1),
- (one->sha1_valid ?
- one->mode : S_IFLNK));
+ return -1;
}
- else {
- /* we can borrow from the file in the work tree */
- temp->name = name;
- if (!one->sha1_valid)
- strcpy(temp->hex, sha1_to_hex(null_sha1));
- else
- strcpy(temp->hex, sha1_to_hex(one->sha1));
- /* Even though we may sometimes borrow the
- * contents from the work tree, we always want
- * one->mode. mode is trustworthy even when
- * !(one->sha1_valid), as long as
- * DIFF_FILE_VALID(one).
- */
- sprintf(temp->mode, "%06o", one->mode);
+ changed = ce_match_stat(ce, &st, 0);
+ if (changed) {
+ mode = create_ce_mode(st.st_mode);
+ if (!trust_executable_bit && S_ISREG(st.st_mode))
+ mode = ce->ce_mode;
+ sha1 = no_sha1;
}
- return;
}
- else {
- if (diff_populate_filespec(one, 0))
- die("cannot read data blob for %s", one->path);
- prep_temp_blob(temp, one->data, one->size,
- one->sha1, one->mode);
- }
-}
-static void remove_tempfile(void)
-{
- int i;
-
- for (i = 0; i < 2; i++)
- if (diff_temp[i].name == diff_temp[i].tmp_path) {
- unlink(diff_temp[i].name);
- diff_temp[i].name = NULL;
- }
-}
-
-static void remove_tempfile_on_signal(int signo)
-{
- remove_tempfile();
- signal(SIGINT, SIG_DFL);
- raise(signo);
+ *sha1p = sha1;
+ *modep = mode;
+ return 0;
}
-static int spawn_prog(const char *pgm, const char **arg)
+static void show_new_file(struct rev_info *revs,
+ struct cache_entry *new,
+ int cached, int match_missing)
{
- pid_t pid;
- int status;
-
- fflush(NULL);
- pid = fork();
- if (pid < 0)
- die("unable to fork");
- if (!pid) {
- execvp(pgm, (char *const*) arg);
- exit(255);
- }
-
- while (waitpid(pid, &status, 0) < 0) {
- if (errno == EINTR)
- continue;
- return -1;
- }
+ unsigned char *sha1;
+ unsigned int mode;
- /* Earlier we did not check the exit status because
- * diff exits non-zero if files are different, and
- * we are not interested in knowing that. It was a
- * mistake which made it harder to quit a diff-*
- * session that uses the git-apply-patch-script as
- * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
- * should also exit non-zero only when it wants to
- * abort the entire diff-* session.
+ /* New file in the index: it might actually be different in
+ * the working copy.
*/
- if (WIFEXITED(status) && !WEXITSTATUS(status))
- return 0;
- return -1;
-}
-
-/* An external diff command takes:
- *
- * diff-cmd name infile1 infile1-sha1 infile1-mode \
- * infile2 infile2-sha1 infile2-mode [ rename-to ]
- *
- */
-static void run_external_diff(const char *pgm,
- const char *name,
- const char *other,
- struct diff_filespec *one,
- struct diff_filespec *two,
- const char *xfrm_msg,
- int complete_rewrite)
-{
- const char *spawn_arg[10];
- struct diff_tempfile *temp = diff_temp;
- int retval;
- static int atexit_asked = 0;
- const char *othername;
- const char **arg = &spawn_arg[0];
-
- othername = (other? other : name);
- if (one && two) {
- prepare_temp_file(name, &temp[0], one);
- prepare_temp_file(othername, &temp[1], two);
- if (! atexit_asked &&
- (temp[0].name == temp[0].tmp_path ||
- temp[1].name == temp[1].tmp_path)) {
- atexit_asked = 1;
- atexit(remove_tempfile);
- }
- signal(SIGINT, remove_tempfile_on_signal);
- }
-
- if (one && two) {
- *arg++ = pgm;
- *arg++ = name;
- *arg++ = temp[0].name;
- *arg++ = temp[0].hex;
- *arg++ = temp[0].mode;
- *arg++ = temp[1].name;
- *arg++ = temp[1].hex;
- *arg++ = temp[1].mode;
- if (other) {
- *arg++ = other;
- *arg++ = xfrm_msg;
- }
- } else {
- *arg++ = pgm;
- *arg++ = name;
- }
- *arg = NULL;
- retval = spawn_prog(pgm, spawn_arg);
- remove_tempfile();
- if (retval) {
- fprintf(stderr, "external diff died, stopping at %s.\n", name);
- exit(1);
- }
-}
-
-static void run_diff_cmd(const char *pgm,
- const char *name,
- const char *other,
- struct diff_filespec *one,
- struct diff_filespec *two,
- const char *xfrm_msg,
- int complete_rewrite)
-{
- if (pgm) {
- run_external_diff(pgm, name, other, one, two, xfrm_msg,
- complete_rewrite);
+ if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
return;
- }
- if (one && two)
- builtin_diff(name, other ? other : name,
- one, two, xfrm_msg, complete_rewrite);
- else
- printf("* Unmerged path %s\n", name);
-}
-static void diff_fill_sha1_info(struct diff_filespec *one)
-{
- if (DIFF_FILE_VALID(one)) {
- if (!one->sha1_valid) {
- struct stat st;
- if (lstat(one->path, &st) < 0)
- die("stat %s", one->path);
- if (index_path(one->sha1, one->path, &st, 0))
- die("cannot hash %s\n", one->path);
- }
- }
- else
- memset(one->sha1, 0, 20);
+ diff_index_show_file(revs, "+", new, sha1, mode);
}
-static void run_diff(struct diff_filepair *p, struct diff_options *o)
+static int show_modified(struct rev_info *revs,
+ struct cache_entry *old,
+ struct cache_entry *new,
+ int report_missing,
+ int cached, int match_missing)
{
- const char *pgm = external_diff();
- char msg[PATH_MAX*2+300], *xfrm_msg;
- struct diff_filespec *one;
- struct diff_filespec *two;
- const char *name;
- const char *other;
- char *name_munged, *other_munged;
- int complete_rewrite = 0;
- int len;
-
- if (DIFF_PAIR_UNMERGED(p)) {
- /* unmerged */
- run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
- return;
- }
-
- name = p->one->path;
- other = (strcmp(name, p->two->path) ? p->two->path : NULL);
- name_munged = quote_one(name);
- other_munged = quote_one(other);
- one = p->one; two = p->two;
-
- diff_fill_sha1_info(one);
- diff_fill_sha1_info(two);
-
- len = 0;
- switch (p->status) {
- case DIFF_STATUS_COPIED:
- len += snprintf(msg + len, sizeof(msg) - len,
- "similarity index %d%%\n"
- "copy from %s\n"
- "copy to %s\n",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name_munged, other_munged);
- break;
- case DIFF_STATUS_RENAMED:
- len += snprintf(msg + len, sizeof(msg) - len,
- "similarity index %d%%\n"
- "rename from %s\n"
- "rename to %s\n",
- (int)(0.5 + p->score * 100.0/MAX_SCORE),
- name_munged, other_munged);
- break;
- case DIFF_STATUS_MODIFIED:
- if (p->score) {
- len += snprintf(msg + len, sizeof(msg) - len,
- "dissimilarity index %d%%\n",
- (int)(0.5 + p->score *
- 100.0/MAX_SCORE));
- complete_rewrite = 1;
- break;
- }
- /* fallthru */
- default:
- /* nothing */
- ;
- }
-
- if (memcmp(one->sha1, two->sha1, 20)) {
- char one_sha1[41];
- int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
- memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
-
- len += snprintf(msg + len, sizeof(msg) - len,
- "index %.*s..%.*s",
- abbrev, one_sha1, abbrev,
- sha1_to_hex(two->sha1));
- if (one->mode == two->mode)
- len += snprintf(msg + len, sizeof(msg) - len,
- " %06o", one->mode);
- len += snprintf(msg + len, sizeof(msg) - len, "\n");
- }
-
- if (len)
- msg[--len] = 0;
- xfrm_msg = len ? msg : NULL;
+ unsigned int mode, oldmode;
+ unsigned char *sha1;
- if (!pgm &&
- DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
- (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
- /* a filepair that changes between file and symlink
- * needs to be split into deletion and creation.
- */
- struct diff_filespec *null = alloc_filespec(two->path);
- run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
- free(null);
- null = alloc_filespec(one->path);
- run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
- free(null);
- }
- else
- run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
- complete_rewrite);
-
- free(name_munged);
- free(other_munged);
-}
-
-static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
- struct diffstat_t *diffstat)
-{
- const char *name;
- const char *other;
-
- if (DIFF_PAIR_UNMERGED(p)) {
- /* unmerged */
- builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat);
- return;
- }
-
- name = p->one->path;
- other = (strcmp(name, p->two->path) ? p->two->path : NULL);
-
- diff_fill_sha1_info(p->one);
- diff_fill_sha1_info(p->two);
-
- builtin_diffstat(name, other, p->one, p->two, diffstat);
-}
-
-void diff_setup(struct diff_options *options)
-{
- memset(options, 0, sizeof(*options));
- options->output_format = DIFF_FORMAT_RAW;
- options->line_termination = '\n';
- options->break_opt = -1;
- options->rename_limit = -1;
-
- options->change = diff_change;
- options->add_remove = diff_addremove;
-}
-
-int diff_setup_done(struct diff_options *options)
-{
- if ((options->find_copies_harder &&
- options->detect_rename != DIFF_DETECT_COPY) ||
- (0 <= options->rename_limit && !options->detect_rename))
+ if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
+ if (report_missing)
+ diff_index_show_file(revs, "-", old,
+ old->sha1, old->ce_mode);
return -1;
-
- /*
- * These cases always need recursive; we do not drop caller-supplied
- * recursive bits for other formats here.
- */
- if ((options->output_format == DIFF_FORMAT_PATCH) ||
- (options->output_format == DIFF_FORMAT_DIFFSTAT))
- options->recursive = 1;
-
- if (options->detect_rename && options->rename_limit < 0)
- options->rename_limit = diff_rename_limit_default;
- if (options->setup & DIFF_SETUP_USE_CACHE) {
- if (!active_cache)
- /* read-cache does not die even when it fails
- * so it is safe for us to do this here. Also
- * it does not smudge active_cache or active_nr
- * when it fails, so we do not have to worry about
- * cleaning it up ourselves either.
- */
- read_cache();
}
- if (options->setup & DIFF_SETUP_USE_SIZE_CACHE)
- use_size_cache = 1;
- if (options->abbrev <= 0 || 40 < options->abbrev)
- options->abbrev = 40; /* full */
-
- return 0;
-}
-int diff_opt_parse(struct diff_options *options, const char **av, int ac)
-{
- const char *arg = av[0];
- if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
- options->output_format = DIFF_FORMAT_PATCH;
- else if (!strcmp(arg, "--patch-with-raw")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_raw = 1;
- }
- else if (!strcmp(arg, "--stat"))
- options->output_format = DIFF_FORMAT_DIFFSTAT;
- else if (!strcmp(arg, "--patch-with-stat")) {
- options->output_format = DIFF_FORMAT_PATCH;
- options->with_stat = 1;
- }
- else if (!strcmp(arg, "-z"))
- options->line_termination = 0;
- else if (!strncmp(arg, "-l", 2))
- options->rename_limit = strtoul(arg+2, NULL, 10);
- else if (!strcmp(arg, "--full-index"))
- options->full_index = 1;
- else if (!strcmp(arg, "--name-only"))
- options->output_format = DIFF_FORMAT_NAME;
- else if (!strcmp(arg, "--name-status"))
- options->output_format = DIFF_FORMAT_NAME_STATUS;
- else if (!strcmp(arg, "-R"))
- options->reverse_diff = 1;
- else if (!strncmp(arg, "-S", 2))
- options->pickaxe = arg + 2;
- else if (!strcmp(arg, "-s"))
- options->output_format = DIFF_FORMAT_NO_OUTPUT;
- else if (!strncmp(arg, "-O", 2))
- options->orderfile = arg + 2;
- else if (!strncmp(arg, "--diff-filter=", 14))
- options->filter = arg + 14;
- else if (!strcmp(arg, "--pickaxe-all"))
- options->pickaxe_opts = DIFF_PICKAXE_ALL;
- else if (!strcmp(arg, "--pickaxe-regex"))
- options->pickaxe_opts = DIFF_PICKAXE_REGEX;
- else if (!strncmp(arg, "-B", 2)) {
- if ((options->break_opt =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- }
- else if (!strncmp(arg, "-M", 2)) {
- if ((options->rename_score =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- options->detect_rename = DIFF_DETECT_RENAME;
- }
- else if (!strncmp(arg, "-C", 2)) {
- if ((options->rename_score =
- diff_scoreopt_parse(arg)) == -1)
- return -1;
- options->detect_rename = DIFF_DETECT_COPY;
- }
- else if (!strcmp(arg, "--find-copies-harder"))
- options->find_copies_harder = 1;
- else if (!strcmp(arg, "--abbrev"))
- options->abbrev = DEFAULT_ABBREV;
- else if (!strncmp(arg, "--abbrev=", 9)) {
- options->abbrev = strtoul(arg + 9, NULL, 10);
- if (options->abbrev < MINIMUM_ABBREV)
- options->abbrev = MINIMUM_ABBREV;
- else if (40 < options->abbrev)
- options->abbrev = 40;
- }
- else
+ oldmode = old->ce_mode;
+ if (mode == oldmode && !memcmp(sha1, old->sha1, 20) &&
+ !revs->diffopt.find_copies_harder)
return 0;
- return 1;
-}
-
-static int parse_num(const char **cp_p)
-{
- unsigned long num, scale;
- int ch, dot;
- const char *cp = *cp_p;
-
- num = 0;
- scale = 1;
- dot = 0;
- for(;;) {
- ch = *cp;
- if ( !dot && ch == '.' ) {
- scale = 1;
- dot = 1;
- } else if ( ch == '%' ) {
- scale = dot ? scale*100 : 100;
- cp++; /* % is always at the end */
- break;
- } else if ( ch >= '0' && ch <= '9' ) {
- if ( scale < 100000 ) {
- scale *= 10;
- num = (num*10) + (ch-'0');
- }
- } else {
- break;
- }
- cp++;
- }
- *cp_p = cp;
-
- /* user says num divided by scale and we say internally that
- * is MAX_SCORE * num / scale.
- */
- return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);
-}
-
-int diff_scoreopt_parse(const char *opt)
-{
- int opt1, opt2, cmd;
-
- if (*opt++ != '-')
- return -1;
- cmd = *opt++;
- if (cmd != 'M' && cmd != 'C' && cmd != 'B')
- return -1; /* that is not a -M, -C nor -B option */
-
- opt1 = parse_num(&opt);
- if (cmd != 'B')
- opt2 = 0;
- else {
- if (*opt == 0)
- opt2 = 0;
- else if (*opt != '/')
- return -1; /* we expect -B80/99 or -B80 */
- else {
- opt++;
- opt2 = parse_num(&opt);
- }
- }
- if (*opt != 0)
- return -1;
- return opt1 | (opt2 << 16);
-}
-
-struct diff_queue_struct diff_queued_diff;
-
-void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
-{
- if (queue->alloc <= queue->nr) {
- queue->alloc = alloc_nr(queue->alloc);
- queue->queue = xrealloc(queue->queue,
- sizeof(dp) * queue->alloc);
- }
- queue->queue[queue->nr++] = dp;
-}
-
-struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
- struct diff_filespec *one,
- struct diff_filespec *two)
-{
- struct diff_filepair *dp = xmalloc(sizeof(*dp));
- dp->one = one;
- dp->two = two;
- dp->score = 0;
- dp->status = 0;
- dp->source_stays = 0;
- dp->broken_pair = 0;
- if (queue)
- diff_q(queue, dp);
- return dp;
-}
-
-void diff_free_filepair(struct diff_filepair *p)
-{
- diff_free_filespec_data(p->one);
- diff_free_filespec_data(p->two);
- free(p->one);
- free(p->two);
- free(p);
-}
-
-/* This is different from find_unique_abbrev() in that
- * it stuffs the result with dots for alignment.
- */
-const char *diff_unique_abbrev(const unsigned char *sha1, int len)
-{
- int abblen;
- const char *abbrev;
- if (len == 40)
- return sha1_to_hex(sha1);
-
- abbrev = find_unique_abbrev(sha1, len);
- if (!abbrev)
- return sha1_to_hex(sha1);
- abblen = strlen(abbrev);
- if (abblen < 37) {
- static char hex[41];
- if (len < abblen && abblen <= len + 2)
- sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
- else
- sprintf(hex, "%s...", abbrev);
- return hex;
- }
- return sha1_to_hex(sha1);
-}
-
-static void diff_flush_raw(struct diff_filepair *p,
- int line_termination,
- int inter_name_termination,
- struct diff_options *options,
- int output_format)
-{
- int two_paths;
- char status[10];
- int abbrev = options->abbrev;
- const char *path_one, *path_two;
-
- path_one = p->one->path;
- path_two = p->two->path;
- if (line_termination) {
- path_one = quote_one(path_one);
- path_two = quote_one(path_two);
- }
-
- if (p->score)
- sprintf(status, "%c%03d", p->status,
- (int)(0.5 + p->score * 100.0/MAX_SCORE));
- else {
- status[0] = p->status;
- status[1] = 0;
- }
- switch (p->status) {
- case DIFF_STATUS_COPIED:
- case DIFF_STATUS_RENAMED:
- two_paths = 1;
- break;
- case DIFF_STATUS_ADDED:
- case DIFF_STATUS_DELETED:
- two_paths = 0;
- break;
- default:
- two_paths = 0;
- break;
- }
- if (output_format != DIFF_FORMAT_NAME_STATUS) {
- printf(":%06o %06o %s ",
- p->one->mode, p->two->mode,
- diff_unique_abbrev(p->one->sha1, abbrev));
- printf("%s ",
- diff_unique_abbrev(p->two->sha1, abbrev));
- }
- printf("%s%c%s", status, inter_name_termination, path_one);
- if (two_paths)
- printf("%c%s", inter_name_termination, path_two);
- putchar(line_termination);
- if (path_one != p->one->path)
- free((void*)path_one);
- if (path_two != p->two->path)
- free((void*)path_two);
-}
-
-static void diff_flush_name(struct diff_filepair *p,
- int inter_name_termination,
- int line_termination)
-{
- char *path = p->two->path;
-
- if (line_termination)
- path = quote_one(p->two->path);
- else
- path = p->two->path;
- printf("%s%c", path, line_termination);
- if (p->two->path != path)
- free(path);
-}
-
-int diff_unmodified_pair(struct diff_filepair *p)
-{
- /* This function is written stricter than necessary to support
- * the currently implemented transformers, but the idea is to
- * let transformers to produce diff_filepairs any way they want,
- * and filter and clean them up here before producing the output.
- */
- struct diff_filespec *one, *two;
- if (DIFF_PAIR_UNMERGED(p))
- return 0; /* unmerged is interesting */
+ mode = ntohl(mode);
+ oldmode = ntohl(oldmode);
- one = p->one;
- two = p->two;
-
- /* deletion, addition, mode or type change
- * and rename are all interesting.
- */
- if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
- DIFF_PAIR_MODE_CHANGED(p) ||
- strcmp(one->path, two->path))
- return 0;
-
- /* both are valid and point at the same path. that is, we are
- * dealing with a change.
- */
- if (one->sha1_valid && two->sha1_valid &&
- !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
- return 1; /* no change */
- if (!one->sha1_valid && !two->sha1_valid)
- return 1; /* both look at the same file on the filesystem. */
+ diff_change(&revs->diffopt, oldmode, mode,
+ old->sha1, sha1, old->name, NULL);
return 0;
}
-static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
+static int diff_cache(struct rev_info *revs,
+ struct cache_entry **ac, int entries,
+ const char **pathspec,
+ int cached, int match_missing)
{
- if (diff_unmodified_pair(p))
- return;
+ while (entries) {
+ struct cache_entry *ce = *ac;
+ int same = (entries > 1) && ce_same_name(ce, ac[1]);
- if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
- (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
- return; /* no tree diffs in patch format */
+ if (!ce_path_match(ce, pathspec))
+ goto skip_entry;
- run_diff(p, o);
-}
-
-static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
- struct diffstat_t *diffstat)
-{
- if (diff_unmodified_pair(p))
- return;
-
- if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
- (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
- return; /* no tree diffs in patch format */
-
- run_diffstat(p, o, diffstat);
-}
-
-int diff_queue_is_empty(void)
-{
- struct diff_queue_struct *q = &diff_queued_diff;
- int i;
- for (i = 0; i < q->nr; i++)
- if (!diff_unmodified_pair(q->queue[i]))
- return 0;
- return 1;
-}
-
-#if DIFF_DEBUG
-void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
-{
- fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
- x, one ? one : "",
- s->path,
- DIFF_FILE_VALID(s) ? "valid" : "invalid",
- s->mode,
- s->sha1_valid ? sha1_to_hex(s->sha1) : "");
- fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
- x, one ? one : "",
- s->size, s->xfrm_flags);
-}
-
-void diff_debug_filepair(const struct diff_filepair *p, int i)
-{
- diff_debug_filespec(p->one, i, "one");
- diff_debug_filespec(p->two, i, "two");
- fprintf(stderr, "score %d, status %c stays %d broken %d\n",
- p->score, p->status ? p->status : '?',
- p->source_stays, p->broken_pair);
-}
-
-void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
-{
- int i;
- if (msg)
- fprintf(stderr, "%s\n", msg);
- fprintf(stderr, "q->nr = %d\n", q->nr);
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- diff_debug_filepair(p, i);
- }
-}
-#endif
-
-static void diff_resolve_rename_copy(void)
-{
- int i, j;
- struct diff_filepair *p, *pp;
- struct diff_queue_struct *q = &diff_queued_diff;
-
- diff_debug_queue("resolve-rename-copy", q);
-
- for (i = 0; i < q->nr; i++) {
- p = q->queue[i];
- p->status = 0; /* undecided */
- if (DIFF_PAIR_UNMERGED(p))
- p->status = DIFF_STATUS_UNMERGED;
- else if (!DIFF_FILE_VALID(p->one))
- p->status = DIFF_STATUS_ADDED;
- else if (!DIFF_FILE_VALID(p->two))
- p->status = DIFF_STATUS_DELETED;
- else if (DIFF_PAIR_TYPE_CHANGED(p))
- p->status = DIFF_STATUS_TYPE_CHANGED;
-
- /* from this point on, we are dealing with a pair
- * whose both sides are valid and of the same type, i.e.
- * either in-place edit or rename/copy edit.
- */
- else if (DIFF_PAIR_RENAME(p)) {
- if (p->source_stays) {
- p->status = DIFF_STATUS_COPIED;
- continue;
+ switch (ce_stage(ce)) {
+ case 0:
+ /* No stage 1 entry? That means it's a new file */
+ if (!same) {
+ show_new_file(revs, ce, cached, match_missing);
+ break;
}
- /* See if there is some other filepair that
- * copies from the same source as us. If so
- * we are a copy. Otherwise we are either a
- * copy if the path stays, or a rename if it
- * does not, but we already handled "stays" case.
+ /* Show difference between old and new */
+ show_modified(revs,ac[1], ce, 1,
+ cached, match_missing);
+ break;
+ case 1:
+ /* No stage 3 (merge) entry?
+ * That means it's been deleted.
*/
- for (j = i + 1; j < q->nr; j++) {
- pp = q->queue[j];
- if (strcmp(pp->one->path, p->one->path))
- continue; /* not us */
- if (!DIFF_PAIR_RENAME(pp))
- continue; /* not a rename/copy */
- /* pp is a rename/copy from the same source */
- p->status = DIFF_STATUS_COPIED;
+ if (!same) {
+ diff_index_show_file(revs, "-", ce,
+ ce->sha1, ce->ce_mode);
break;
}
- if (!p->status)
- p->status = DIFF_STATUS_RENAMED;
- }
- else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
- p->one->mode != p->two->mode)
- p->status = DIFF_STATUS_MODIFIED;
- else {
- /* This is a "no-change" entry and should not
- * happen anymore, but prepare for broken callers.
+ /* We come here with ce pointing at stage 1
+ * (original tree) and ac[1] pointing at stage
+ * 3 (unmerged). show-modified with
+ * report-missing set to false does not say the
+ * file is deleted but reports true if work
+ * tree does not have it, in which case we
+ * fall through to report the unmerged state.
+ * Otherwise, we show the differences between
+ * the original tree and the work tree.
*/
- error("feeding unmodified %s to diffcore",
- p->one->path);
- p->status = DIFF_STATUS_UNKNOWN;
- }
- }
- diff_debug_queue("resolve-rename-copy done", q);
-}
-
-static void flush_one_pair(struct diff_filepair *p,
- int diff_output_format,
- struct diff_options *options,
- struct diffstat_t *diffstat)
-{
- int inter_name_termination = '\t';
- int line_termination = options->line_termination;
- if (!line_termination)
- inter_name_termination = 0;
-
- switch (p->status) {
- case DIFF_STATUS_UNKNOWN:
- break;
- case 0:
- die("internal error in diff-resolve-rename-copy");
- break;
- default:
- switch (diff_output_format) {
- case DIFF_FORMAT_DIFFSTAT:
- diff_flush_stat(p, options, diffstat);
- break;
- case DIFF_FORMAT_PATCH:
- diff_flush_patch(p, options);
- break;
- case DIFF_FORMAT_RAW:
- case DIFF_FORMAT_NAME_STATUS:
- diff_flush_raw(p, line_termination,
- inter_name_termination,
- options, diff_output_format);
- break;
- case DIFF_FORMAT_NAME:
- diff_flush_name(p,
- inter_name_termination,
- line_termination);
- break;
- case DIFF_FORMAT_NO_OUTPUT:
+ if (!cached &&
+ !show_modified(revs, ce, ac[1], 0,
+ cached, match_missing))
+ break;
+ /* fallthru */
+ case 3:
+ diff_unmerge(&revs->diffopt, ce->name);
break;
- }
- }
-}
-void diff_flush(struct diff_options *options)
-{
- struct diff_queue_struct *q = &diff_queued_diff;
- int i;
- int diff_output_format = options->output_format;
- struct diffstat_t *diffstat = NULL;
-
- if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) {
- diffstat = xcalloc(sizeof (struct diffstat_t), 1);
- diffstat->xm.consume = diffstat_consume;
- }
-
- if (options->with_raw) {
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
- }
- putchar(options->line_termination);
- }
- if (options->with_stat) {
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
- diffstat);
+ default:
+ die("impossible cache entry stage");
}
- show_stats(diffstat);
- free(diffstat);
- diffstat = NULL;
- putchar(options->line_termination);
- }
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- flush_one_pair(p, diff_output_format, options, diffstat);
- diff_free_filepair(p);
- }
- if (diffstat) {
- show_stats(diffstat);
- free(diffstat);
+skip_entry:
+ /*
+ * Ignore all the different stages for this file,
+ * we've handled the relevant cases now.
+ */
+ do {
+ ac++;
+ entries--;
+ } while (entries && ce_same_name(ce, ac[0]));
}
-
- free(q->queue);
- q->queue = NULL;
- q->nr = q->alloc = 0;
+ return 0;
}
-static void diffcore_apply_filter(const char *filter)
+/*
+ * This turns all merge entries into "stage 3". That guarantees that
+ * when we read in the new tree (into "stage 1"), we won't lose sight
+ * of the fact that we had unmerged entries.
+ */
+static void mark_merge_entries(void)
{
int i;
- struct diff_queue_struct *q = &diff_queued_diff;
- struct diff_queue_struct outq;
- outq.queue = NULL;
- outq.nr = outq.alloc = 0;
-
- if (!filter)
- return;
-
- if (strchr(filter, DIFF_STATUS_FILTER_AON)) {
- int found;
- for (i = found = 0; !found && i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- if (((p->status == DIFF_STATUS_MODIFIED) &&
- ((p->score &&
- strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
- (!p->score &&
- strchr(filter, DIFF_STATUS_MODIFIED)))) ||
- ((p->status != DIFF_STATUS_MODIFIED) &&
- strchr(filter, p->status)))
- found++;
- }
- if (found)
- return;
-
- /* otherwise we will clear the whole queue
- * by copying the empty outq at the end of this
- * function, but first clear the current entries
- * in the queue.
- */
- for (i = 0; i < q->nr; i++)
- diff_free_filepair(q->queue[i]);
- }
- else {
- /* Only the matching ones */
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
-
- if (((p->status == DIFF_STATUS_MODIFIED) &&
- ((p->score &&
- strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
- (!p->score &&
- strchr(filter, DIFF_STATUS_MODIFIED)))) ||
- ((p->status != DIFF_STATUS_MODIFIED) &&
- strchr(filter, p->status)))
- diff_q(&outq, p);
- else
- diff_free_filepair(p);
- }
+ for (i = 0; i < active_nr; i++) {
+ struct cache_entry *ce = active_cache[i];
+ if (!ce_stage(ce))
+ continue;
+ ce->ce_flags |= htons(CE_STAGEMASK);
}
- free(q->queue);
- *q = outq;
-}
-
-void diffcore_std(struct diff_options *options)
-{
- if (options->break_opt != -1)
- diffcore_break(options->break_opt);
- if (options->detect_rename)
- diffcore_rename(options);
- if (options->break_opt != -1)
- diffcore_merge_broken();
- if (options->pickaxe)
- diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
- if (options->orderfile)
- diffcore_order(options->orderfile);
- diff_resolve_rename_copy();
- diffcore_apply_filter(options->filter);
-}
-
-
-void diffcore_std_no_resolve(struct diff_options *options)
-{
- if (options->pickaxe)
- diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
- if (options->orderfile)
- diffcore_order(options->orderfile);
- diffcore_apply_filter(options->filter);
}
-void diff_addremove(struct diff_options *options,
- int addremove, unsigned mode,
- const unsigned char *sha1,
- const char *base, const char *path)
+int run_diff_index(struct rev_info *revs, int cached)
{
- char concatpath[PATH_MAX];
- struct diff_filespec *one, *two;
+ int ret;
+ struct object *ent;
+ struct tree *tree;
+ const char *tree_name;
+ int match_missing = 0;
- /* This may look odd, but it is a preparation for
- * feeding "there are unchanged files which should
- * not produce diffs, but when you are doing copy
- * detection you would need them, so here they are"
- * entries to the diff-core. They will be prefixed
- * with something like '=' or '*' (I haven't decided
- * which but should not make any difference).
- * Feeding the same new and old to diff_change()
- * also has the same effect.
- * Before the final output happens, they are pruned after
- * merged into rename/copy pairs as appropriate.
+ /*
+ * Backward compatibility wart - "diff-index -m" does
+ * not mean "do not ignore merges", but totally different.
*/
- if (options->reverse_diff)
- addremove = (addremove == '+' ? '-' :
- addremove == '-' ? '+' : addremove);
-
- if (!path) path = "";
- sprintf(concatpath, "%s%s", base, path);
- one = alloc_filespec(concatpath);
- two = alloc_filespec(concatpath);
-
- if (addremove != '+')
- fill_filespec(one, sha1, mode);
- if (addremove != '-')
- fill_filespec(two, sha1, mode);
+ if (!revs->ignore_merges)
+ match_missing = 1;
- diff_queue(&diff_queued_diff, one, two);
-}
-
-void diff_change(struct diff_options *options,
- unsigned old_mode, unsigned new_mode,
- const unsigned char *old_sha1,
- const unsigned char *new_sha1,
- const char *base, const char *path)
-{
- char concatpath[PATH_MAX];
- struct diff_filespec *one, *two;
-
- if (options->reverse_diff) {
- unsigned tmp;
- const unsigned char *tmp_c;
- tmp = old_mode; old_mode = new_mode; new_mode = tmp;
- tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+ if (read_cache() < 0) {
+ perror("read_cache");
+ return -1;
}
- if (!path) path = "";
- sprintf(concatpath, "%s%s", base, path);
- one = alloc_filespec(concatpath);
- two = alloc_filespec(concatpath);
- fill_filespec(one, old_sha1, old_mode);
- fill_filespec(two, new_sha1, new_mode);
-
- diff_queue(&diff_queued_diff, one, two);
-}
-
-void diff_unmerge(struct diff_options *options,
- const char *path)
-{
- struct diff_filespec *one, *two;
- one = alloc_filespec(path);
- two = alloc_filespec(path);
- diff_queue(&diff_queued_diff, one, two);
+ mark_merge_entries();
+
+ ent = revs->pending_objects->item;
+ tree_name = revs->pending_objects->name;
+ tree = parse_tree_indirect(ent->sha1);
+ if (!tree)
+ return error("bad tree object %s", tree_name);
+ if (read_tree(tree, 1, revs->prune_data))
+ return error("unable to read tree object %s", tree_name);
+ ret = diff_cache(revs, active_cache, active_nr, revs->prune_data,
+ cached, match_missing);
+ diffcore_std(&revs->diffopt);
+ diff_flush(&revs->diffopt);
+ return ret;
}
diff --git a/diff.c b/diff.c
new file mode 100644
index 0000000..6762fce
--- /dev/null
+++ b/diff.c
@@ -0,0 +1,1805 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ */
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "cache.h"
+#include "quote.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "xdiff-interface.h"
+
+static int use_size_cache;
+
+int diff_rename_limit_default = -1;
+
+int git_diff_config(const char *var, const char *value)
+{
+ if (!strcmp(var, "diff.renamelimit")) {
+ diff_rename_limit_default = git_config_int(var, value);
+ return 0;
+ }
+
+ return git_default_config(var, value);
+}
+
+static char *quote_one(const char *str)
+{
+ int needlen;
+ char *xp;
+
+ if (!str)
+ return NULL;
+ needlen = quote_c_style(str, NULL, NULL, 0);
+ if (!needlen)
+ return strdup(str);
+ xp = xmalloc(needlen + 1);
+ quote_c_style(str, xp, NULL, 0);
+ return xp;
+}
+
+static char *quote_two(const char *one, const char *two)
+{
+ int need_one = quote_c_style(one, NULL, NULL, 1);
+ int need_two = quote_c_style(two, NULL, NULL, 1);
+ char *xp;
+
+ if (need_one + need_two) {
+ if (!need_one) need_one = strlen(one);
+ if (!need_two) need_one = strlen(two);
+
+ xp = xmalloc(need_one + need_two + 3);
+ xp[0] = '"';
+ quote_c_style(one, xp + 1, NULL, 1);
+ quote_c_style(two, xp + need_one + 1, NULL, 1);
+ strcpy(xp + need_one + need_two + 1, "\"");
+ return xp;
+ }
+ need_one = strlen(one);
+ need_two = strlen(two);
+ xp = xmalloc(need_one + need_two + 1);
+ strcpy(xp, one);
+ strcpy(xp + need_one, two);
+ return xp;
+}
+
+static const char *external_diff(void)
+{
+ static const char *external_diff_cmd = NULL;
+ static int done_preparing = 0;
+
+ if (done_preparing)
+ return external_diff_cmd;
+ external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
+ done_preparing = 1;
+ return external_diff_cmd;
+}
+
+#define TEMPFILE_PATH_LEN 50
+
+static struct diff_tempfile {
+ const char *name; /* filename external diff should read from */
+ char hex[41];
+ char mode[10];
+ char tmp_path[TEMPFILE_PATH_LEN];
+} diff_temp[2];
+
+static int count_lines(const char *data, int size)
+{
+ int count, ch, completely_empty = 1, nl_just_seen = 0;
+ count = 0;
+ while (0 < size--) {
+ ch = *data++;
+ if (ch == '\n') {
+ count++;
+ nl_just_seen = 1;
+ completely_empty = 0;
+ }
+ else {
+ nl_just_seen = 0;
+ completely_empty = 0;
+ }
+ }
+ if (completely_empty)
+ return 0;
+ if (!nl_just_seen)
+ count++; /* no trailing newline */
+ return count;
+}
+
+static void print_line_count(int count)
+{
+ switch (count) {
+ case 0:
+ printf("0,0");
+ break;
+ case 1:
+ printf("1");
+ break;
+ default:
+ printf("1,%d", count);
+ break;
+ }
+}
+
+static void copy_file(int prefix, const char *data, int size)
+{
+ int ch, nl_just_seen = 1;
+ while (0 < size--) {
+ ch = *data++;
+ if (nl_just_seen)
+ putchar(prefix);
+ putchar(ch);
+ if (ch == '\n')
+ nl_just_seen = 1;
+ else
+ nl_just_seen = 0;
+ }
+ if (!nl_just_seen)
+ printf("\n\\ No newline at end of file\n");
+}
+
+static void emit_rewrite_diff(const char *name_a,
+ const char *name_b,
+ struct diff_filespec *one,
+ struct diff_filespec *two)
+{
+ int lc_a, lc_b;
+ diff_populate_filespec(one, 0);
+ diff_populate_filespec(two, 0);
+ lc_a = count_lines(one->data, one->size);
+ lc_b = count_lines(two->data, two->size);
+ printf("--- %s\n+++ %s\n@@ -", name_a, name_b);
+ print_line_count(lc_a);
+ printf(" +");
+ print_line_count(lc_b);
+ printf(" @@\n");
+ if (lc_a)
+ copy_file('-', one->data, one->size);
+ if (lc_b)
+ copy_file('+', two->data, two->size);
+}
+
+static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+{
+ if (!DIFF_FILE_VALID(one)) {
+ mf->ptr = ""; /* does not matter */
+ mf->size = 0;
+ return 0;
+ }
+ else if (diff_populate_filespec(one, 0))
+ return -1;
+ mf->ptr = one->data;
+ mf->size = one->size;
+ return 0;
+}
+
+struct emit_callback {
+ const char **label_path;
+};
+
+static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+{
+ int i;
+ struct emit_callback *ecbdata = priv;
+
+ if (ecbdata->label_path[0]) {
+ printf("--- %s\n", ecbdata->label_path[0]);
+ printf("+++ %s\n", ecbdata->label_path[1]);
+ ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
+ }
+ for (i = 0; i < nbuf; i++)
+ if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
+ return -1;
+ return 0;
+}
+
+static char *pprint_rename(const char *a, const char *b)
+{
+ const char *old = a;
+ const char *new = b;
+ char *name = NULL;
+ int pfx_length, sfx_length;
+ int len_a = strlen(a);
+ int len_b = strlen(b);
+
+ /* Find common prefix */
+ pfx_length = 0;
+ while (*old && *new && *old == *new) {
+ if (*old == '/')
+ pfx_length = old - a + 1;
+ old++;
+ new++;
+ }
+
+ /* Find common suffix */
+ old = a + len_a;
+ new = b + len_b;
+ sfx_length = 0;
+ while (a <= old && b <= new && *old == *new) {
+ if (*old == '/')
+ sfx_length = len_a - (old - a);
+ old--;
+ new--;
+ }
+
+ /*
+ * pfx{mid-a => mid-b}sfx
+ * {pfx-a => pfx-b}sfx
+ * pfx{sfx-a => sfx-b}
+ * name-a => name-b
+ */
+ if (pfx_length + sfx_length) {
+ name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
+ sprintf(name, "%.*s{%.*s => %.*s}%s",
+ pfx_length, a,
+ len_a - pfx_length - sfx_length, a + pfx_length,
+ len_b - pfx_length - sfx_length, b + pfx_length,
+ a + len_a - sfx_length);
+ }
+ else {
+ name = xmalloc(len_a + len_b + 5);
+ sprintf(name, "%s => %s", a, b);
+ }
+ return name;
+}
+
+struct diffstat_t {
+ struct xdiff_emit_state xm;
+
+ int nr;
+ int alloc;
+ struct diffstat_file {
+ char *name;
+ unsigned is_unmerged:1;
+ unsigned is_binary:1;
+ unsigned is_renamed:1;
+ unsigned int added, deleted;
+ } **files;
+};
+
+static struct diffstat_file *diffstat_add(struct diffstat_t *diffstat,
+ const char *name_a,
+ const char *name_b)
+{
+ struct diffstat_file *x;
+ x = xcalloc(sizeof (*x), 1);
+ if (diffstat->nr == diffstat->alloc) {
+ diffstat->alloc = alloc_nr(diffstat->alloc);
+ diffstat->files = xrealloc(diffstat->files,
+ diffstat->alloc * sizeof(x));
+ }
+ diffstat->files[diffstat->nr++] = x;
+ if (name_b) {
+ x->name = pprint_rename(name_a, name_b);
+ x->is_renamed = 1;
+ }
+ else
+ x->name = strdup(name_a);
+ return x;
+}
+
+static void diffstat_consume(void *priv, char *line, unsigned long len)
+{
+ struct diffstat_t *diffstat = priv;
+ struct diffstat_file *x = diffstat->files[diffstat->nr - 1];
+
+ if (line[0] == '+')
+ x->added++;
+ else if (line[0] == '-')
+ x->deleted++;
+}
+
+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct diffstat_t* data)
+{
+ char *prefix = "";
+ int i, len, add, del, total, adds = 0, dels = 0;
+ int max, max_change = 0, max_len = 0;
+ int total_files = data->nr;
+
+ if (data->nr == 0)
+ return;
+
+ for (i = 0; i < data->nr; i++) {
+ struct diffstat_file *file = data->files[i];
+
+ len = strlen(file->name);
+ if (max_len < len)
+ max_len = len;
+
+ if (file->is_binary || file->is_unmerged)
+ continue;
+ if (max_change < file->added + file->deleted)
+ max_change = file->added + file->deleted;
+ }
+
+ for (i = 0; i < data->nr; i++) {
+ char *name = data->files[i]->name;
+ int added = data->files[i]->added;
+ int deleted = data->files[i]->deleted;
+
+ if (0 < (len = quote_c_style(name, NULL, NULL, 0))) {
+ char *qname = xmalloc(len + 1);
+ quote_c_style(name, qname, NULL, 0);
+ free(name);
+ data->files[i]->name = name = qname;
+ }
+
+ /*
+ * "scale" the filename
+ */
+ len = strlen(name);
+ max = max_len;
+ if (max > 50)
+ max = 50;
+ if (len > max) {
+ char *slash;
+ prefix = "...";
+ max -= 3;
+ name += len - max;
+ slash = strchr(name, '/');
+ if (slash)
+ name = slash;
+ }
+ len = max;
+
+ /*
+ * scale the add/delete
+ */
+ max = max_change;
+ if (max + len > 70)
+ max = 70 - len;
+
+ if (data->files[i]->is_binary) {
+ printf(" %s%-*s | Bin\n", prefix, len, name);
+ goto free_diffstat_file;
+ }
+ else if (data->files[i]->is_unmerged) {
+ printf(" %s%-*s | Unmerged\n", prefix, len, name);
+ goto free_diffstat_file;
+ }
+ else if (!data->files[i]->is_renamed &&
+ (added + deleted == 0)) {
+ total_files--;
+ goto free_diffstat_file;
+ }
+
+ add = added;
+ del = deleted;
+ total = add + del;
+ adds += add;
+ dels += del;
+
+ if (max_change > 0) {
+ total = (total * max + max_change / 2) / max_change;
+ add = (add * max + max_change / 2) / max_change;
+ del = total - add;
+ }
+ printf(" %s%-*s |%5d %.*s%.*s\n", prefix,
+ len, name, added + deleted,
+ add, pluses, del, minuses);
+ free_diffstat_file:
+ free(data->files[i]->name);
+ free(data->files[i]);
+ }
+ free(data->files);
+ printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+ total_files, adds, dels);
+}
+
+#define FIRST_FEW_BYTES 8000
+static int mmfile_is_binary(mmfile_t *mf)
+{
+ long sz = mf->size;
+ if (FIRST_FEW_BYTES < sz)
+ sz = FIRST_FEW_BYTES;
+ if (memchr(mf->ptr, 0, sz))
+ return 1;
+ return 0;
+}
+
+static void builtin_diff(const char *name_a,
+ const char *name_b,
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ const char *xfrm_msg,
+ int complete_rewrite)
+{
+ mmfile_t mf1, mf2;
+ const char *lbl[2];
+ char *a_one, *b_two;
+
+ a_one = quote_two("a/", name_a);
+ b_two = quote_two("b/", name_b);
+ lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
+ lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
+ printf("diff --git %s %s\n", a_one, b_two);
+ if (lbl[0][0] == '/') {
+ /* /dev/null */
+ printf("new file mode %06o\n", two->mode);
+ if (xfrm_msg && xfrm_msg[0])
+ puts(xfrm_msg);
+ }
+ else if (lbl[1][0] == '/') {
+ printf("deleted file mode %06o\n", one->mode);
+ if (xfrm_msg && xfrm_msg[0])
+ puts(xfrm_msg);
+ }
+ else {
+ if (one->mode != two->mode) {
+ printf("old mode %06o\n", one->mode);
+ printf("new mode %06o\n", two->mode);
+ }
+ if (xfrm_msg && xfrm_msg[0])
+ puts(xfrm_msg);
+ /*
+ * we do not run diff between different kind
+ * of objects.
+ */
+ if ((one->mode ^ two->mode) & S_IFMT)
+ goto free_ab_and_return;
+ if (complete_rewrite) {
+ emit_rewrite_diff(name_a, name_b, one, two);
+ goto free_ab_and_return;
+ }
+ }
+
+ if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ die("unable to read files to diff");
+
+ if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+ printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
+ else {
+ /* Crazy xdl interfaces.. */
+ const char *diffopts = getenv("GIT_DIFF_OPTS");
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+ struct emit_callback ecbdata;
+
+ ecbdata.label_path = lbl;
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 3;
+ xecfg.flags = XDL_EMIT_FUNCNAMES;
+ if (!diffopts)
+ ;
+ else if (!strncmp(diffopts, "--unified=", 10))
+ xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
+ else if (!strncmp(diffopts, "-u", 2))
+ xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
+ ecb.outf = fn_out;
+ ecb.priv = &ecbdata;
+ xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ }
+
+ free_ab_and_return:
+ free(a_one);
+ free(b_two);
+ return;
+}
+
+static void builtin_diffstat(const char *name_a, const char *name_b,
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ struct diffstat_t *diffstat,
+ int complete_rewrite)
+{
+ mmfile_t mf1, mf2;
+ struct diffstat_file *data;
+
+ data = diffstat_add(diffstat, name_a, name_b);
+
+ if (!one || !two) {
+ data->is_unmerged = 1;
+ return;
+ }
+ if (complete_rewrite) {
+ diff_populate_filespec(one, 0);
+ diff_populate_filespec(two, 0);
+ data->deleted = count_lines(one->data, one->size);
+ data->added = count_lines(two->data, two->size);
+ return;
+ }
+ if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ die("unable to read files to diff");
+
+ if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+ data->is_binary = 1;
+ else {
+ /* Crazy xdl interfaces.. */
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 0;
+ xecfg.flags = 0;
+ ecb.outf = xdiff_outf;
+ ecb.priv = diffstat;
+ xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ }
+}
+
+struct diff_filespec *alloc_filespec(const char *path)
+{
+ int namelen = strlen(path);
+ struct diff_filespec *spec = xmalloc(sizeof(*spec) + namelen + 1);
+
+ memset(spec, 0, sizeof(*spec));
+ spec->path = (char *)(spec + 1);
+ memcpy(spec->path, path, namelen+1);
+ return spec;
+}
+
+void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
+ unsigned short mode)
+{
+ if (mode) {
+ spec->mode = canon_mode(mode);
+ memcpy(spec->sha1, sha1, 20);
+ spec->sha1_valid = !!memcmp(sha1, null_sha1, 20);
+ }
+}
+
+/*
+ * Given a name and sha1 pair, if the dircache tells us the file in
+ * the work tree has that object contents, return true, so that
+ * prepare_temp_file() does not have to inflate and extract.
+ */
+static int work_tree_matches(const char *name, const unsigned char *sha1)
+{
+ struct cache_entry *ce;
+ struct stat st;
+ int pos, len;
+
+ /* We do not read the cache ourselves here, because the
+ * benchmark with my previous version that always reads cache
+ * shows that it makes things worse for diff-tree comparing
+ * two linux-2.6 kernel trees in an already checked out work
+ * tree. This is because most diff-tree comparisons deal with
+ * only a small number of files, while reading the cache is
+ * expensive for a large project, and its cost outweighs the
+ * savings we get by not inflating the object to a temporary
+ * file. Practically, this code only helps when we are used
+ * by diff-cache --cached, which does read the cache before
+ * calling us.
+ */
+ if (!active_cache)
+ return 0;
+
+ len = strlen(name);
+ pos = cache_name_pos(name, len);
+ if (pos < 0)
+ return 0;
+ ce = active_cache[pos];
+ if ((lstat(name, &st) < 0) ||
+ !S_ISREG(st.st_mode) || /* careful! */
+ ce_match_stat(ce, &st, 0) ||
+ memcmp(sha1, ce->sha1, 20))
+ return 0;
+ /* we return 1 only when we can stat, it is a regular file,
+ * stat information matches, and sha1 recorded in the cache
+ * matches. I.e. we know the file in the work tree really is
+ * the same as the <name, sha1> pair.
+ */
+ return 1;
+}
+
+static struct sha1_size_cache {
+ unsigned char sha1[20];
+ unsigned long size;
+} **sha1_size_cache;
+static int sha1_size_cache_nr, sha1_size_cache_alloc;
+
+static struct sha1_size_cache *locate_size_cache(unsigned char *sha1,
+ int find_only,
+ unsigned long size)
+{
+ int first, last;
+ struct sha1_size_cache *e;
+
+ first = 0;
+ last = sha1_size_cache_nr;
+ while (last > first) {
+ int cmp, next = (last + first) >> 1;
+ e = sha1_size_cache[next];
+ cmp = memcmp(e->sha1, sha1, 20);
+ if (!cmp)
+ return e;
+ if (cmp < 0) {
+ last = next;
+ continue;
+ }
+ first = next+1;
+ }
+ /* not found */
+ if (find_only)
+ return NULL;
+ /* insert to make it at "first" */
+ if (sha1_size_cache_alloc <= sha1_size_cache_nr) {
+ sha1_size_cache_alloc = alloc_nr(sha1_size_cache_alloc);
+ sha1_size_cache = xrealloc(sha1_size_cache,
+ sha1_size_cache_alloc *
+ sizeof(*sha1_size_cache));
+ }
+ sha1_size_cache_nr++;
+ if (first < sha1_size_cache_nr)
+ memmove(sha1_size_cache + first + 1, sha1_size_cache + first,
+ (sha1_size_cache_nr - first - 1) *
+ sizeof(*sha1_size_cache));
+ e = xmalloc(sizeof(struct sha1_size_cache));
+ sha1_size_cache[first] = e;
+ memcpy(e->sha1, sha1, 20);
+ e->size = size;
+ return e;
+}
+
+/*
+ * While doing rename detection and pickaxe operation, we may need to
+ * grab the data for the blob (or file) for our own in-core comparison.
+ * diff_filespec has data and size fields for this purpose.
+ */
+int diff_populate_filespec(struct diff_filespec *s, int size_only)
+{
+ int err = 0;
+ if (!DIFF_FILE_VALID(s))
+ die("internal error: asking to populate invalid file.");
+ if (S_ISDIR(s->mode))
+ return -1;
+
+ if (!use_size_cache)
+ size_only = 0;
+
+ if (s->data)
+ return err;
+ if (!s->sha1_valid ||
+ work_tree_matches(s->path, s->sha1)) {
+ struct stat st;
+ int fd;
+ if (lstat(s->path, &st) < 0) {
+ if (errno == ENOENT) {
+ err_empty:
+ err = -1;
+ empty:
+ s->data = "";
+ s->size = 0;
+ return err;
+ }
+ }
+ s->size = st.st_size;
+ if (!s->size)
+ goto empty;
+ if (size_only)
+ return 0;
+ if (S_ISLNK(st.st_mode)) {
+ int ret;
+ s->data = xmalloc(s->size);
+ s->should_free = 1;
+ ret = readlink(s->path, s->data, s->size);
+ if (ret < 0) {
+ free(s->data);
+ goto err_empty;
+ }
+ return 0;
+ }
+ fd = open(s->path, O_RDONLY);
+ if (fd < 0)
+ goto err_empty;
+ s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
+ close(fd);
+ if (s->data == MAP_FAILED)
+ goto err_empty;
+ s->should_munmap = 1;
+ }
+ else {
+ char type[20];
+ struct sha1_size_cache *e;
+
+ if (size_only) {
+ e = locate_size_cache(s->sha1, 1, 0);
+ if (e) {
+ s->size = e->size;
+ return 0;
+ }
+ if (!sha1_object_info(s->sha1, type, &s->size))
+ locate_size_cache(s->sha1, 0, s->size);
+ }
+ else {
+ s->data = read_sha1_file(s->sha1, type, &s->size);
+ s->should_free = 1;
+ }
+ }
+ return 0;
+}
+
+void diff_free_filespec_data(struct diff_filespec *s)
+{
+ if (s->should_free)
+ free(s->data);
+ else if (s->should_munmap)
+ munmap(s->data, s->size);
+ s->should_free = s->should_munmap = 0;
+ s->data = NULL;
+ free(s->cnt_data);
+ s->cnt_data = NULL;
+}
+
+static void prep_temp_blob(struct diff_tempfile *temp,
+ void *blob,
+ unsigned long size,
+ const unsigned char *sha1,
+ int mode)
+{
+ int fd;
+
+ fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX");
+ if (fd < 0)
+ die("unable to create temp-file");
+ if (write(fd, blob, size) != size)
+ die("unable to write temp-file");
+ close(fd);
+ temp->name = temp->tmp_path;
+ strcpy(temp->hex, sha1_to_hex(sha1));
+ temp->hex[40] = 0;
+ sprintf(temp->mode, "%06o", mode);
+}
+
+static void prepare_temp_file(const char *name,
+ struct diff_tempfile *temp,
+ struct diff_filespec *one)
+{
+ if (!DIFF_FILE_VALID(one)) {
+ not_a_valid_file:
+ /* A '-' entry produces this for file-2, and
+ * a '+' entry produces this for file-1.
+ */
+ temp->name = "/dev/null";
+ strcpy(temp->hex, ".");
+ strcpy(temp->mode, ".");
+ return;
+ }
+
+ if (!one->sha1_valid ||
+ work_tree_matches(name, one->sha1)) {
+ struct stat st;
+ if (lstat(name, &st) < 0) {
+ if (errno == ENOENT)
+ goto not_a_valid_file;
+ die("stat(%s): %s", name, strerror(errno));
+ }
+ if (S_ISLNK(st.st_mode)) {
+ int ret;
+ char buf[PATH_MAX + 1]; /* ought to be SYMLINK_MAX */
+ if (sizeof(buf) <= st.st_size)
+ die("symlink too long: %s", name);
+ ret = readlink(name, buf, st.st_size);
+ if (ret < 0)
+ die("readlink(%s)", name);
+ prep_temp_blob(temp, buf, st.st_size,
+ (one->sha1_valid ?
+ one->sha1 : null_sha1),
+ (one->sha1_valid ?
+ one->mode : S_IFLNK));
+ }
+ else {
+ /* we can borrow from the file in the work tree */
+ temp->name = name;
+ if (!one->sha1_valid)
+ strcpy(temp->hex, sha1_to_hex(null_sha1));
+ else
+ strcpy(temp->hex, sha1_to_hex(one->sha1));
+ /* Even though we may sometimes borrow the
+ * contents from the work tree, we always want
+ * one->mode. mode is trustworthy even when
+ * !(one->sha1_valid), as long as
+ * DIFF_FILE_VALID(one).
+ */
+ sprintf(temp->mode, "%06o", one->mode);
+ }
+ return;
+ }
+ else {
+ if (diff_populate_filespec(one, 0))
+ die("cannot read data blob for %s", one->path);
+ prep_temp_blob(temp, one->data, one->size,
+ one->sha1, one->mode);
+ }
+}
+
+static void remove_tempfile(void)
+{
+ int i;
+
+ for (i = 0; i < 2; i++)
+ if (diff_temp[i].name == diff_temp[i].tmp_path) {
+ unlink(diff_temp[i].name);
+ diff_temp[i].name = NULL;
+ }
+}
+
+static void remove_tempfile_on_signal(int signo)
+{
+ remove_tempfile();
+ signal(SIGINT, SIG_DFL);
+ raise(signo);
+}
+
+static int spawn_prog(const char *pgm, const char **arg)
+{
+ pid_t pid;
+ int status;
+
+ fflush(NULL);
+ pid = fork();
+ if (pid < 0)
+ die("unable to fork");
+ if (!pid) {
+ execvp(pgm, (char *const*) arg);
+ exit(255);
+ }
+
+ while (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ }
+
+ /* Earlier we did not check the exit status because
+ * diff exits non-zero if files are different, and
+ * we are not interested in knowing that. It was a
+ * mistake which made it harder to quit a diff-*
+ * session that uses the git-apply-patch-script as
+ * the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
+ * should also exit non-zero only when it wants to
+ * abort the entire diff-* session.
+ */
+ if (WIFEXITED(status) && !WEXITSTATUS(status))
+ return 0;
+ return -1;
+}
+
+/* An external diff command takes:
+ *
+ * diff-cmd name infile1 infile1-sha1 infile1-mode \
+ * infile2 infile2-sha1 infile2-mode [ rename-to ]
+ *
+ */
+static void run_external_diff(const char *pgm,
+ const char *name,
+ const char *other,
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ const char *xfrm_msg,
+ int complete_rewrite)
+{
+ const char *spawn_arg[10];
+ struct diff_tempfile *temp = diff_temp;
+ int retval;
+ static int atexit_asked = 0;
+ const char *othername;
+ const char **arg = &spawn_arg[0];
+
+ othername = (other? other : name);
+ if (one && two) {
+ prepare_temp_file(name, &temp[0], one);
+ prepare_temp_file(othername, &temp[1], two);
+ if (! atexit_asked &&
+ (temp[0].name == temp[0].tmp_path ||
+ temp[1].name == temp[1].tmp_path)) {
+ atexit_asked = 1;
+ atexit(remove_tempfile);
+ }
+ signal(SIGINT, remove_tempfile_on_signal);
+ }
+
+ if (one && two) {
+ *arg++ = pgm;
+ *arg++ = name;
+ *arg++ = temp[0].name;
+ *arg++ = temp[0].hex;
+ *arg++ = temp[0].mode;
+ *arg++ = temp[1].name;
+ *arg++ = temp[1].hex;
+ *arg++ = temp[1].mode;
+ if (other) {
+ *arg++ = other;
+ *arg++ = xfrm_msg;
+ }
+ } else {
+ *arg++ = pgm;
+ *arg++ = name;
+ }
+ *arg = NULL;
+ retval = spawn_prog(pgm, spawn_arg);
+ remove_tempfile();
+ if (retval) {
+ fprintf(stderr, "external diff died, stopping at %s.\n", name);
+ exit(1);
+ }
+}
+
+static void run_diff_cmd(const char *pgm,
+ const char *name,
+ const char *other,
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ const char *xfrm_msg,
+ int complete_rewrite)
+{
+ if (pgm) {
+ run_external_diff(pgm, name, other, one, two, xfrm_msg,
+ complete_rewrite);
+ return;
+ }
+ if (one && two)
+ builtin_diff(name, other ? other : name,
+ one, two, xfrm_msg, complete_rewrite);
+ else
+ printf("* Unmerged path %s\n", name);
+}
+
+static void diff_fill_sha1_info(struct diff_filespec *one)
+{
+ if (DIFF_FILE_VALID(one)) {
+ if (!one->sha1_valid) {
+ struct stat st;
+ if (lstat(one->path, &st) < 0)
+ die("stat %s", one->path);
+ if (index_path(one->sha1, one->path, &st, 0))
+ die("cannot hash %s\n", one->path);
+ }
+ }
+ else
+ memset(one->sha1, 0, 20);
+}
+
+static void run_diff(struct diff_filepair *p, struct diff_options *o)
+{
+ const char *pgm = external_diff();
+ char msg[PATH_MAX*2+300], *xfrm_msg;
+ struct diff_filespec *one;
+ struct diff_filespec *two;
+ const char *name;
+ const char *other;
+ char *name_munged, *other_munged;
+ int complete_rewrite = 0;
+ int len;
+
+ if (DIFF_PAIR_UNMERGED(p)) {
+ /* unmerged */
+ run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+ return;
+ }
+
+ name = p->one->path;
+ other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+ name_munged = quote_one(name);
+ other_munged = quote_one(other);
+ one = p->one; two = p->two;
+
+ diff_fill_sha1_info(one);
+ diff_fill_sha1_info(two);
+
+ len = 0;
+ switch (p->status) {
+ case DIFF_STATUS_COPIED:
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "similarity index %d%%\n"
+ "copy from %s\n"
+ "copy to %s\n",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name_munged, other_munged);
+ break;
+ case DIFF_STATUS_RENAMED:
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "similarity index %d%%\n"
+ "rename from %s\n"
+ "rename to %s\n",
+ (int)(0.5 + p->score * 100.0/MAX_SCORE),
+ name_munged, other_munged);
+ break;
+ case DIFF_STATUS_MODIFIED:
+ if (p->score) {
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "dissimilarity index %d%%\n",
+ (int)(0.5 + p->score *
+ 100.0/MAX_SCORE));
+ complete_rewrite = 1;
+ break;
+ }
+ /* fallthru */
+ default:
+ /* nothing */
+ ;
+ }
+
+ if (memcmp(one->sha1, two->sha1, 20)) {
+ char one_sha1[41];
+ int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
+ memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
+
+ len += snprintf(msg + len, sizeof(msg) - len,
+ "index %.*s..%.*s",
+ abbrev, one_sha1, abbrev,
+ sha1_to_hex(two->sha1));
+ if (one->mode == two->mode)
+ len += snprintf(msg + len, sizeof(msg) - len,
+ " %06o", one->mode);
+ len += snprintf(msg + len, sizeof(msg) - len, "\n");
+ }
+
+ if (len)
+ msg[--len] = 0;
+ xfrm_msg = len ? msg : NULL;
+
+ if (!pgm &&
+ DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
+ (S_IFMT & one->mode) != (S_IFMT & two->mode)) {
+ /* a filepair that changes between file and symlink
+ * needs to be split into deletion and creation.
+ */
+ struct diff_filespec *null = alloc_filespec(two->path);
+ run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+ free(null);
+ null = alloc_filespec(one->path);
+ run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+ free(null);
+ }
+ else
+ run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
+ complete_rewrite);
+
+ free(name_munged);
+ free(other_munged);
+}
+
+static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
+ struct diffstat_t *diffstat)
+{
+ const char *name;
+ const char *other;
+ int complete_rewrite = 0;
+
+ if (DIFF_PAIR_UNMERGED(p)) {
+ /* unmerged */
+ builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0);
+ return;
+ }
+
+ name = p->one->path;
+ other = (strcmp(name, p->two->path) ? p->two->path : NULL);
+
+ diff_fill_sha1_info(p->one);
+ diff_fill_sha1_info(p->two);
+
+ if (p->status == DIFF_STATUS_MODIFIED && p->score)
+ complete_rewrite = 1;
+ builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite);
+}
+
+void diff_setup(struct diff_options *options)
+{
+ memset(options, 0, sizeof(*options));
+ options->output_format = DIFF_FORMAT_RAW;
+ options->line_termination = '\n';
+ options->break_opt = -1;
+ options->rename_limit = -1;
+
+ options->change = diff_change;
+ options->add_remove = diff_addremove;
+}
+
+int diff_setup_done(struct diff_options *options)
+{
+ if ((options->find_copies_harder &&
+ options->detect_rename != DIFF_DETECT_COPY) ||
+ (0 <= options->rename_limit && !options->detect_rename))
+ return -1;
+
+ /*
+ * These cases always need recursive; we do not drop caller-supplied
+ * recursive bits for other formats here.
+ */
+ if ((options->output_format == DIFF_FORMAT_PATCH) ||
+ (options->output_format == DIFF_FORMAT_DIFFSTAT))
+ options->recursive = 1;
+
+ if (options->detect_rename && options->rename_limit < 0)
+ options->rename_limit = diff_rename_limit_default;
+ if (options->setup & DIFF_SETUP_USE_CACHE) {
+ if (!active_cache)
+ /* read-cache does not die even when it fails
+ * so it is safe for us to do this here. Also
+ * it does not smudge active_cache or active_nr
+ * when it fails, so we do not have to worry about
+ * cleaning it up ourselves either.
+ */
+ read_cache();
+ }
+ if (options->setup & DIFF_SETUP_USE_SIZE_CACHE)
+ use_size_cache = 1;
+ if (options->abbrev <= 0 || 40 < options->abbrev)
+ options->abbrev = 40; /* full */
+
+ return 0;
+}
+
+int diff_opt_parse(struct diff_options *options, const char **av, int ac)
+{
+ const char *arg = av[0];
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+ options->output_format = DIFF_FORMAT_PATCH;
+ else if (!strcmp(arg, "--patch-with-raw")) {
+ options->output_format = DIFF_FORMAT_PATCH;
+ options->with_raw = 1;
+ }
+ else if (!strcmp(arg, "--stat"))
+ options->output_format = DIFF_FORMAT_DIFFSTAT;
+ else if (!strcmp(arg, "--patch-with-stat")) {
+ options->output_format = DIFF_FORMAT_PATCH;
+ options->with_stat = 1;
+ }
+ else if (!strcmp(arg, "-z"))
+ options->line_termination = 0;
+ else if (!strncmp(arg, "-l", 2))
+ options->rename_limit = strtoul(arg+2, NULL, 10);
+ else if (!strcmp(arg, "--full-index"))
+ options->full_index = 1;
+ else if (!strcmp(arg, "--name-only"))
+ options->output_format = DIFF_FORMAT_NAME;
+ else if (!strcmp(arg, "--name-status"))
+ options->output_format = DIFF_FORMAT_NAME_STATUS;
+ else if (!strcmp(arg, "-R"))
+ options->reverse_diff = 1;
+ else if (!strncmp(arg, "-S", 2))
+ options->pickaxe = arg + 2;
+ else if (!strcmp(arg, "-s"))
+ options->output_format = DIFF_FORMAT_NO_OUTPUT;
+ else if (!strncmp(arg, "-O", 2))
+ options->orderfile = arg + 2;
+ else if (!strncmp(arg, "--diff-filter=", 14))
+ options->filter = arg + 14;
+ else if (!strcmp(arg, "--pickaxe-all"))
+ options->pickaxe_opts = DIFF_PICKAXE_ALL;
+ else if (!strcmp(arg, "--pickaxe-regex"))
+ options->pickaxe_opts = DIFF_PICKAXE_REGEX;
+ else if (!strncmp(arg, "-B", 2)) {
+ if ((options->break_opt =
+ diff_scoreopt_parse(arg)) == -1)
+ return -1;
+ }
+ else if (!strncmp(arg, "-M", 2)) {
+ if ((options->rename_score =
+ diff_scoreopt_parse(arg)) == -1)
+ return -1;
+ options->detect_rename = DIFF_DETECT_RENAME;
+ }
+ else if (!strncmp(arg, "-C", 2)) {
+ if ((options->rename_score =
+ diff_scoreopt_parse(arg)) == -1)
+ return -1;
+ options->detect_rename = DIFF_DETECT_COPY;
+ }
+ else if (!strcmp(arg, "--find-copies-harder"))
+ options->find_copies_harder = 1;
+ else if (!strcmp(arg, "--abbrev"))
+ options->abbrev = DEFAULT_ABBREV;
+ else if (!strncmp(arg, "--abbrev=", 9)) {
+ options->abbrev = strtoul(arg + 9, NULL, 10);
+ if (options->abbrev < MINIMUM_ABBREV)
+ options->abbrev = MINIMUM_ABBREV;
+ else if (40 < options->abbrev)
+ options->abbrev = 40;
+ }
+ else
+ return 0;
+ return 1;
+}
+
+static int parse_num(const char **cp_p)
+{
+ unsigned long num, scale;
+ int ch, dot;
+ const char *cp = *cp_p;
+
+ num = 0;
+ scale = 1;
+ dot = 0;
+ for(;;) {
+ ch = *cp;
+ if ( !dot && ch == '.' ) {
+ scale = 1;
+ dot = 1;
+ } else if ( ch == '%' ) {
+ scale = dot ? scale*100 : 100;
+ cp++; /* % is always at the end */
+ break;
+ } else if ( ch >= '0' && ch <= '9' ) {
+ if ( scale < 100000 ) {
+ scale *= 10;
+ num = (num*10) + (ch-'0');
+ }
+ } else {
+ break;
+ }
+ cp++;
+ }
+ *cp_p = cp;
+
+ /* user says num divided by scale and we say internally that
+ * is MAX_SCORE * num / scale.
+ */
+ return (num >= scale) ? MAX_SCORE : (MAX_SCORE * num / scale);
+}
+
+int diff_scoreopt_parse(const char *opt)
+{
+ int opt1, opt2, cmd;
+
+ if (*opt++ != '-')
+ return -1;
+ cmd = *opt++;
+ if (cmd != 'M' && cmd != 'C' && cmd != 'B')
+ return -1; /* that is not a -M, -C nor -B option */
+
+ opt1 = parse_num(&opt);
+ if (cmd != 'B')
+ opt2 = 0;
+ else {
+ if (*opt == 0)
+ opt2 = 0;
+ else if (*opt != '/')
+ return -1; /* we expect -B80/99 or -B80 */
+ else {
+ opt++;
+ opt2 = parse_num(&opt);
+ }
+ }
+ if (*opt != 0)
+ return -1;
+ return opt1 | (opt2 << 16);
+}
+
+struct diff_queue_struct diff_queued_diff;
+
+void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp)
+{
+ if (queue->alloc <= queue->nr) {
+ queue->alloc = alloc_nr(queue->alloc);
+ queue->queue = xrealloc(queue->queue,
+ sizeof(dp) * queue->alloc);
+ }
+ queue->queue[queue->nr++] = dp;
+}
+
+struct diff_filepair *diff_queue(struct diff_queue_struct *queue,
+ struct diff_filespec *one,
+ struct diff_filespec *two)
+{
+ struct diff_filepair *dp = xmalloc(sizeof(*dp));
+ dp->one = one;
+ dp->two = two;
+ dp->score = 0;
+ dp->status = 0;
+ dp->source_stays = 0;
+ dp->broken_pair = 0;
+ if (queue)
+ diff_q(queue, dp);
+ return dp;
+}
+
+void diff_free_filepair(struct diff_filepair *p)
+{
+ diff_free_filespec_data(p->one);
+ diff_free_filespec_data(p->two);
+ free(p->one);
+ free(p->two);
+ free(p);
+}
+
+/* This is different from find_unique_abbrev() in that
+ * it stuffs the result with dots for alignment.
+ */
+const char *diff_unique_abbrev(const unsigned char *sha1, int len)
+{
+ int abblen;
+ const char *abbrev;
+ if (len == 40)
+ return sha1_to_hex(sha1);
+
+ abbrev = find_unique_abbrev(sha1, len);
+ if (!abbrev)
+ return sha1_to_hex(sha1);
+ abblen = strlen(abbrev);
+ if (abblen < 37) {
+ static char hex[41];
+ if (len < abblen && abblen <= len + 2)
+ sprintf(hex, "%s%.*s", abbrev, len+3-abblen, "..");
+ else
+ sprintf(hex, "%s...", abbrev);
+ return hex;
+ }
+ return sha1_to_hex(sha1);
+}
+
+static void diff_flush_raw(struct diff_filepair *p,
+ int line_termination,
+ int inter_name_termination,
+ struct diff_options *options,
+ int output_format)
+{
+ int two_paths;
+ char status[10];
+ int abbrev = options->abbrev;
+ const char *path_one, *path_two;
+
+ path_one = p->one->path;
+ path_two = p->two->path;
+ if (line_termination) {
+ path_one = quote_one(path_one);
+ path_two = quote_one(path_two);
+ }
+
+ if (p->score)
+ sprintf(status, "%c%03d", p->status,
+ (int)(0.5 + p->score * 100.0/MAX_SCORE));
+ else {
+ status[0] = p->status;
+ status[1] = 0;
+ }
+ switch (p->status) {
+ case DIFF_STATUS_COPIED:
+ case DIFF_STATUS_RENAMED:
+ two_paths = 1;
+ break;
+ case DIFF_STATUS_ADDED:
+ case DIFF_STATUS_DELETED:
+ two_paths = 0;
+ break;
+ default:
+ two_paths = 0;
+ break;
+ }
+ if (output_format != DIFF_FORMAT_NAME_STATUS) {
+ printf(":%06o %06o %s ",
+ p->one->mode, p->two->mode,
+ diff_unique_abbrev(p->one->sha1, abbrev));
+ printf("%s ",
+ diff_unique_abbrev(p->two->sha1, abbrev));
+ }
+ printf("%s%c%s", status, inter_name_termination, path_one);
+ if (two_paths)
+ printf("%c%s", inter_name_termination, path_two);
+ putchar(line_termination);
+ if (path_one != p->one->path)
+ free((void*)path_one);
+ if (path_two != p->two->path)
+ free((void*)path_two);
+}
+
+static void diff_flush_name(struct diff_filepair *p,
+ int inter_name_termination,
+ int line_termination)
+{
+ char *path = p->two->path;
+
+ if (line_termination)
+ path = quote_one(p->two->path);
+ else
+ path = p->two->path;
+ printf("%s%c", path, line_termination);
+ if (p->two->path != path)
+ free(path);
+}
+
+int diff_unmodified_pair(struct diff_filepair *p)
+{
+ /* This function is written stricter than necessary to support
+ * the currently implemented transformers, but the idea is to
+ * let transformers to produce diff_filepairs any way they want,
+ * and filter and clean them up here before producing the output.
+ */
+ struct diff_filespec *one, *two;
+
+ if (DIFF_PAIR_UNMERGED(p))
+ return 0; /* unmerged is interesting */
+
+ one = p->one;
+ two = p->two;
+
+ /* deletion, addition, mode or type change
+ * and rename are all interesting.
+ */
+ if (DIFF_FILE_VALID(one) != DIFF_FILE_VALID(two) ||
+ DIFF_PAIR_MODE_CHANGED(p) ||
+ strcmp(one->path, two->path))
+ return 0;
+
+ /* both are valid and point at the same path. that is, we are
+ * dealing with a change.
+ */
+ if (one->sha1_valid && two->sha1_valid &&
+ !memcmp(one->sha1, two->sha1, sizeof(one->sha1)))
+ return 1; /* no change */
+ if (!one->sha1_valid && !two->sha1_valid)
+ return 1; /* both look at the same file on the filesystem. */
+ return 0;
+}
+
+static void diff_flush_patch(struct diff_filepair *p, struct diff_options *o)
+{
+ if (diff_unmodified_pair(p))
+ return;
+
+ if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+ (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+ return; /* no tree diffs in patch format */
+
+ run_diff(p, o);
+}
+
+static void diff_flush_stat(struct diff_filepair *p, struct diff_options *o,
+ struct diffstat_t *diffstat)
+{
+ if (diff_unmodified_pair(p))
+ return;
+
+ if ((DIFF_FILE_VALID(p->one) && S_ISDIR(p->one->mode)) ||
+ (DIFF_FILE_VALID(p->two) && S_ISDIR(p->two->mode)))
+ return; /* no tree diffs in patch format */
+
+ run_diffstat(p, o, diffstat);
+}
+
+int diff_queue_is_empty(void)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i;
+ for (i = 0; i < q->nr; i++)
+ if (!diff_unmodified_pair(q->queue[i]))
+ return 0;
+ return 1;
+}
+
+#if DIFF_DEBUG
+void diff_debug_filespec(struct diff_filespec *s, int x, const char *one)
+{
+ fprintf(stderr, "queue[%d] %s (%s) %s %06o %s\n",
+ x, one ? one : "",
+ s->path,
+ DIFF_FILE_VALID(s) ? "valid" : "invalid",
+ s->mode,
+ s->sha1_valid ? sha1_to_hex(s->sha1) : "");
+ fprintf(stderr, "queue[%d] %s size %lu flags %d\n",
+ x, one ? one : "",
+ s->size, s->xfrm_flags);
+}
+
+void diff_debug_filepair(const struct diff_filepair *p, int i)
+{
+ diff_debug_filespec(p->one, i, "one");
+ diff_debug_filespec(p->two, i, "two");
+ fprintf(stderr, "score %d, status %c stays %d broken %d\n",
+ p->score, p->status ? p->status : '?',
+ p->source_stays, p->broken_pair);
+}
+
+void diff_debug_queue(const char *msg, struct diff_queue_struct *q)
+{
+ int i;
+ if (msg)
+ fprintf(stderr, "%s\n", msg);
+ fprintf(stderr, "q->nr = %d\n", q->nr);
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ diff_debug_filepair(p, i);
+ }
+}
+#endif
+
+static void diff_resolve_rename_copy(void)
+{
+ int i, j;
+ struct diff_filepair *p, *pp;
+ struct diff_queue_struct *q = &diff_queued_diff;
+
+ diff_debug_queue("resolve-rename-copy", q);
+
+ for (i = 0; i < q->nr; i++) {
+ p = q->queue[i];
+ p->status = 0; /* undecided */
+ if (DIFF_PAIR_UNMERGED(p))
+ p->status = DIFF_STATUS_UNMERGED;
+ else if (!DIFF_FILE_VALID(p->one))
+ p->status = DIFF_STATUS_ADDED;
+ else if (!DIFF_FILE_VALID(p->two))
+ p->status = DIFF_STATUS_DELETED;
+ else if (DIFF_PAIR_TYPE_CHANGED(p))
+ p->status = DIFF_STATUS_TYPE_CHANGED;
+
+ /* from this point on, we are dealing with a pair
+ * whose both sides are valid and of the same type, i.e.
+ * either in-place edit or rename/copy edit.
+ */
+ else if (DIFF_PAIR_RENAME(p)) {
+ if (p->source_stays) {
+ p->status = DIFF_STATUS_COPIED;
+ continue;
+ }
+ /* See if there is some other filepair that
+ * copies from the same source as us. If so
+ * we are a copy. Otherwise we are either a
+ * copy if the path stays, or a rename if it
+ * does not, but we already handled "stays" case.
+ */
+ for (j = i + 1; j < q->nr; j++) {
+ pp = q->queue[j];
+ if (strcmp(pp->one->path, p->one->path))
+ continue; /* not us */
+ if (!DIFF_PAIR_RENAME(pp))
+ continue; /* not a rename/copy */
+ /* pp is a rename/copy from the same source */
+ p->status = DIFF_STATUS_COPIED;
+ break;
+ }
+ if (!p->status)
+ p->status = DIFF_STATUS_RENAMED;
+ }
+ else if (memcmp(p->one->sha1, p->two->sha1, 20) ||
+ p->one->mode != p->two->mode)
+ p->status = DIFF_STATUS_MODIFIED;
+ else {
+ /* This is a "no-change" entry and should not
+ * happen anymore, but prepare for broken callers.
+ */
+ error("feeding unmodified %s to diffcore",
+ p->one->path);
+ p->status = DIFF_STATUS_UNKNOWN;
+ }
+ }
+ diff_debug_queue("resolve-rename-copy done", q);
+}
+
+static void flush_one_pair(struct diff_filepair *p,
+ int diff_output_format,
+ struct diff_options *options,
+ struct diffstat_t *diffstat)
+{
+ int inter_name_termination = '\t';
+ int line_termination = options->line_termination;
+ if (!line_termination)
+ inter_name_termination = 0;
+
+ switch (p->status) {
+ case DIFF_STATUS_UNKNOWN:
+ break;
+ case 0:
+ die("internal error in diff-resolve-rename-copy");
+ break;
+ default:
+ switch (diff_output_format) {
+ case DIFF_FORMAT_DIFFSTAT:
+ diff_flush_stat(p, options, diffstat);
+ break;
+ case DIFF_FORMAT_PATCH:
+ diff_flush_patch(p, options);
+ break;
+ case DIFF_FORMAT_RAW:
+ case DIFF_FORMAT_NAME_STATUS:
+ diff_flush_raw(p, line_termination,
+ inter_name_termination,
+ options, diff_output_format);
+ break;
+ case DIFF_FORMAT_NAME:
+ diff_flush_name(p,
+ inter_name_termination,
+ line_termination);
+ break;
+ case DIFF_FORMAT_NO_OUTPUT:
+ break;
+ }
+ }
+}
+
+void diff_flush(struct diff_options *options)
+{
+ struct diff_queue_struct *q = &diff_queued_diff;
+ int i;
+ int diff_output_format = options->output_format;
+ struct diffstat_t *diffstat = NULL;
+
+ if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) {
+ diffstat = xcalloc(sizeof (struct diffstat_t), 1);
+ diffstat->xm.consume = diffstat_consume;
+ }
+
+ if (options->with_raw) {
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
+ }
+ putchar(options->line_termination);
+ }
+ if (options->with_stat) {
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
+ diffstat);
+ }
+ show_stats(diffstat);
+ free(diffstat);
+ diffstat = NULL;
+ putchar(options->line_termination);
+ }
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ flush_one_pair(p, diff_output_format, options, diffstat);
+ diff_free_filepair(p);
+ }
+
+ if (diffstat) {
+ show_stats(diffstat);
+ free(diffstat);
+ }
+
+ free(q->queue);
+ q->queue = NULL;
+ q->nr = q->alloc = 0;
+}
+
+static void diffcore_apply_filter(const char *filter)
+{
+ int i;
+ struct diff_queue_struct *q = &diff_queued_diff;
+ struct diff_queue_struct outq;
+ outq.queue = NULL;
+ outq.nr = outq.alloc = 0;
+
+ if (!filter)
+ return;
+
+ if (strchr(filter, DIFF_STATUS_FILTER_AON)) {
+ int found;
+ for (i = found = 0; !found && i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ if (((p->status == DIFF_STATUS_MODIFIED) &&
+ ((p->score &&
+ strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
+ (!p->score &&
+ strchr(filter, DIFF_STATUS_MODIFIED)))) ||
+ ((p->status != DIFF_STATUS_MODIFIED) &&
+ strchr(filter, p->status)))
+ found++;
+ }
+ if (found)
+ return;
+
+ /* otherwise we will clear the whole queue
+ * by copying the empty outq at the end of this
+ * function, but first clear the current entries
+ * in the queue.
+ */
+ for (i = 0; i < q->nr; i++)
+ diff_free_filepair(q->queue[i]);
+ }
+ else {
+ /* Only the matching ones */
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+
+ if (((p->status == DIFF_STATUS_MODIFIED) &&
+ ((p->score &&
+ strchr(filter, DIFF_STATUS_FILTER_BROKEN)) ||
+ (!p->score &&
+ strchr(filter, DIFF_STATUS_MODIFIED)))) ||
+ ((p->status != DIFF_STATUS_MODIFIED) &&
+ strchr(filter, p->status)))
+ diff_q(&outq, p);
+ else
+ diff_free_filepair(p);
+ }
+ }
+ free(q->queue);
+ *q = outq;
+}
+
+void diffcore_std(struct diff_options *options)
+{
+ if (options->break_opt != -1)
+ diffcore_break(options->break_opt);
+ if (options->detect_rename)
+ diffcore_rename(options);
+ if (options->break_opt != -1)
+ diffcore_merge_broken();
+ if (options->pickaxe)
+ diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+ if (options->orderfile)
+ diffcore_order(options->orderfile);
+ diff_resolve_rename_copy();
+ diffcore_apply_filter(options->filter);
+}
+
+
+void diffcore_std_no_resolve(struct diff_options *options)
+{
+ if (options->pickaxe)
+ diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
+ if (options->orderfile)
+ diffcore_order(options->orderfile);
+ diffcore_apply_filter(options->filter);
+}
+
+void diff_addremove(struct diff_options *options,
+ int addremove, unsigned mode,
+ const unsigned char *sha1,
+ const char *base, const char *path)
+{
+ char concatpath[PATH_MAX];
+ struct diff_filespec *one, *two;
+
+ /* This may look odd, but it is a preparation for
+ * feeding "there are unchanged files which should
+ * not produce diffs, but when you are doing copy
+ * detection you would need them, so here they are"
+ * entries to the diff-core. They will be prefixed
+ * with something like '=' or '*' (I haven't decided
+ * which but should not make any difference).
+ * Feeding the same new and old to diff_change()
+ * also has the same effect.
+ * Before the final output happens, they are pruned after
+ * merged into rename/copy pairs as appropriate.
+ */
+ if (options->reverse_diff)
+ addremove = (addremove == '+' ? '-' :
+ addremove == '-' ? '+' : addremove);
+
+ if (!path) path = "";
+ sprintf(concatpath, "%s%s", base, path);
+ one = alloc_filespec(concatpath);
+ two = alloc_filespec(concatpath);
+
+ if (addremove != '+')
+ fill_filespec(one, sha1, mode);
+ if (addremove != '-')
+ fill_filespec(two, sha1, mode);
+
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+void diff_change(struct diff_options *options,
+ unsigned old_mode, unsigned new_mode,
+ const unsigned char *old_sha1,
+ const unsigned char *new_sha1,
+ const char *base, const char *path)
+{
+ char concatpath[PATH_MAX];
+ struct diff_filespec *one, *two;
+
+ if (options->reverse_diff) {
+ unsigned tmp;
+ const unsigned char *tmp_c;
+ tmp = old_mode; old_mode = new_mode; new_mode = tmp;
+ tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
+ }
+ if (!path) path = "";
+ sprintf(concatpath, "%s%s", base, path);
+ one = alloc_filespec(concatpath);
+ two = alloc_filespec(concatpath);
+ fill_filespec(one, old_sha1, old_mode);
+ fill_filespec(two, new_sha1, new_mode);
+
+ diff_queue(&diff_queued_diff, one, two);
+}
+
+void diff_unmerge(struct diff_options *options,
+ const char *path)
+{
+ struct diff_filespec *one, *two;
+ one = alloc_filespec(path);
+ two = alloc_filespec(path);
+ diff_queue(&diff_queued_diff, one, two);
+}
diff --git a/diff.h b/diff.h
index 52fff66..7150b90 100644
--- a/diff.h
+++ b/diff.h
@@ -28,10 +28,11 @@ struct diff_options {
with_raw:1,
with_stat:1,
tree_in_recursive:1,
- full_index:1;
+ full_index:1,
+ silent_on_remove:1,
+ find_copies_harder:1;
int break_opt;
int detect_rename;
- int find_copies_harder;
int line_termination;
int output_format;
int pickaxe_opts;
@@ -168,4 +169,8 @@ extern void diff_flush(struct diff_options*);
extern const char *diff_unique_abbrev(const unsigned char *, int);
+extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
+
+extern int run_diff_index(struct rev_info *revs, int cached);
+
#endif /* DIFF_H */
diff --git a/git-am.sh b/git-am.sh
index eab4aa8..872145b 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -376,6 +376,13 @@ do
echo "No changes - did you forget update-index?"
stop_here $this
fi
+ unmerged=$(git-ls-files -u)
+ if test -n "$unmerged"
+ then
+ echo "You still have unmerged paths in your index"
+ echo "did you forget update-index?"
+ stop_here $this
+ fi
apply_status=0
;;
esac
diff --git a/git-annotate.perl b/git-annotate.perl
index 9df72a1..a6a7a48 100755
--- a/git-annotate.perl
+++ b/git-annotate.perl
@@ -10,9 +10,10 @@ use warnings;
use strict;
use Getopt::Long;
use POSIX qw(strftime gmtime);
+use File::Basename qw(basename dirname);
sub usage() {
- print STDERR 'Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
+ print STDERR "Usage: ${\basename $0} [-s] [-S revs-file] file [ revision ]
-l, --long
Show long rev (Defaults off)
-t, --time
@@ -23,7 +24,7 @@ sub usage() {
Use revs from revs-file instead of calling git-rev-list
-h, --help
This message.
-';
+";
exit(1);
}
@@ -35,7 +36,7 @@ my $rc = GetOptions( "long|l" => \$longrev,
"help|h" => \$help,
"rename|r" => \$rename,
"rev-file|S=s" => \$rev_file);
-if (!$rc or $help) {
+if (!$rc or $help or !@ARGV) {
usage();
}
@@ -208,6 +209,9 @@ sub find_parent_renames {
while (my $change = <$patch>) {
chomp $change;
my $filename = <$patch>;
+ if (!defined $filename) {
+ next;
+ }
chomp $filename;
if ($change =~ m/^[AMD]$/ ) {
diff --git a/git-branch.sh b/git-branch.sh
index 663a3a3..ebcc898 100755
--- a/git-branch.sh
+++ b/git-branch.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-USAGE='[(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]]'
+USAGE='[(-d | -D) <branchname>] | [[-f] <branchname> [<start-point>]] | -r'
LONG_USAGE='If no arguments, show available branches and mark current branch with a star.
If one argument, create a new branch <branchname> based off of current HEAD.
If two arguments, create a new branch <branchname> based off of <start-point>.'
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 7d3f78e..11d153c 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -88,7 +88,7 @@ my $TEMP_DIR = tempdir( CLEANUP => 1 );
$log->debug("Temporary directory is '$TEMP_DIR'");
# if we are called with a pserver argument,
-# deal with the authentication cat before entereing the
+# deal with the authentication cat before entering the
# main loop
if (@ARGV && $ARGV[0] eq 'pserver') {
my $line = <STDIN>; chomp $line;
@@ -117,7 +117,7 @@ while (<STDIN>)
{
chomp;
- # Check to see if we've seen this method, and call appropiate function.
+ # Check to see if we've seen this method, and call appropriate function.
if ( /^([\w-]+)(?:\s+(.*))?$/ and defined($methods->{$1}) )
{
# use the $methods hash to call the appropriate sub for this command
@@ -171,11 +171,11 @@ sub req_Root
return 0;
}
- my @gitvars = `git-var -l`;
+ my @gitvars = `git-repo-config -l`;
if ($?) {
- print "E problems executing git-var on the server -- this is not a git repository or the PATH is not set correcly.\n";
+ print "E problems executing git-repo-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
print "E \n";
- print "error 1 - problem executing git-var\n";
+ print "error 1 - problem executing git-repo-config\n";
return 0;
}
foreach my $line ( @gitvars )
@@ -224,7 +224,7 @@ sub req_Globaloption
sub req_Validresponses
{
my ( $cmd, $data ) = @_;
- $log->debug("req_Validrepsonses : $data");
+ $log->debug("req_Validresponses : $data");
# TODO : re-enable this, currently it's not particularly useful
#$state->{validresponses} = [ split /\s+/, $data ];
@@ -733,7 +733,7 @@ sub req_update
argsplit("update");
#
- # It may just be a client exploring the available heads/modukles
+ # It may just be a client exploring the available heads/modules
# in that case, list them as top level directories and leave it
# at that. Eclipse uses this technique to offer you a list of
# projects (heads in this case) to checkout.
@@ -1731,7 +1731,7 @@ sub transmitfile
}
# This method takes a file name, and returns ( $dirpart, $filepart ) which
-# refers to the directory porition and the file portion of the filename
+# refers to the directory portion and the file portion of the filename
# respectively
sub filenamesplit
{
@@ -1790,7 +1790,7 @@ Log::Log4perl
=head2 new
Creates a new log object, optionally you can specify a filename here to
-indicate the file to log to. If no log file is specified, you can specifiy one
+indicate the file to log to. If no log file is specified, you can specify one
later with method setfile, or indicate you no longer want logging with method
nofile.
@@ -2595,7 +2595,7 @@ sub in_array
=head2 safe_pipe_capture
-an alterative to `command` that allows input to be passed as an array
+an alternative to `command` that allows input to be passed as an array
to work around shell problems with weird characters in arguments
=cut
diff --git a/git-format-patch.sh b/git-format-patch.sh
index c7133bc..c077f44 100755
--- a/git-format-patch.sh
+++ b/git-format-patch.sh
@@ -205,11 +205,10 @@ sub show_date {
}
my $t = $time + $minutes * 60;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
- return sprintf("%s %s %d %02d:%02d:%02d %d %+05d",
- $weekday_names[$wday],
- $month_names[$mon],
- $mday, $hour, $min, $sec,
- $year+1900, $tz);
+ return sprintf("%s, %d %s %d %02d:%02d:%02d %+05d",
+ $weekday_names[$wday], $mday,
+ $month_names[$mon], $year+1900,
+ $hour, $min, $sec, $tz);
}
print "From nobody Mon Sep 17 00:00:00 2001\n";
diff --git a/git-rebase.sh b/git-rebase.sh
index 86dfe9c..f7b2b94 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -107,7 +107,7 @@ onto=$(git-rev-parse --verify "${onto_name}^0") || exit
# Check if we are already based on $onto, but this should be
# done only when upstream and onto are the same.
-if test "$upstream" = "onto"
+if test "$upstream" = "$onto"
then
mb=$(git-merge-base "$onto" "$branch")
if test "$mb" = "$onto"
diff --git a/git.c b/git.c
index aa2b814..01b7e28 100644
--- a/git.c
+++ b/git.c
@@ -8,7 +8,6 @@
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
-#include <sys/ioctl.h>
#include "git-compat-util.h"
#include "exec_cmd.h"
diff --git a/pack-check.c b/pack-check.c
index 84ed90d..e575879 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -29,12 +29,12 @@ static int verify_packfile(struct packed_git *p)
pack_base = p->pack_base;
SHA1_Update(&ctx, pack_base, pack_size - 20);
SHA1_Final(sha1, &ctx);
- if (memcmp(sha1, index_base + index_size - 40, 20))
- return error("Packfile %s SHA1 mismatch with idx",
- p->pack_name);
if (memcmp(sha1, pack_base + pack_size - 20, 20))
return error("Packfile %s SHA1 mismatch with itself",
p->pack_name);
+ if (memcmp(sha1, index_base + index_size - 40, 20))
+ return error("Packfile %s SHA1 mismatch with idx",
+ p->pack_name);
/* Make sure everything reachable from idx is valid. Since we
* have verified that nr_objects matches between idx and pack,
diff --git a/repo-config.c b/repo-config.c
index c5ebb76..fa8aba7 100644
--- a/repo-config.c
+++ b/repo-config.c
@@ -2,7 +2,7 @@
#include <regex.h>
static const char git_config_set_usage[] =
-"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]]";
+"git-repo-config [ --bool | --int ] [--get | --get-all | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
static char* key = NULL;
static char* value = NULL;
@@ -12,6 +12,15 @@ static int do_not_match = 0;
static int seen = 0;
static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
+static int show_all_config(const char *key_, const char *value_)
+{
+ if (value_)
+ printf("%s=%s\n", key_, value_);
+ else
+ printf("%s\n", key_);
+ return 0;
+}
+
static int show_config(const char* key_, const char* value_)
{
if (value_ == NULL)
@@ -67,7 +76,7 @@ static int get_value(const char* key_, const char* regex_)
}
}
- i = git_config(show_config);
+ git_config(show_config);
if (value) {
printf("%s\n", value);
free(value);
@@ -99,6 +108,9 @@ int main(int argc, const char **argv)
argv++;
}
+ if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l"))
+ return git_config(show_all_config);
+
switch (argc) {
case 2:
return get_value(argv[1], NULL);
diff --git a/rev-parse.c b/rev-parse.c
index 7f66ae2..62e16af 100644
--- a/rev-parse.c
+++ b/rev-parse.c
@@ -160,14 +160,6 @@ static int show_file(const char *arg)
return 0;
}
-static void die_badfile(const char *arg)
-{
- if (errno != ENOENT)
- die("'%s': %s", arg, strerror(errno));
- die("'%s' is ambiguous - revision name or file/directory name?\n"
- "Please put '--' before the list of filenames.", arg);
-}
-
int main(int argc, char **argv)
{
int i, as_is = 0, verify = 0;
@@ -177,14 +169,12 @@ int main(int argc, char **argv)
git_config(git_default_config);
for (i = 1; i < argc; i++) {
- struct stat st;
char *arg = argv[i];
char *dotdot;
if (as_is) {
if (show_file(arg) && as_is < 2)
- if (lstat(arg, &st) < 0)
- die_badfile(arg);
+ verify_filename(prefix, arg);
continue;
}
if (!strcmp(arg,"-n")) {
@@ -350,8 +340,7 @@ int main(int argc, char **argv)
continue;
if (verify)
die("Needed a single revision");
- if (lstat(arg, &st) < 0)
- die_badfile(arg);
+ verify_filename(prefix, arg);
}
show_default();
if (verify && revs_count != 1)
diff --git a/revision.c b/revision.c
index f9c7d15..f2a9f25 100644
--- a/revision.c
+++ b/revision.c
@@ -752,17 +752,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
arg++;
}
if (get_sha1(arg, sha1) < 0) {
- struct stat st;
int j;
if (seen_dashdash || local_flags)
die("bad revision '%s'", arg);
/* If we didn't have a "--", all filenames must exist */
- for (j = i; j < argc; j++) {
- if (lstat(argv[j], &st) < 0)
- die("'%s': %s", argv[j], strerror(errno));
- }
+ for (j = i; j < argc; j++)
+ verify_filename(revs->prefix, argv[j]);
+
revs->prune_data = get_pathspec(revs->prefix, argv + i);
break;
}
diff --git a/setup.c b/setup.c
index 36ede3d..cce9bb8 100644
--- a/setup.c
+++ b/setup.c
@@ -62,6 +62,29 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
return path;
}
+/*
+ * Verify a filename that we got as an argument for a pathspec
+ * entry. Note that a filename that begins with "-" never verifies
+ * as true, because even if such a filename were to exist, we want
+ * it to be preceded by the "--" marker (or we want the user to
+ * use a format like "./-filename")
+ */
+void verify_filename(const char *prefix, const char *arg)
+{
+ const char *name;
+ struct stat st;
+
+ if (*arg == '-')
+ die("bad flag '%s' used after filename", arg);
+ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
+ if (!lstat(name, &st))
+ return;
+ if (errno == ENOENT)
+ die("ambiguous argument '%s': unknown revision or filename\n"
+ "Use '--' to separate filenames from revisions", arg);
+ die("'%s': %s", arg, strerror(errno));
+}
+
const char **get_pathspec(const char *prefix, const char **pathspec)
{
const char *entry = *pathspec;
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 6729a18..cf33989 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -174,6 +174,27 @@ test_expect_success \
'git-ls-tree -r output for a known tree.' \
'diff current expected'
+# But with -r -t we can have both.
+test_expect_success \
+ 'showing tree with git-ls-tree -r -t' \
+ 'git-ls-tree -r -t $tree >current'
+cat >expected <<\EOF
+100644 blob f87290f8eb2cbbea7857214459a0739927eab154 path0
+120000 blob 15a98433ae33114b085f3eb3bb03b832b3180a01 path0sym
+040000 tree 58a09c23e2ca152193f2786e06986b7b6712bdbe path2
+100644 blob 3feff949ed00a62d9f7af97c15cd8a30595e7ac7 path2/file2
+120000 blob d8ce161addc5173867a3c3c730924388daedbc38 path2/file2sym
+040000 tree 21ae8269cacbe57ae09138dcc3a2887f904d02b3 path3
+100644 blob 0aa34cae68d0878578ad119c86ca2b5ed5b28376 path3/file3
+120000 blob 8599103969b43aff7e430efea79ca4636466794f path3/file3sym
+040000 tree 3c5e5399f3a333eddecce7a9b9465b63f65f51e2 path3/subp3
+100644 blob 00fb5908cb97c2564a9783c0c64087333b3b464f path3/subp3/file3
+120000 blob 6649a1ebe9e9f1c553b66f5a6e74136a07ccc57c path3/subp3/file3sym
+EOF
+test_expect_success \
+ 'git-ls-tree -r output for a known tree.' \
+ 'diff current expected'
+
################################################################
rm .git/index
test_expect_success \
@@ -205,4 +226,32 @@ test_expect_success \
'no diff after checkout and git-update-index --refresh.' \
'git-diff-files >current && cmp -s current /dev/null'
+################################################################
+P=087704a96baf1c2d1c869a8b084481e121c88b5b
+test_expect_success \
+ 'git-commit-tree records the correct tree in a commit.' \
+ 'commit0=$(echo NO | git-commit-tree $P) &&
+ tree=$(git show --pretty=raw $commit0 |
+ sed -n -e "s/^tree //p" -e "/^author /q") &&
+ test "z$tree" = "z$P"'
+
+test_expect_success \
+ 'git-commit-tree records the correct parent in a commit.' \
+ 'commit1=$(echo NO | git-commit-tree $P -p $commit0) &&
+ parent=$(git show --pretty=raw $commit1 |
+ sed -n -e "s/^parent //p" -e "/^author /q") &&
+ test "z$commit0" = "z$parent"'
+
+test_expect_success \
+ 'git-commit-tree omits duplicated parent in a commit.' \
+ 'commit2=$(echo NO | git-commit-tree $P -p $commit0 -p $commit0) &&
+ parent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ sort -u) &&
+ test "z$commit0" = "z$parent" &&
+ numparent=$(git show --pretty=raw $commit2 |
+ sed -n -e "s/^parent //p" -e "/^author /q" |
+ wc -l) &&
+ test $numparent = 1'
+
test_done
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index d0ed242..75e4c9a 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -37,7 +37,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git-diff-files "$1"`
+ clean_if_empty=`git-diff-files -- "$1"`
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 861ef4c..4d175d8 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -20,7 +20,7 @@ compare_change () {
}
check_cache_at () {
- clean_if_empty=`git-diff-files "$1"`
+ clean_if_empty=`git-diff-files -- "$1"`
case "$clean_if_empty" in
'') echo "$1: clean" ;;
?*) echo "$1: dirty" ;;
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index 8db329d..9e1544d 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -28,7 +28,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path should show nothing' \
- 'git-diff-index --cached $tree path >current &&
+ 'git-diff-index --cached $tree -- path >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -36,7 +36,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path1 should show path1/file1' \
- 'git-diff-index --cached $tree path1 >current &&
+ 'git-diff-index --cached $tree -- path1 >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -44,7 +44,7 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to path1/ should show path1/file1' \
- 'git-diff-index --cached $tree path1/ >current &&
+ 'git-diff-index --cached $tree -- path1/ >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
@@ -52,14 +52,14 @@ cat >expected <<\EOF
EOF
test_expect_success \
'limit to file0 should show file0' \
- 'git-diff-index --cached $tree file0 >current &&
+ 'git-diff-index --cached $tree -- file0 >current &&
compare_diff_raw current expected'
cat >expected <<\EOF
EOF
test_expect_success \
'limit to file0/ should emit nothing.' \
- 'git-diff-index --cached $tree file0/ >current &&
+ 'git-diff-index --cached $tree -- file0/ >current &&
compare_diff_raw current expected'
test_done