Merge branch 'jc/dirwalk-n-cache-tree' into jc/cache-tree
* jc/dirwalk-n-cache-tree: (212 commits) builtin-rm: squelch compiler warnings. Add builtin "git rm" command Move pathspec matching from builtin-add.c into dir.c Prevent bogus paths from being added to the index. builtin-add: fix unmatched pathspec warnings. Remove old "git-add.sh" remnants builtin-add: warn on unmatched pathspecs Do "git add" as a builtin Clean up git-ls-file directory walking library interface libify git-ls-files directory traversal Add a conversion tool to migrate remote information into the config fetch, pull: ask config for remote information Fix build procedure for builtin-init-db read-tree -m -u: do not overwrite or remove untracked working tree files. apply --cached: do not check newly added file in the working tree Implement a --dry-run option to git-quiltimport Implement git-quiltimport Revert "builtin-grep: workaround for non GNU grep." builtin-grep: workaround for non GNU grep. builtin-grep: workaround for non GNU grep. ...
This commit is contained in:
commit
7d55561986
@ -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 $<
|
||||
|
@ -266,8 +266,8 @@ This recipe appears to work with the current [*1*] Thunderbird from Suse.
|
||||
The following Thunderbird extensions are needed:
|
||||
AboutConfig 0.5
|
||||
http://aboutconfig.mozdev.org/
|
||||
External Editor 0.5.4
|
||||
http://extensionroom.mozdev.org/more-info/exteditor
|
||||
External Editor 0.7.2
|
||||
http://globs.org/articles.php?lng=en&pg=8
|
||||
|
||||
1) Prepare the patch as a text file using your method of choice.
|
||||
|
||||
|
16
Documentation/callouts.xsl
Normal file
16
Documentation/callouts.xsl
Normal file
@ -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 </xsl:text>
|
||||
<xsl:apply-templates/>
|
||||
<xsl:text> </xsl:text>
|
||||
</xsl:template>
|
||||
<xsl:template match="callout">
|
||||
<xsl:value-of select="concat('\fB',substring-after(@arearefs,'-'),'. \fR')"/>
|
||||
<xsl:apply-templates/>
|
||||
<xsl:text>.br </xsl:text>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
@ -64,9 +64,11 @@ core.ignoreStat::
|
||||
slow, such as Microsoft Windows. See gitlink:git-update-index[1].
|
||||
False by default.
|
||||
|
||||
core.onlyUseSymrefs::
|
||||
Always use the "symref" format instead of symbolic links for HEAD
|
||||
and other symbolic reference files. True by default.
|
||||
core.preferSymlinkRefs::
|
||||
Instead of the default "symref" format for HEAD
|
||||
and other symbolic reference files, use symbolic links.
|
||||
This is sometimes needed to work with old scripts that
|
||||
expect HEAD to be a symbolic link.
|
||||
|
||||
core.repositoryFormatVersion::
|
||||
Internal variable identifying the repository format and layout
|
||||
|
@ -971,7 +971,7 @@ $ git show-branch --topo-order master mybranch
|
||||
The first two lines indicate that it is showing the two branches
|
||||
and the first line of the commit log message from their
|
||||
top-of-the-tree commits, you are currently on `master` branch
|
||||
(notice the asterisk `*` character), and the first column for
|
||||
(notice the asterisk `\*` character), and the first column for
|
||||
the later output lines is used to show commits contained in the
|
||||
`master` branch, and the second column for the `mybranch`
|
||||
branch. Three commits are shown along with their log messages.
|
||||
|
@ -10,6 +10,10 @@
|
||||
--stat::
|
||||
Generate a diffstat instead of a patch.
|
||||
|
||||
--summary::
|
||||
Output a condensed summary of extended header information
|
||||
such as creations, renames and mode changes.
|
||||
|
||||
--patch-with-stat::
|
||||
Generate patch and prepend its diffstat.
|
||||
|
||||
|
@ -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.
|
||||
------------
|
||||
|
@ -26,7 +26,7 @@ OPTIONS
|
||||
-v::
|
||||
Be verbose.
|
||||
|
||||
--::
|
||||
\--::
|
||||
This option can be used to separate command-line options from
|
||||
the list of files, (useful when filenames might be mistaken
|
||||
for command-line options).
|
||||
|
@ -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
|
||||
------
|
||||
|
@ -63,7 +63,7 @@ OPTIONS
|
||||
Only meaningful with `--stdin`; paths are separated with
|
||||
NUL character instead of LF.
|
||||
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
The order of the flags used to matter, but not anymore.
|
||||
|
@ -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>
|
||||
|
||||
<1> switch branch
|
||||
<2> take out a file out of other commit
|
||||
<3> or "git checkout -- hello.c", as in the next example.
|
||||
$ git checkout hello.c <3>
|
||||
------------
|
||||
+
|
||||
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:
|
||||
<1> switch branch
|
||||
<2> take out a file out of other commit
|
||||
<3> restore hello.c from HEAD of current branch
|
||||
+
|
||||
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
|
||||
|
@ -11,11 +11,20 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Each commit between the fork-point and <head> is examined, and compared against
|
||||
the change each commit between the fork-point and <upstream> introduces.
|
||||
Commits already included in upstream are prefixed with '-' (meaning "drop from
|
||||
my local pull"), while commits missing from upstream are prefixed with '+'
|
||||
(meaning "add to the updated upstream").
|
||||
The changeset (or "diff") of each commit between the fork-point and <head>
|
||||
is compared against each commit between the fork-point and <upstream>.
|
||||
|
||||
Every commit with a changeset that doesn't exist in the other branch
|
||||
has its id (sha1) reported, prefixed by a symbol. Those existing only
|
||||
in the <upstream> branch are prefixed with a minus (-) sign, and those
|
||||
that only exist in the <head> branch are prefixed with a plus (+) symbol.
|
||||
|
||||
Because git-cherry compares the changeset rather than the commit id
|
||||
(sha1), you can use git-cherry to find out if a commit you made locally
|
||||
has been applied <upstream> under a different commit id. For example,
|
||||
this will happen if you're feeding patches <upstream> via email rather
|
||||
than pushing or pulling commits directly.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-clean' [-d] [-n] [-q] [-x | -X]
|
||||
'git-clean' [-d] [-n] [-q] [-x | -X] [--] <paths>...
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -16,6 +16,9 @@ Removes files unknown to git. This allows to clean the working tree
|
||||
from files that are not under version control. If the '-x' option is
|
||||
specified, ignored files are also removed, allowing to remove all
|
||||
build products.
|
||||
When optional `<paths>...` arguments are given, the paths
|
||||
affected are further limited to those that match them.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
@ -101,7 +101,7 @@ OPTIONS
|
||||
is not allowed.
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
--------
|
||||
|
||||
Clone from upstream::
|
||||
+
|
||||
|
@ -106,7 +106,7 @@ but can be used to amend a merge commit.
|
||||
index and the latest commit does not match on the
|
||||
specified paths to avoid confusion.
|
||||
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
<file>...::
|
||||
|
@ -7,13 +7,23 @@ git-count-objects - Reports on unpacked objects
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-count-objects'
|
||||
'git-count-objects' [-v]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
This counts the number of unpacked object files and disk space consumed by
|
||||
them, to help you decide when it is a good time to repack.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-v::
|
||||
In addition to the number of loose objects and disk
|
||||
space consumed, it reports the number of in-pack
|
||||
objects, and number of objects that can be removed by
|
||||
running `git-prune-packed`.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <junkio@cox.net>
|
||||
|
@ -8,7 +8,7 @@ git-cvsexportcommit - Export a commit to a CVS checkout
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [PARENTCOMMIT] COMMITID
|
||||
'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
@ -39,6 +39,13 @@ OPTIONS
|
||||
Be pedantic (paranoid) when applying patches. Invokes patch with
|
||||
--fuzz=0
|
||||
|
||||
-f::
|
||||
Force the merge even if the files are not up to date.
|
||||
|
||||
-m::
|
||||
Prepend the commit message with the provided prefix.
|
||||
Useful for patch series and the like.
|
||||
|
||||
-v::
|
||||
Verbose.
|
||||
|
||||
|
@ -92,7 +92,7 @@ separated with a single space are given.
|
||||
Furthermore, it lists only files which were modified
|
||||
from all parents.
|
||||
|
||||
-cc::
|
||||
--cc::
|
||||
This flag changes the way a merge commit patch is displayed,
|
||||
in a similar way to the '-c' option. It implies the '-c'
|
||||
and '-p' options and further compresses the patch output
|
||||
|
@ -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
|
||||
|
@ -8,43 +8,82 @@ git-grep - Print lines matching a pattern
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-grep' [<option>...] [-e] <pattern> [--] [<path>...]
|
||||
[verse]
|
||||
'git-grep' [--cached]
|
||||
[-a | --text] [-I] [-i | --ignore-case] [-w | --word-regexp]
|
||||
[-v | --invert-match]
|
||||
[-E | --extended-regexp] [-G | --basic-regexp] [-F | --fixed-strings]
|
||||
[-n] [-l | --files-with-matches] [-L | --files-without-match]
|
||||
[-c | --count]
|
||||
[-A <post-context>] [-B <pre-context>] [-C <context>]
|
||||
[-f <file>] [-e <pattern>]
|
||||
[<tree>...]
|
||||
[--] [<path>...]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Searches list of files `git-ls-files` produces for lines
|
||||
containing a match to the given pattern.
|
||||
Look for specified patterns in the working tree files, blobs
|
||||
registered in the index file, or given tree objects.
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--cached::
|
||||
Instead of searching in the working tree files, check
|
||||
the blobs registerd in the index file.
|
||||
|
||||
-a | --text::
|
||||
Process binary files as if they were text.
|
||||
|
||||
-i | --ignore-case::
|
||||
Ignore case differences between the patterns and the
|
||||
files.
|
||||
|
||||
-w | --word-regexp::
|
||||
Match the pattern only at word boundary (either begin at the
|
||||
beginning of a line, or preceded by a non-word character; end at
|
||||
the end of a line or followed by a non-word character).
|
||||
|
||||
-v | --invert-match::
|
||||
Select non-matching lines.
|
||||
|
||||
-E | --extended-regexp | -G | --basic-regexp::
|
||||
Use POSIX extended/basic regexp for patterns. Default
|
||||
is to use basic regexp.
|
||||
|
||||
-n::
|
||||
Prefix the line number to matching lines.
|
||||
|
||||
-l | --files-with-matches | -L | --files-without-match::
|
||||
Instead of showing every matched line, show only the
|
||||
names of files that contain (or do not contain) matches.
|
||||
|
||||
-c | --count::
|
||||
Instead of showing every matched line, show the number of
|
||||
lines that match.
|
||||
|
||||
-[ABC] <context>::
|
||||
Show `context` trailing (`A` -- after), or leading (`B`
|
||||
-- before), or both (`C` -- context) lines, and place a
|
||||
line containing `--` between continguous groups of
|
||||
matches.
|
||||
|
||||
-f <file>::
|
||||
Read patterns from <file>, one per line.
|
||||
|
||||
`<tree>...`::
|
||||
Search blobs in the trees for specified patterns.
|
||||
|
||||
`--`::
|
||||
Signals the end of options; the rest of the parameters
|
||||
are <path> limiters.
|
||||
|
||||
<option>...::
|
||||
Either an option to pass to `grep` or `git-ls-files`.
|
||||
+
|
||||
The following are the specific `git-ls-files` options
|
||||
that may be given: `-o`, `--cached`, `--deleted`, `--others`,
|
||||
`--killed`, `--ignored`, `--modified`, `--exclude=\*`,
|
||||
`--exclude-from=\*`, and `--exclude-per-directory=\*`.
|
||||
+
|
||||
All other options will be passed to `grep`.
|
||||
|
||||
<pattern>::
|
||||
The pattern to look for. The first non option is taken
|
||||
as the pattern; if your pattern begins with a dash, use
|
||||
`-e <pattern>`.
|
||||
|
||||
<path>...::
|
||||
Optional paths to limit the set of files to be searched;
|
||||
passed to `git-ls-files`.
|
||||
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Linus Torvalds <torvalds@osdl.org>
|
||||
Originally written by Linus Torvalds <torvalds@osdl.org>, later
|
||||
revamped by Junio C Hamano.
|
||||
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
|
@ -29,6 +29,7 @@ CONFIGURATION
|
||||
git-imap-send requires the following values in the repository
|
||||
configuration file (shown with examples):
|
||||
|
||||
..........................
|
||||
[imap]
|
||||
Folder = "INBOX.Drafts"
|
||||
|
||||
@ -38,8 +39,9 @@ configuration file (shown with examples):
|
||||
[imap]
|
||||
Host = imap.server.com
|
||||
User = bob
|
||||
Password = pwd
|
||||
Pass = pwd
|
||||
Port = 143
|
||||
..........................
|
||||
|
||||
|
||||
BUGS
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -52,7 +51,7 @@ git log v2.6.12.. include/scsi drivers/scsi::
|
||||
Show all commits since version 'v2.6.12' that changed any file
|
||||
in the include/scsi or drivers/scsi subdirectories
|
||||
|
||||
git log --since="2 weeks ago" -- gitk::
|
||||
git log --since="2 weeks ago" \-- gitk::
|
||||
|
||||
Show the changes during the last two weeks to the file 'gitk'.
|
||||
The "--" is necessary to avoid confusion with the *branch* named
|
||||
|
@ -106,7 +106,7 @@ OPTIONS
|
||||
lines, show only handful hexdigits prefix.
|
||||
Non default number of digits can be specified with --abbrev=<n>.
|
||||
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
<file>::
|
||||
|
@ -8,16 +8,26 @@ git-merge-base - Finds as good a common ancestor as possible for a merge
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-merge-base' <commit> <commit>
|
||||
'git-merge-base' [--all] <commit> <commit>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
"git-merge-base" finds as good a common ancestor as possible. Given a
|
||||
selection of equally good common ancestors it should not be relied on
|
||||
to decide in any particular way.
|
||||
|
||||
"git-merge-base" finds as good a common ancestor as possible between
|
||||
the two commits. That is, given two commits A and B 'git-merge-base A
|
||||
B' will output a commit which is reachable from both A and B through
|
||||
the parent relationship.
|
||||
|
||||
Given a selection of equally good common ancestors it should not be
|
||||
relied on to decide in any particular way.
|
||||
|
||||
The "git-merge-base" algorithm is still in flux - use the source...
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--all::
|
||||
Output all common ancestors for the two commits instead of
|
||||
just one.
|
||||
|
||||
Author
|
||||
------
|
||||
|
@ -8,7 +8,7 @@ git-merge-index - Runs a merge for files needing merging
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-merge-index' [-o] [-q] <merge-program> (-a | -- | <file>\*)
|
||||
'git-merge-index' [-o] [-q] <merge-program> (-a | \-- | <file>\*)
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@ -19,7 +19,7 @@ files are passed as arguments 5, 6 and 7.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
-a::
|
||||
|
@ -41,6 +41,7 @@ Enter git-name-rev:
|
||||
|
||||
------------
|
||||
% git name-rev 33db5f4d9027a10e477ccf054b2c1ab94f74c85a
|
||||
33db5f4d9027a10e477ccf054b2c1ab94f74c85a tags/v0.99^0~940
|
||||
------------
|
||||
|
||||
Now you are wiser, because you know that it happened 940 revisions before v0.99.
|
||||
|
@ -28,7 +28,7 @@ OPTIONS
|
||||
Do not remove anything; just report what it would
|
||||
remove.
|
||||
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
<head>...::
|
||||
|
61
Documentation/git-quiltimport.txt
Normal file
61
Documentation/git-quiltimport.txt
Normal file
@ -0,0 +1,61 @@
|
||||
git-quiltimport(1)
|
||||
================
|
||||
|
||||
NAME
|
||||
----
|
||||
git-quiltimport - Applies a quilt patchset onto the current branch
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-quiltimport' [--dry-run] [--author <author>] [--patches <dir>]
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Applies a quilt patchset onto the current git branch, preserving
|
||||
the patch boundaries, patch order, and patch descriptions present
|
||||
in the quilt patchset.
|
||||
|
||||
For each patch the code attempts to extract the author from the
|
||||
patch description. If that fails it falls back to the author
|
||||
specified with --author. If the --author flag was not given
|
||||
the patch description is displayed and the user is asked to
|
||||
interactively enter the author of the patch.
|
||||
|
||||
If a subject is not found in the patch description the patch name is
|
||||
preserved as the 1 line subject in the git description.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
--dry-run::
|
||||
Walk through the patches in the series and warn
|
||||
if we cannot find all of the necessary information to commit
|
||||
a patch. At the time of this writing only missing author
|
||||
information is warned about.
|
||||
|
||||
--author Author Name <Author Email>::
|
||||
The author name and email address to use when no author
|
||||
information can be found in the patch description.
|
||||
|
||||
--patches <dir>::
|
||||
The directory to find the quilt patches and the
|
||||
quilt series file.
|
||||
|
||||
The default for the patch directory is patches
|
||||
or the value of the $QUILT_PATCHES environment
|
||||
variable.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Eric Biederman <ebiederm@lnxi.com>
|
||||
|
||||
Documentation
|
||||
--------------
|
||||
Documentation by Eric Biederman <ebiederm@lnxi.com>
|
||||
|
||||
GIT
|
||||
---
|
||||
Part of the gitlink:git[7] suite
|
||||
|
@ -3,38 +3,53 @@ git-rebase(1)
|
||||
|
||||
NAME
|
||||
----
|
||||
git-rebase - Rebase local commits to new upstream head
|
||||
git-rebase - Rebase local commits to a new head
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
'git-rebase' [--onto <newbase>] <upstream> [<branch>]
|
||||
|
||||
'git-rebase' --continue | --skip | --abort
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
git-rebase applies to <upstream> (or optionally to <newbase>) commits
|
||||
from <branch> that do not appear in <upstream>. When <branch> is not
|
||||
specified it defaults to the current branch (HEAD).
|
||||
git-rebase replaces <branch> with a new branch of the same name. When
|
||||
the --onto option is provided the new branch starts out with a HEAD equal
|
||||
to <newbase>, otherwise it is equal to <upstream>. It then attempts to
|
||||
create a new commit for each commit from the original <branch> that does
|
||||
not exist in the <upstream> branch.
|
||||
|
||||
When git-rebase is complete, <branch> will be updated to point to the
|
||||
newly created line of commit objects, so the previous line will not be
|
||||
accessible unless there are other references to it already.
|
||||
It is possible that a merge failure will prevent this process from being
|
||||
completely automatic. You will have to resolve any such merge failure
|
||||
and run `git rebase --continue`. Another option is to bypass the commit
|
||||
that caused the merge failure with `git rebase --skip`. To restore the
|
||||
original <branch> and remove the .dotest working files, use the command
|
||||
`git rebase --abort` instead.
|
||||
|
||||
Note that if <branch> is not specified on the command line, the currently
|
||||
checked out branch is used.
|
||||
|
||||
Assume the following history exists and the current branch is "topic":
|
||||
|
||||
------------
|
||||
A---B---C topic
|
||||
/
|
||||
D---E---F---G master
|
||||
------------
|
||||
|
||||
From this point, the result of either of the following commands:
|
||||
|
||||
|
||||
git-rebase master
|
||||
git-rebase master topic
|
||||
|
||||
would be:
|
||||
|
||||
------------
|
||||
A'--B'--C' topic
|
||||
/
|
||||
D---E---F---G master
|
||||
------------
|
||||
|
||||
While, starting from the same point, the result of either of the following
|
||||
commands:
|
||||
@ -44,21 +59,33 @@ commands:
|
||||
|
||||
would be:
|
||||
|
||||
------------
|
||||
A'--B'--C' topic
|
||||
/
|
||||
D---E---F---G master
|
||||
------------
|
||||
|
||||
In case of conflict, git-rebase will stop at the first problematic commit
|
||||
and leave conflict markers in the tree. After resolving the conflict manually
|
||||
and updating the index with the desired resolution, you can continue the
|
||||
rebasing process with
|
||||
and leave conflict markers in the tree. You can use git diff to locate
|
||||
the markers (<<<<<<) and make edits to resolve the conflict. For each
|
||||
file you edit, you need to tell git that the conflict has been resolved,
|
||||
typically this would be done with
|
||||
|
||||
|
||||
git update-index <filename>
|
||||
|
||||
|
||||
After resolving the conflict manually and updating the index with the
|
||||
desired resolution, you can continue the rebasing process with
|
||||
|
||||
|
||||
git rebase --continue
|
||||
|
||||
git am --resolved --3way
|
||||
|
||||
Alternatively, you can undo the git-rebase with
|
||||
|
||||
git reset --hard ORIG_HEAD
|
||||
rm -r .dotest
|
||||
|
||||
git rebase --abort
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
@ -73,6 +100,28 @@ OPTIONS
|
||||
<branch>::
|
||||
Working branch; defaults to HEAD.
|
||||
|
||||
--continue::
|
||||
Restart the rebasing process after having resolved a merge conflict.
|
||||
|
||||
--abort::
|
||||
Restore the original branch and abort the rebase operation.
|
||||
|
||||
NOTES
|
||||
-----
|
||||
When you rebase a branch, you are changing its history in a way that
|
||||
will cause problems for anyone who already has a copy of the branch
|
||||
in their repository and tries to pull updates from you. You should
|
||||
understand the implications of using 'git rebase' on a repository that
|
||||
you share.
|
||||
|
||||
When the git rebase command is run, it will first execute a "pre-rebase"
|
||||
hook if one exists. You can use this hook to do sanity checks and
|
||||
reject the rebase if it isn't appropriate. Please see the template
|
||||
pre-rebase hook script for an example.
|
||||
|
||||
You must be in the top directory of your project to start (or continue)
|
||||
a rebase. Upon completion, <branch> will be the current branch.
|
||||
|
||||
Author
|
||||
------
|
||||
Written by Junio C Hamano <junkio@cox.net>
|
||||
|
@ -38,6 +38,7 @@ OPTIONS
|
||||
-d::
|
||||
After packing, if the newly created packs make some
|
||||
existing packs redundant, remove the redundant packs.
|
||||
Also runs gitlink:git-prune-packed[1].
|
||||
|
||||
-l::
|
||||
Pass the `--local` option to `git pack-objects`, see
|
||||
|
@ -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
|
||||
-----------
|
||||
@ -22,10 +23,11 @@ You can query/set/replace/unset options with this command. The name is
|
||||
actually the section and the key separated by a dot, and the value will be
|
||||
escaped.
|
||||
|
||||
If you want to set/unset an option which can occur on multiple lines, you
|
||||
should provide a POSIX regex for the value. If you want to handle the lines
|
||||
*not* matching the regex, just prepend a single exclamation mark in front
|
||||
(see EXAMPLES).
|
||||
If you want to set/unset an option which can occur on multiple
|
||||
lines, a POSIX regexp `value_regex` needs to be given. Only the
|
||||
existing values that match the regexp are updated or unset. If
|
||||
you want to handle the lines that do *not* match the regex, just
|
||||
prepend a single exclamation mark in front (see EXAMPLES).
|
||||
|
||||
The type specifier can be either '--int' or '--bool', which will make
|
||||
'git-repo-config' ensure that the variable(s) are of the given type and
|
||||
@ -33,10 +35,10 @@ convert the value to the canonical form (simple decimal number for int,
|
||||
a "true" or "false" string for bool). If no type specifier is passed,
|
||||
no checks or transformations are performed on the value.
|
||||
|
||||
This command will fail if
|
||||
This command will fail if:
|
||||
|
||||
. .git/config is invalid,
|
||||
. .git/config can not be written to,
|
||||
. The .git/config file is invalid,
|
||||
. Can not write to .git/config,
|
||||
. no section was provided,
|
||||
. the section or key is invalid,
|
||||
. you try to unset an option which does not exist, or
|
||||
@ -48,7 +50,7 @@ OPTIONS
|
||||
|
||||
--replace-all::
|
||||
Default behaviour is to replace at most one line. This replaces
|
||||
all lines matching the key (and optionally the value_regex)
|
||||
all lines matching the key (and optionally the value_regex).
|
||||
|
||||
--get::
|
||||
Get the value for a given key (optionally filtered by a regex
|
||||
@ -58,12 +60,18 @@ OPTIONS
|
||||
Like get, but does not fail if the number of values for the key
|
||||
is not exactly one.
|
||||
|
||||
--get-regexp::
|
||||
Like --get-all, but interprets the name as a regular expression.
|
||||
|
||||
--unset::
|
||||
Remove the line matching the key from .git/config.
|
||||
|
||||
--unset-all::
|
||||
Remove all matching lines from .git/config.
|
||||
|
||||
-l, --list::
|
||||
List all variables set in .git/config.
|
||||
|
||||
|
||||
EXAMPLE
|
||||
-------
|
||||
|
@ -43,16 +43,17 @@ OPTIONS
|
||||
Commit to make the current HEAD.
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
--------
|
||||
|
||||
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
|
||||
------
|
||||
|
@ -68,9 +68,10 @@ OPTIONS
|
||||
--bisect::
|
||||
Limit output to the one commit object which is roughly halfway
|
||||
between the included and excluded commits. Thus, if 'git-rev-list
|
||||
--bisect foo ^bar ^baz' outputs 'midpoint', the output
|
||||
of 'git-rev-list foo ^midpoint' and 'git-rev-list midpoint
|
||||
^bar ^baz' would be of roughly the same length. Finding the change
|
||||
--bisect foo {caret}bar {caret}baz' outputs 'midpoint', the output
|
||||
of 'git-rev-list foo {caret}midpoint' and 'git-rev-list midpoint
|
||||
{caret}bar {caret}baz' would be of roughly the same length.
|
||||
Finding the change
|
||||
which introduces a regression is thus reduced to a binary search:
|
||||
repeatedly generate and test new 'midpoint's until the commit chain
|
||||
is of length one.
|
||||
|
@ -67,6 +67,15 @@ OPTIONS
|
||||
--all::
|
||||
Show all refs found in `$GIT_DIR/refs`.
|
||||
|
||||
--branches::
|
||||
Show branch refs found in `$GIT_DIR/refs/heads`.
|
||||
|
||||
--tags::
|
||||
Show tag refs found in `$GIT_DIR/refs/tags`.
|
||||
|
||||
--remotes::
|
||||
Show tag refs found in `$GIT_DIR/refs/remotes`.
|
||||
|
||||
--show-prefix::
|
||||
When the command is invoked from a subdirectory, show the
|
||||
path of the current directory relative to the top-level
|
||||
|
@ -32,7 +32,7 @@ OPTIONS
|
||||
-v::
|
||||
Be verbose.
|
||||
|
||||
--::
|
||||
\--::
|
||||
This option can be used to separate command-line options from
|
||||
the list of files, (useful when filenames might be mistaken
|
||||
for command-line options).
|
||||
|
@ -13,9 +13,16 @@ SYNOPSIS
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
Reads a packed archive (.pack) from the standard input, and
|
||||
expands the objects contained in the pack into "one-file
|
||||
one-object" format in $GIT_OBJECT_DIRECTORY.
|
||||
Read a packed archive (.pack) from the standard input, expanding
|
||||
the objects contained within and writing them into the repository in
|
||||
"loose" (one object per file) format.
|
||||
|
||||
Objects that already exist in the repository will *not* be unpacked
|
||||
from the pack-file. Therefore, nothing will be unpacked if you use
|
||||
this command on a pack-file that exists within the target repository.
|
||||
|
||||
Please see the `git-repack` documentation for options to generate
|
||||
new packs and replace existing ones.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
@ -10,12 +10,12 @@ SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'git-update-index'
|
||||
[--add] [--remove | --force-remove] [--replace]
|
||||
[--refresh [-q] [--unmerged] [--ignore-missing]]
|
||||
[--add] [--remove | --force-remove] [--replace]
|
||||
[--refresh] [-q] [--unmerged] [--ignore-missing]
|
||||
[--cacheinfo <mode> <object> <file>]\*
|
||||
[--chmod=(+|-)x]
|
||||
[--assume-unchanged | --no-assume-unchanged]
|
||||
[--really-refresh]
|
||||
[--really-refresh] [--unresolve] [--again]
|
||||
[--info-only] [--index-info]
|
||||
[-z] [--stdin]
|
||||
[--verbose]
|
||||
@ -80,6 +80,14 @@ OPTIONS
|
||||
filesystem that has very slow lstat(2) system call
|
||||
(e.g. cifs).
|
||||
|
||||
--again::
|
||||
Runs `git-update-index` itself on the paths whose index
|
||||
entries are different from those from the `HEAD` commit.
|
||||
|
||||
--unresolve::
|
||||
Restores the 'unmerged' or 'needs updating' state of a
|
||||
file during a merge if it was cleared by accident.
|
||||
|
||||
--info-only::
|
||||
Do not create objects in the object database for all
|
||||
<file> arguments that follow this flag; just insert
|
||||
@ -109,7 +117,7 @@ OPTIONS
|
||||
Only meaningful with `--stdin`; paths are separated with
|
||||
NUL character instead of LF.
|
||||
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
<file>::
|
||||
@ -247,34 +255,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
|
||||
|
@ -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
|
||||
--------
|
||||
|
@ -25,7 +25,7 @@ OPTIONS
|
||||
-v::
|
||||
After verifying the pack, show list of objects contained
|
||||
in the pack.
|
||||
--::
|
||||
\--::
|
||||
Do not interpret any more arguments as options.
|
||||
|
||||
OUTPUT FORMAT
|
||||
|
@ -58,7 +58,7 @@ git-whatchanged -p v2.6.12.. include/scsi drivers/scsi::
|
||||
Show as patches the commits since version 'v2.6.12' that changed
|
||||
any file in the include/scsi or drivers/scsi subdirectories
|
||||
|
||||
git-whatchanged --since="2 weeks ago" -- gitk::
|
||||
git-whatchanged --since="2 weeks ago" \-- gitk::
|
||||
|
||||
Show the changes during the last two weeks to the file 'gitk'.
|
||||
The "--" is necessary to avoid confusion with the *branch* named
|
||||
|
@ -31,7 +31,7 @@ gitk v2.6.12.. include/scsi drivers/scsi::
|
||||
Show as the changes since version 'v2.6.12' that changed any
|
||||
file in the include/scsi or drivers/scsi subdirectories
|
||||
|
||||
gitk --since="2 weeks ago" -- gitk::
|
||||
gitk --since="2 weeks ago" \-- gitk::
|
||||
|
||||
Show the changes during the last two weeks to the file 'gitk'.
|
||||
The "--" is necessary to avoid confusion with the *branch* named
|
||||
|
@ -1,39 +1,71 @@
|
||||
object::
|
||||
The unit of storage in git. It is uniquely identified by
|
||||
the SHA1 of its contents. Consequently, an object can not
|
||||
be changed.
|
||||
alternate object database::
|
||||
Via the alternates mechanism, a repository can inherit part of its
|
||||
object database from another object database, which is called
|
||||
"alternate".
|
||||
|
||||
object name::
|
||||
The unique identifier of an object. The hash of the object's contents
|
||||
using the Secure Hash Algorithm 1 and usually represented by the 40
|
||||
character hexadecimal encoding of the hash of the object (possibly
|
||||
followed by a white space).
|
||||
|
||||
SHA1::
|
||||
Synonym for object name.
|
||||
|
||||
object identifier::
|
||||
Synonym for object name.
|
||||
|
||||
hash::
|
||||
In git's context, synonym to object name.
|
||||
|
||||
object database::
|
||||
Stores a set of "objects", and an individual object is identified
|
||||
by its object name. The objects usually live in `$GIT_DIR/objects/`.
|
||||
bare repository::
|
||||
A bare repository is normally an appropriately named
|
||||
directory with a `.git` suffix that does not have a
|
||||
locally checked-out copy of any of the files under revision
|
||||
control. That is, all of the `git` administrative and
|
||||
control files that would normally be present in the
|
||||
hidden `.git` sub-directory are directly present in
|
||||
the `repository.git` directory instead, and no other files
|
||||
are present and checked out. Usually publishers of public
|
||||
repositories make bare repositories available.
|
||||
|
||||
blob object::
|
||||
Untyped object, e.g. the contents of a file.
|
||||
|
||||
tree object::
|
||||
An object containing a list of file names and modes along with refs
|
||||
to the associated blob and/or tree objects. A tree is equivalent
|
||||
to a directory.
|
||||
branch::
|
||||
A non-cyclical graph of revisions, i.e. the complete history of
|
||||
a particular revision, which is called the branch head. The
|
||||
branch heads are stored in `$GIT_DIR/refs/heads/`.
|
||||
|
||||
tree::
|
||||
Either a working tree, or a tree object together with the
|
||||
dependent blob and tree objects (i.e. a stored representation
|
||||
of a working tree).
|
||||
cache::
|
||||
Obsolete for: index.
|
||||
|
||||
chain::
|
||||
A list of objects, where each object in the list contains a
|
||||
reference to its successor (for example, the successor of a commit
|
||||
could be one of its parents).
|
||||
|
||||
changeset::
|
||||
BitKeeper/cvsps speak for "commit". Since git does not store
|
||||
changes, but states, it really does not make sense to use
|
||||
the term "changesets" with git.
|
||||
|
||||
checkout::
|
||||
The action of updating the working tree to a revision which was
|
||||
stored in the object database.
|
||||
|
||||
cherry-picking::
|
||||
In SCM jargon, "cherry pick" means to choose a subset of
|
||||
changes out of a series of changes (typically commits)
|
||||
and record them as a new series of changes on top of
|
||||
different codebase. In GIT, this is performed by
|
||||
"git cherry-pick" command to extract the change
|
||||
introduced by an existing commit and to record it based
|
||||
on the tip of the current branch as a new commit.
|
||||
|
||||
clean::
|
||||
A working tree is clean, if it corresponds to the revision
|
||||
referenced by the current head. Also see "dirty".
|
||||
|
||||
commit::
|
||||
As a verb: The action of storing the current state of the index in the
|
||||
object database. The result is a revision.
|
||||
As a noun: Short hand for commit object.
|
||||
|
||||
commit object::
|
||||
An object which contains the information about a particular
|
||||
revision, such as parents, committer, author, date and the
|
||||
tree object which corresponds to the top directory of the
|
||||
stored revision.
|
||||
|
||||
core git::
|
||||
Fundamental data structures and utilities of git. Exposes only
|
||||
limited source code management tools.
|
||||
|
||||
DAG::
|
||||
Directed acyclic graph. The commit objects form a directed acyclic
|
||||
@ -41,6 +73,63 @@ DAG::
|
||||
objects is acyclic (there is no chain which begins and ends with the
|
||||
same object).
|
||||
|
||||
dircache::
|
||||
You are *waaaaay* behind.
|
||||
|
||||
dirty::
|
||||
A working tree is said to be dirty if it contains modifications
|
||||
which have not been committed to the current branch.
|
||||
|
||||
directory::
|
||||
The list you get with "ls" :-)
|
||||
|
||||
ent::
|
||||
Favorite synonym to "tree-ish" by some total geeks. See
|
||||
`http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
|
||||
explanation.
|
||||
|
||||
fast forward::
|
||||
A fast-forward is a special type of merge where you have
|
||||
a revision and you are "merging" another branch's changes
|
||||
that happen to be a descendant of what you have.
|
||||
In such these cases, you do not make a new merge commit but
|
||||
instead just update to his revision. This will happen
|
||||
frequently on a tracking branch of a remote repository.
|
||||
|
||||
fetch::
|
||||
Fetching a branch means to get the branch's head ref from a
|
||||
remote repository, to find out which objects are missing from
|
||||
the local object database, and to get them, too.
|
||||
|
||||
file system::
|
||||
Linus Torvalds originally designed git to be a user space file
|
||||
system, i.e. the infrastructure to hold files and directories.
|
||||
That ensured the efficiency and speed of git.
|
||||
|
||||
git archive::
|
||||
Synonym for repository (for arch people).
|
||||
|
||||
hash::
|
||||
In git's context, synonym to object name.
|
||||
|
||||
head::
|
||||
The top of a branch. It contains a ref to the corresponding
|
||||
commit object.
|
||||
|
||||
head ref::
|
||||
A ref pointing to a head. Often, this is abbreviated to "head".
|
||||
Head refs are stored in `$GIT_DIR/refs/heads/`.
|
||||
|
||||
hook::
|
||||
During the normal execution of several git commands,
|
||||
call-outs are made to optional scripts that allow
|
||||
a developer to add functionality or checking.
|
||||
Typically, the hooks allow for a command to be pre-verified
|
||||
and potentially aborted, and allow for a post-notification
|
||||
after the operation is done.
|
||||
The hook scripts are found in the `$GIT_DIR/hooks/` directory,
|
||||
and are enabled by simply making them executable.
|
||||
|
||||
index::
|
||||
A collection of files with stat information, whose contents are
|
||||
stored as objects. The index is a stored version of your working
|
||||
@ -53,92 +142,167 @@ index entry::
|
||||
yet finished (i.e. if the index contains multiple versions of
|
||||
that file).
|
||||
|
||||
unmerged index:
|
||||
An index which contains unmerged index entries.
|
||||
master::
|
||||
The default development branch. Whenever you create a git
|
||||
repository, a branch named "master" is created, and becomes
|
||||
the active branch. In most cases, this contains the local
|
||||
development, though that is purely conventional and not required.
|
||||
|
||||
cache::
|
||||
Obsolete for: index.
|
||||
merge::
|
||||
To merge branches means to try to accumulate the changes since a
|
||||
common ancestor and apply them to the first branch. An automatic
|
||||
merge uses heuristics to accomplish that. Evidently, an automatic
|
||||
merge can fail.
|
||||
|
||||
working tree::
|
||||
The set of files and directories currently being worked on,
|
||||
i.e. you can work in your working tree without using git at all.
|
||||
object::
|
||||
The unit of storage in git. It is uniquely identified by
|
||||
the SHA1 of its contents. Consequently, an object can not
|
||||
be changed.
|
||||
|
||||
directory::
|
||||
The list you get with "ls" :-)
|
||||
object database::
|
||||
Stores a set of "objects", and an individual object is identified
|
||||
by its object name. The objects usually live in `$GIT_DIR/objects/`.
|
||||
|
||||
revision::
|
||||
A particular state of files and directories which was stored in
|
||||
the object database. It is referenced by a commit object.
|
||||
object identifier::
|
||||
Synonym for object name.
|
||||
|
||||
checkout::
|
||||
The action of updating the working tree to a revision which was
|
||||
stored in the object database.
|
||||
object name::
|
||||
The unique identifier of an object. The hash of the object's contents
|
||||
using the Secure Hash Algorithm 1 and usually represented by the 40
|
||||
character hexadecimal encoding of the hash of the object (possibly
|
||||
followed by a white space).
|
||||
|
||||
commit::
|
||||
As a verb: The action of storing the current state of the index in the
|
||||
object database. The result is a revision.
|
||||
As a noun: Short hand for commit object.
|
||||
object type:
|
||||
One of the identifiers "commit","tree","tag" and "blob" describing
|
||||
the type of an object.
|
||||
|
||||
commit object::
|
||||
An object which contains the information about a particular
|
||||
revision, such as parents, committer, author, date and the
|
||||
tree object which corresponds to the top directory of the
|
||||
stored revision.
|
||||
octopus::
|
||||
To merge more than two branches. Also denotes an intelligent
|
||||
predator.
|
||||
|
||||
origin::
|
||||
The default upstream tracking branch. Most projects have at
|
||||
least one upstream project which they track. By default
|
||||
'origin' is used for that purpose. New upstream updates
|
||||
will be fetched into this branch; you should never commit
|
||||
to it yourself.
|
||||
|
||||
pack::
|
||||
A set of objects which have been compressed into one file (to save
|
||||
space or to transmit them efficiently).
|
||||
|
||||
pack index::
|
||||
The list of identifiers, and other information, of the objects in a
|
||||
pack, to assist in efficiently accessing the contents of a pack.
|
||||
|
||||
parent::
|
||||
A commit object contains a (possibly empty) list of the logical
|
||||
predecessor(s) in the line of development, i.e. its parents.
|
||||
|
||||
changeset::
|
||||
BitKeeper/cvsps speak for "commit". Since git does not store
|
||||
changes, but states, it really does not make sense to use
|
||||
the term "changesets" with git.
|
||||
pickaxe::
|
||||
The term pickaxe refers to an option to the diffcore routines
|
||||
that help select changes that add or delete a given text string.
|
||||
With the --pickaxe-all option, it can be used to view the
|
||||
full changeset that introduced or removed, say, a particular
|
||||
line of text. See gitlink:git-diff[1].
|
||||
|
||||
clean::
|
||||
A working tree is clean, if it corresponds to the revision
|
||||
referenced by the current head.
|
||||
plumbing::
|
||||
Cute name for core git.
|
||||
|
||||
dirty::
|
||||
A working tree is said to be dirty if it contains modifications
|
||||
which have not been committed to the current branch.
|
||||
porcelain::
|
||||
Cute name for programs and program suites depending on core git,
|
||||
presenting a high level access to core git. Porcelains expose
|
||||
more of a SCM interface than the plumbing.
|
||||
|
||||
head::
|
||||
The top of a branch. It contains a ref to the corresponding
|
||||
commit object.
|
||||
pull::
|
||||
Pulling a branch means to fetch it and merge it.
|
||||
|
||||
branch::
|
||||
A non-cyclical graph of revisions, i.e. the complete history of
|
||||
a particular revision, which is called the branch head. The
|
||||
branch heads are stored in `$GIT_DIR/refs/heads/`.
|
||||
push::
|
||||
Pushing a branch means to get the branch's head ref from a remote
|
||||
repository, find out if it is an ancestor to the branch's local
|
||||
head ref is a direct, and in that case, putting all objects, which
|
||||
are reachable from the local head ref, and which are missing from
|
||||
the remote repository, into the remote object database, and updating
|
||||
the remote head ref. If the remote head is not an ancestor to the
|
||||
local head, the push fails.
|
||||
|
||||
master::
|
||||
The default branch. Whenever you create a git repository, a branch
|
||||
named "master" is created, and becomes the active branch. In most
|
||||
cases, this contains the local development.
|
||||
reachable::
|
||||
An object is reachable from a ref/commit/tree/tag, if there is a
|
||||
chain leading from the latter to the former.
|
||||
|
||||
origin::
|
||||
The default upstream branch. Most projects have one upstream
|
||||
project which they track, and by default 'origin' is used for
|
||||
that purpose. New updates from upstream will be fetched into
|
||||
this branch; you should never commit to it yourself.
|
||||
rebase::
|
||||
To clean a branch by starting from the head of the main line of
|
||||
development ("master"), and reapply the (possibly cherry-picked)
|
||||
changes from that branch.
|
||||
|
||||
ref::
|
||||
A 40-byte hex representation of a SHA1 pointing to a particular
|
||||
object. These may be stored in `$GIT_DIR/refs/`.
|
||||
A 40-byte hex representation of a SHA1 or a name that denotes
|
||||
a particular object. These may be stored in `$GIT_DIR/refs/`.
|
||||
|
||||
head ref::
|
||||
A ref pointing to a head. Often, this is abbreviated to "head".
|
||||
Head refs are stored in `$GIT_DIR/refs/heads/`.
|
||||
refspec::
|
||||
A refspec is used by fetch and push to describe the mapping
|
||||
between remote ref and local ref. They are combined with
|
||||
a colon in the format <src>:<dst>, preceded by an optional
|
||||
plus sign, +. For example:
|
||||
`git fetch $URL refs/heads/master:refs/heads/origin`
|
||||
means "grab the master branch head from the $URL and store
|
||||
it as my origin branch head".
|
||||
And `git push $URL refs/heads/master:refs/heads/to-upstream`
|
||||
means "publish my master branch head as to-upstream master head
|
||||
at $URL". See also gitlink:git-push[1]
|
||||
|
||||
repository::
|
||||
A collection of refs together with an object database containing
|
||||
all objects, which are reachable from the refs, possibly accompanied
|
||||
by meta data from one or more porcelains. A repository can
|
||||
share an object database with other repositories.
|
||||
|
||||
resolve::
|
||||
The action of fixing up manually what a failed automatic merge
|
||||
left behind.
|
||||
|
||||
revision::
|
||||
A particular state of files and directories which was stored in
|
||||
the object database. It is referenced by a commit object.
|
||||
|
||||
rewind::
|
||||
To throw away part of the development, i.e. to assign the head to
|
||||
an earlier revision.
|
||||
|
||||
SCM::
|
||||
Source code management (tool).
|
||||
|
||||
SHA1::
|
||||
Synonym for object name.
|
||||
|
||||
topic branch::
|
||||
A regular git branch that is used by a developer to
|
||||
identify a conceptual line of development. Since branches
|
||||
are very easy and inexpensive, it is often desirable to
|
||||
have several small branches that each contain very well
|
||||
defined concepts or small incremental yet related changes.
|
||||
|
||||
tracking branch::
|
||||
A regular git branch that is used to follow changes from
|
||||
another repository. A tracking branch should not contain
|
||||
direct modifications or have local commits made to it.
|
||||
A tracking branch can usually be identified as the
|
||||
right-hand-side ref in a Pull: refspec.
|
||||
|
||||
tree object::
|
||||
An object containing a list of file names and modes along with refs
|
||||
to the associated blob and/or tree objects. A tree is equivalent
|
||||
to a directory.
|
||||
|
||||
tree::
|
||||
Either a working tree, or a tree object together with the
|
||||
dependent blob and tree objects (i.e. a stored representation
|
||||
of a working tree).
|
||||
|
||||
tree-ish::
|
||||
A ref pointing to either a commit object, a tree object, or a
|
||||
tag object pointing to a tag or commit or tree object.
|
||||
|
||||
ent::
|
||||
Favorite synonym to "tree-ish" by some total geeks. See
|
||||
`http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
|
||||
explanation.
|
||||
|
||||
tag object::
|
||||
An object containing a ref pointing to another object, which can
|
||||
contain a message just like a commit object. It can also
|
||||
@ -153,101 +317,10 @@ tag::
|
||||
A tag is most typically used to mark a particular point in the
|
||||
commit ancestry chain.
|
||||
|
||||
merge::
|
||||
To merge branches means to try to accumulate the changes since a
|
||||
common ancestor and apply them to the first branch. An automatic
|
||||
merge uses heuristics to accomplish that. Evidently, an automatic
|
||||
merge can fail.
|
||||
unmerged index:
|
||||
An index which contains unmerged index entries.
|
||||
|
||||
octopus::
|
||||
To merge more than two branches. Also denotes an intelligent
|
||||
predator.
|
||||
|
||||
resolve::
|
||||
The action of fixing up manually what a failed automatic merge
|
||||
left behind.
|
||||
|
||||
rewind::
|
||||
To throw away part of the development, i.e. to assign the head to
|
||||
an earlier revision.
|
||||
|
||||
rebase::
|
||||
To clean a branch by starting from the head of the main line of
|
||||
development ("master"), and reapply the (possibly cherry-picked)
|
||||
changes from that branch.
|
||||
|
||||
repository::
|
||||
A collection of refs together with an object database containing
|
||||
all objects, which are reachable from the refs, possibly accompanied
|
||||
by meta data from one or more porcelains. A repository can
|
||||
share an object database with other repositories.
|
||||
|
||||
git archive::
|
||||
Synonym for repository (for arch people).
|
||||
|
||||
file system::
|
||||
Linus Torvalds originally designed git to be a user space file
|
||||
system, i.e. the infrastructure to hold files and directories.
|
||||
That ensured the efficiency and speed of git.
|
||||
|
||||
alternate object database::
|
||||
Via the alternates mechanism, a repository can inherit part of its
|
||||
object database from another object database, which is called
|
||||
"alternate".
|
||||
|
||||
reachable::
|
||||
An object is reachable from a ref/commit/tree/tag, if there is a
|
||||
chain leading from the latter to the former.
|
||||
|
||||
chain::
|
||||
A list of objects, where each object in the list contains a
|
||||
reference to its successor (for example, the successor of a commit
|
||||
could be one of its parents).
|
||||
|
||||
fetch::
|
||||
Fetching a branch means to get the branch's head ref from a
|
||||
remote repository, to find out which objects are missing from
|
||||
the local object database, and to get them, too.
|
||||
|
||||
pull::
|
||||
Pulling a branch means to fetch it and merge it.
|
||||
|
||||
push::
|
||||
Pushing a branch means to get the branch's head ref from a remote
|
||||
repository, find out if it is an ancestor to the branch's local
|
||||
head ref is a direct, and in that case, putting all objects, which
|
||||
are reachable from the local head ref, and which are missing from
|
||||
the remote repository, into the remote object database, and updating
|
||||
the remote head ref. If the remote head is not an ancestor to the
|
||||
local head, the push fails.
|
||||
|
||||
pack::
|
||||
A set of objects which have been compressed into one file (to save
|
||||
space or to transmit them efficiently).
|
||||
|
||||
pack index::
|
||||
The list of identifiers, and other information, of the objects in a
|
||||
pack, to assist in efficiently accessing the contents of a pack.
|
||||
|
||||
core git::
|
||||
Fundamental data structures and utilities of git. Exposes only
|
||||
limited source code management tools.
|
||||
|
||||
plumbing::
|
||||
Cute name for core git.
|
||||
|
||||
porcelain::
|
||||
Cute name for programs and program suites depending on core git,
|
||||
presenting a high level access to core git. Porcelains expose
|
||||
more of a SCM interface than the plumbing.
|
||||
|
||||
object type:
|
||||
One of the identifiers "commit","tree","tag" and "blob" describing
|
||||
the type of an object.
|
||||
|
||||
SCM::
|
||||
Source code management (tool).
|
||||
|
||||
dircache::
|
||||
You are *waaaaay* behind.
|
||||
working tree::
|
||||
The set of files and directories currently being worked on,
|
||||
i.e. you can work in your working tree without using git at all.
|
||||
|
||||
|
@ -4,12 +4,16 @@ T="$1"
|
||||
|
||||
for h in *.html *.txt howto/*.txt howto/*.html
|
||||
do
|
||||
diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h" || {
|
||||
if test -f "$T/$h" &&
|
||||
diff -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
|
||||
then
|
||||
:; # up to date
|
||||
else
|
||||
echo >&2 "# install $h $T/$h"
|
||||
rm -f "$T/$h"
|
||||
mkdir -p `dirname "$T/$h"`
|
||||
cp "$h" "$T/$h"
|
||||
}
|
||||
fi
|
||||
done
|
||||
strip_leading=`echo "$T/" | sed -e 's|.|.|g'`
|
||||
for th in "$T"/*.html "$T"/*.txt "$T"/howto/*.txt "$T"/howto/*.html
|
||||
|
@ -48,7 +48,7 @@ This list is sorted alphabetically:
|
||||
';
|
||||
|
||||
@keys=sort {uc($a) cmp uc($b)} keys %terms;
|
||||
$pattern='(\b'.join('\b|\b',reverse @keys).'\b)';
|
||||
$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!link:git-)',reverse @keys).'\b)';
|
||||
foreach $key (@keys) {
|
||||
$terms{$key}=~s/$pattern/sprintf "<<ref_".no_spaces($1).",$1>>";/eg;
|
||||
print '[[ref_'.no_spaces($key).']]'.$key."::\n"
|
||||
|
93
Makefile
93
Makefile
@ -28,8 +28,8 @@ all:
|
||||
#
|
||||
# Define NO_SETENV if you don't have setenv in the C library.
|
||||
#
|
||||
# Define USE_SYMLINK_HEAD if you want .git/HEAD to be a symbolic link.
|
||||
# Don't enable it on Windows.
|
||||
# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
|
||||
# Enable it on Windows. By default, symrefs are still used.
|
||||
#
|
||||
# Define PPC_SHA1 environment variable when running make to make use of
|
||||
# a bundled SHA1 routine optimized for PowerPC.
|
||||
@ -113,25 +113,26 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
|
||||
### --- END CONFIGURATION SECTION ---
|
||||
|
||||
SCRIPT_SH = \
|
||||
git-add.sh git-bisect.sh git-branch.sh git-checkout.sh \
|
||||
git-bisect.sh git-branch.sh git-checkout.sh \
|
||||
git-cherry.sh git-clean.sh git-clone.sh git-commit.sh \
|
||||
git-count-objects.sh git-diff.sh git-fetch.sh \
|
||||
git-fetch.sh \
|
||||
git-format-patch.sh git-ls-remote.sh \
|
||||
git-merge-one-file.sh git-parse-remote.sh \
|
||||
git-prune.sh git-pull.sh git-push.sh git-rebase.sh \
|
||||
git-prune.sh git-pull.sh git-rebase.sh \
|
||||
git-repack.sh git-request-pull.sh git-reset.sh \
|
||||
git-resolve.sh git-revert.sh git-rm.sh git-sh-setup.sh \
|
||||
git-tag.sh git-verify-tag.sh git-whatchanged.sh \
|
||||
git-resolve.sh git-revert.sh git-sh-setup.sh \
|
||||
git-tag.sh git-verify-tag.sh \
|
||||
git-applymbox.sh git-applypatch.sh git-am.sh \
|
||||
git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
|
||||
git-merge-resolve.sh git-merge-ours.sh git-grep.sh \
|
||||
git-lost-found.sh
|
||||
git-merge-resolve.sh git-merge-ours.sh \
|
||||
git-lost-found.sh git-quiltimport.sh
|
||||
|
||||
SCRIPT_PERL = \
|
||||
git-archimport.perl git-cvsimport.perl git-relink.perl \
|
||||
git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
|
||||
git-annotate.perl git-cvsserver.perl \
|
||||
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl
|
||||
git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
|
||||
git-send-email.perl
|
||||
|
||||
SCRIPT_PYTHON = \
|
||||
git-merge-recursive.py
|
||||
@ -139,7 +140,7 @@ SCRIPT_PYTHON = \
|
||||
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
|
||||
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
|
||||
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
|
||||
git-cherry-pick git-show git-status
|
||||
git-cherry-pick git-status
|
||||
|
||||
# The ones that do not have to link with lcrypto, lz nor xdiff.
|
||||
SIMPLE_PROGRAMS = \
|
||||
@ -153,21 +154,25 @@ PROGRAMS = \
|
||||
git-convert-objects$X git-diff-files$X \
|
||||
git-diff-index$X git-diff-stages$X \
|
||||
git-diff-tree$X git-fetch-pack$X git-fsck-objects$X \
|
||||
git-hash-object$X git-index-pack$X git-init-db$X git-local-fetch$X \
|
||||
git-hash-object$X git-index-pack$X git-local-fetch$X \
|
||||
git-ls-files$X git-ls-tree$X git-mailinfo$X git-merge-base$X \
|
||||
git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
|
||||
git-peek-remote$X git-prune-packed$X git-read-tree$X \
|
||||
git-receive-pack$X git-rev-list$X git-rev-parse$X \
|
||||
git-receive-pack$X git-rev-parse$X \
|
||||
git-send-pack$X git-show-branch$X git-shell$X \
|
||||
git-show-index$X git-ssh-fetch$X \
|
||||
git-ssh-upload$X git-tar-tree$X git-unpack-file$X \
|
||||
git-unpack-objects$X git-update-index$X git-update-server-info$X \
|
||||
git-upload-pack$X git-verify-pack$X git-write-tree$X \
|
||||
git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
|
||||
git-update-ref$X git-symbolic-ref$X \
|
||||
git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
|
||||
git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
|
||||
|
||||
BUILT_INS = git-log$X
|
||||
BUILT_INS = git-log$X git-whatchanged$X git-show$X \
|
||||
git-count-objects$X git-diff$X git-push$X \
|
||||
git-grep$X git-add$X git-rm$X git-rev-list$X \
|
||||
git-check-ref-format$X \
|
||||
git-init-db$X
|
||||
|
||||
# what 'all' will build and 'install' will install, in gitexecdir
|
||||
ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
|
||||
@ -196,7 +201,7 @@ LIB_H = \
|
||||
blob.h cache.h commit.h csum-file.h delta.h \
|
||||
diff.h object.h pack.h pkt-line.h quote.h refs.h \
|
||||
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
|
||||
tree-walk.h log-tree.h
|
||||
tree-walk.h log-tree.h dir.h
|
||||
|
||||
DIFF_OBJS = \
|
||||
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
|
||||
@ -204,17 +209,19 @@ DIFF_OBJS = \
|
||||
diffcore-delta.o log-tree.o
|
||||
|
||||
LIB_OBJS = \
|
||||
blob.o commit.o connect.o csum-file.o cache-tree.o \
|
||||
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
|
||||
date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
|
||||
object.o pack-check.o patch-delta.o path.o pkt-line.o \
|
||||
quote.o read-cache.o refs.o run-command.o \
|
||||
quote.o read-cache.o refs.o run-command.o dir.o \
|
||||
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
|
||||
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
|
||||
fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
|
||||
$(DIFF_OBJS)
|
||||
|
||||
BUILTIN_OBJS = \
|
||||
builtin-log.o builtin-help.o
|
||||
builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
|
||||
builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
|
||||
builtin-rm.o builtin-init-db.o
|
||||
|
||||
GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
|
||||
LIBS = $(GITLIBS) -lz
|
||||
@ -263,6 +270,7 @@ ifeq ($(uname_O),Cygwin)
|
||||
NO_D_TYPE_IN_DIRENT = YesPlease
|
||||
NO_D_INO_IN_DIRENT = YesPlease
|
||||
NO_STRCASESTR = YesPlease
|
||||
NO_SYMLINK_HEAD = YesPlease
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
# There are conflicting reports about this.
|
||||
# On some boxes NO_MMAP is needed, and not so elsewhere.
|
||||
@ -283,7 +291,9 @@ ifeq ($(uname_S),OpenBSD)
|
||||
ALL_LDFLAGS += -L/usr/local/lib
|
||||
endif
|
||||
ifeq ($(uname_S),NetBSD)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
|
||||
NEEDS_LIBICONV = YesPlease
|
||||
endif
|
||||
ALL_CFLAGS += -I/usr/pkg/include
|
||||
ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib
|
||||
endif
|
||||
@ -317,10 +327,6 @@ else
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef WITH_SEND_EMAIL
|
||||
SCRIPT_PERL += git-send-email.perl
|
||||
endif
|
||||
|
||||
ifndef NO_CURL
|
||||
ifdef CURLDIR
|
||||
# This is still problematic -- gcc does not always want -R.
|
||||
@ -386,6 +392,9 @@ endif
|
||||
ifdef NO_D_INO_IN_DIRENT
|
||||
ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
|
||||
endif
|
||||
ifdef NO_SYMLINK_HEAD
|
||||
ALL_CFLAGS += -DNO_SYMLINK_HEAD
|
||||
endif
|
||||
ifdef NO_STRCASESTR
|
||||
COMPAT_CFLAGS += -DNO_STRCASESTR
|
||||
COMPAT_OBJS += compat/strcasestr.o
|
||||
@ -453,6 +462,7 @@ PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
|
||||
GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
|
||||
|
||||
ALL_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' $(COMPAT_CFLAGS)
|
||||
ALL_CFLAGS += -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
|
||||
LIB_OBJS += $(COMPAT_OBJS)
|
||||
export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
|
||||
### Build rules
|
||||
@ -505,9 +515,6 @@ $(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py
|
||||
git-cherry-pick: git-revert
|
||||
cp $< $@
|
||||
|
||||
git-show: git-whatchanged
|
||||
cp $< $@
|
||||
|
||||
git-status: git-commit
|
||||
cp $< $@
|
||||
|
||||
@ -562,14 +569,6 @@ git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
|
||||
|
||||
git-rev-list$X: rev-list.o $(LIB_FILE)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||
$(LIBS) $(OPENSSL_LIBSSL)
|
||||
|
||||
init-db.o: init-db.c
|
||||
$(CC) -c $(ALL_CFLAGS) \
|
||||
-DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c
|
||||
|
||||
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
|
||||
$(patsubst git-%$X,%.o,$(PROGRAMS)): $(GITLIBS)
|
||||
$(DIFF_OBJS): diffcore.h
|
||||
@ -609,7 +608,7 @@ test-date$X: test-date.c date.o ctype.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
|
||||
|
||||
test-delta$X: test-delta.c diff-delta.o patch-delta.o
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
|
||||
|
||||
test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
|
||||
$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
||||
@ -656,6 +655,25 @@ dist: git.spec git-tar-tree
|
||||
rpm: dist
|
||||
$(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
|
||||
|
||||
htmldocs = git-htmldocs-$(GIT_VERSION)
|
||||
manpages = git-manpages-$(GIT_VERSION)
|
||||
dist-doc:
|
||||
rm -fr .doc-tmp-dir
|
||||
mkdir .doc-tmp-dir
|
||||
$(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc
|
||||
cd .doc-tmp-dir && $(TAR) cf ../$(htmldocs).tar .
|
||||
gzip -n -9 -f $(htmldocs).tar
|
||||
:
|
||||
rm -fr .doc-tmp-dir
|
||||
mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
|
||||
$(MAKE) -C Documentation DESTDIR=. \
|
||||
man1=../.doc-tmp-dir/man1 \
|
||||
man7=../.doc-tmp-dir/man7 \
|
||||
install
|
||||
cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
|
||||
gzip -n -9 -f $(manpages).tar
|
||||
rm -fr .doc-tmp-dir
|
||||
|
||||
### Cleaning rules
|
||||
|
||||
clean:
|
||||
@ -663,8 +681,9 @@ clean:
|
||||
$(LIB_FILE) $(XDIFF_LIB)
|
||||
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
|
||||
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
|
||||
rm -rf $(GIT_TARNAME)
|
||||
rm -rf $(GIT_TARNAME) .doc-tmp-dir
|
||||
rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
|
||||
rm -f $(htmldocs).tar $(manpages).tar
|
||||
$(MAKE) -C Documentation/ clean
|
||||
$(MAKE) -C templates clean
|
||||
$(MAKE) -C t/ clean
|
||||
|
408
apply.c
408
apply.c
@ -11,20 +11,25 @@
|
||||
#include "cache-tree.h"
|
||||
#include "quote.h"
|
||||
#include "blob.h"
|
||||
#include "delta.h"
|
||||
|
||||
// --check turns on checking that the working tree matches the
|
||||
// files that are being modified, but doesn't apply the patch
|
||||
// --stat does just a diffstat, and doesn't actually apply
|
||||
// --numstat does numeric diffstat, and doesn't actually apply
|
||||
// --index-info shows the old and new index info for paths if available.
|
||||
// --index updates the cache as well.
|
||||
// --cached updates only the cache without ever touching the working tree.
|
||||
//
|
||||
static const char *prefix;
|
||||
static int prefix_length = -1;
|
||||
static int newfd = -1;
|
||||
|
||||
static int p_value = 1;
|
||||
static int allow_binary_replacement = 0;
|
||||
static int check_index = 0;
|
||||
static int write_index = 0;
|
||||
static int cached = 0;
|
||||
static int diffstat = 0;
|
||||
static int numstat = 0;
|
||||
static int summary = 0;
|
||||
@ -35,7 +40,7 @@ static int show_index_info = 0;
|
||||
static int line_termination = '\n';
|
||||
static unsigned long p_context = -1;
|
||||
static const char apply_usage[] =
|
||||
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
|
||||
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--cached] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
|
||||
|
||||
static enum whitespace_eol {
|
||||
nowarn_whitespace,
|
||||
@ -114,6 +119,9 @@ struct patch {
|
||||
char *new_name, *old_name, *def_name;
|
||||
unsigned int old_mode, new_mode;
|
||||
int is_rename, is_copy, is_new, is_delete, is_binary;
|
||||
#define BINARY_DELTA_DEFLATED 1
|
||||
#define BINARY_LITERAL_DEFLATED 2
|
||||
unsigned long deflate_origlen;
|
||||
int lines_added, lines_deleted;
|
||||
int score;
|
||||
struct fragment *fragments;
|
||||
@ -967,6 +975,88 @@ static inline int metadata_changes(struct patch *patch)
|
||||
patch->old_mode != patch->new_mode);
|
||||
}
|
||||
|
||||
static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
|
||||
{
|
||||
/* We have read "GIT binary patch\n"; what follows is a line
|
||||
* that says the patch method (currently, either "deflated
|
||||
* literal" or "deflated delta") and the length of data before
|
||||
* deflating; a sequence of 'length-byte' followed by base-85
|
||||
* encoded data follows.
|
||||
*
|
||||
* Each 5-byte sequence of base-85 encodes up to 4 bytes,
|
||||
* and we would limit the patch line to 66 characters,
|
||||
* so one line can fit up to 13 groups that would decode
|
||||
* to 52 bytes max. The length byte 'A'-'Z' corresponds
|
||||
* to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
|
||||
* The end of binary is signalled with an empty line.
|
||||
*/
|
||||
int llen, used;
|
||||
struct fragment *fragment;
|
||||
char *data = NULL;
|
||||
|
||||
patch->fragments = fragment = xcalloc(1, sizeof(*fragment));
|
||||
|
||||
/* Grab the type of patch */
|
||||
llen = linelen(buffer, size);
|
||||
used = llen;
|
||||
linenr++;
|
||||
|
||||
if (!strncmp(buffer, "delta ", 6)) {
|
||||
patch->is_binary = BINARY_DELTA_DEFLATED;
|
||||
patch->deflate_origlen = strtoul(buffer + 6, NULL, 10);
|
||||
}
|
||||
else if (!strncmp(buffer, "literal ", 8)) {
|
||||
patch->is_binary = BINARY_LITERAL_DEFLATED;
|
||||
patch->deflate_origlen = strtoul(buffer + 8, NULL, 10);
|
||||
}
|
||||
else
|
||||
return error("unrecognized binary patch at line %d: %.*s",
|
||||
linenr-1, llen-1, buffer);
|
||||
buffer += llen;
|
||||
while (1) {
|
||||
int byte_length, max_byte_length, newsize;
|
||||
llen = linelen(buffer, size);
|
||||
used += llen;
|
||||
linenr++;
|
||||
if (llen == 1)
|
||||
break;
|
||||
/* Minimum line is "A00000\n" which is 7-byte long,
|
||||
* and the line length must be multiple of 5 plus 2.
|
||||
*/
|
||||
if ((llen < 7) || (llen-2) % 5)
|
||||
goto corrupt;
|
||||
max_byte_length = (llen - 2) / 5 * 4;
|
||||
byte_length = *buffer;
|
||||
if ('A' <= byte_length && byte_length <= 'Z')
|
||||
byte_length = byte_length - 'A' + 1;
|
||||
else if ('a' <= byte_length && byte_length <= 'z')
|
||||
byte_length = byte_length - 'a' + 27;
|
||||
else
|
||||
goto corrupt;
|
||||
/* if the input length was not multiple of 4, we would
|
||||
* have filler at the end but the filler should never
|
||||
* exceed 3 bytes
|
||||
*/
|
||||
if (max_byte_length < byte_length ||
|
||||
byte_length <= max_byte_length - 4)
|
||||
goto corrupt;
|
||||
newsize = fragment->size + byte_length;
|
||||
data = xrealloc(data, newsize);
|
||||
if (decode_85(data + fragment->size,
|
||||
buffer + 1,
|
||||
byte_length))
|
||||
goto corrupt;
|
||||
fragment->size = newsize;
|
||||
buffer += llen;
|
||||
size -= llen;
|
||||
}
|
||||
fragment->patch = data;
|
||||
return used;
|
||||
corrupt:
|
||||
return error("corrupt binary patch at line %d: %.*s",
|
||||
linenr-1, llen-1, buffer);
|
||||
}
|
||||
|
||||
static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
|
||||
{
|
||||
int hdrsize, patchsize;
|
||||
@ -983,19 +1073,34 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
|
||||
"Files ",
|
||||
NULL,
|
||||
};
|
||||
static const char git_binary[] = "GIT binary patch\n";
|
||||
int i;
|
||||
int hd = hdrsize + offset;
|
||||
unsigned long llen = linelen(buffer + hd, size - hd);
|
||||
|
||||
if (!memcmp(" differ\n", buffer + hd + llen - 8, 8))
|
||||
if (llen == sizeof(git_binary) - 1 &&
|
||||
!memcmp(git_binary, buffer + hd, llen)) {
|
||||
int used;
|
||||
linenr++;
|
||||
used = parse_binary(buffer + hd + llen,
|
||||
size - hd - llen, patch);
|
||||
if (used)
|
||||
patchsize = used + llen;
|
||||
else
|
||||
patchsize = 0;
|
||||
}
|
||||
else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) {
|
||||
for (i = 0; binhdr[i]; i++) {
|
||||
int len = strlen(binhdr[i]);
|
||||
if (len < size - hd &&
|
||||
!memcmp(binhdr[i], buffer + hd, len)) {
|
||||
linenr++;
|
||||
patch->is_binary = 1;
|
||||
patchsize = llen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Empty patch cannot be applied if:
|
||||
* - it is a binary patch and we do not do binary_replace, or
|
||||
@ -1346,75 +1451,149 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
|
||||
return offset;
|
||||
}
|
||||
|
||||
static char *inflate_it(const void *data, unsigned long size,
|
||||
unsigned long inflated_size)
|
||||
{
|
||||
z_stream stream;
|
||||
void *out;
|
||||
int st;
|
||||
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
|
||||
stream.next_in = (unsigned char *)data;
|
||||
stream.avail_in = size;
|
||||
stream.next_out = out = xmalloc(inflated_size);
|
||||
stream.avail_out = inflated_size;
|
||||
inflateInit(&stream);
|
||||
st = inflate(&stream, Z_FINISH);
|
||||
if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
|
||||
{
|
||||
unsigned long dst_size;
|
||||
struct fragment *fragment = patch->fragments;
|
||||
void *data;
|
||||
void *result;
|
||||
|
||||
data = inflate_it(fragment->patch, fragment->size,
|
||||
patch->deflate_origlen);
|
||||
if (!data)
|
||||
return error("corrupt patch data");
|
||||
switch (patch->is_binary) {
|
||||
case BINARY_DELTA_DEFLATED:
|
||||
result = patch_delta(desc->buffer, desc->size,
|
||||
data,
|
||||
patch->deflate_origlen,
|
||||
&dst_size);
|
||||
free(desc->buffer);
|
||||
desc->buffer = result;
|
||||
free(data);
|
||||
break;
|
||||
case BINARY_LITERAL_DEFLATED:
|
||||
free(desc->buffer);
|
||||
desc->buffer = data;
|
||||
dst_size = patch->deflate_origlen;
|
||||
break;
|
||||
}
|
||||
if (!desc->buffer)
|
||||
return -1;
|
||||
desc->size = desc->alloc = dst_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_binary(struct buffer_desc *desc, struct patch *patch)
|
||||
{
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
unsigned char sha1[20];
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
|
||||
if (!allow_binary_replacement)
|
||||
return error("cannot apply binary patch to '%s' "
|
||||
"without --allow-binary-replacement",
|
||||
name);
|
||||
|
||||
/* For safety, we require patch index line to contain
|
||||
* full 40-byte textual SHA1 for old and new, at least for now.
|
||||
*/
|
||||
if (strlen(patch->old_sha1_prefix) != 40 ||
|
||||
strlen(patch->new_sha1_prefix) != 40 ||
|
||||
get_sha1_hex(patch->old_sha1_prefix, sha1) ||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1))
|
||||
return error("cannot apply binary patch to '%s' "
|
||||
"without full index line", name);
|
||||
|
||||
if (patch->old_name) {
|
||||
/* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
write_sha1_file_prepare(desc->buffer, desc->size,
|
||||
blob_type, sha1, hdr, &hdrlen);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||
return error("the patch applies to '%s' (%s), "
|
||||
"which does not match the "
|
||||
"current contents.",
|
||||
name, sha1_to_hex(sha1));
|
||||
}
|
||||
else {
|
||||
/* Otherwise, the old one must be empty. */
|
||||
if (desc->size)
|
||||
return error("the patch applies to an empty "
|
||||
"'%s' but it is not empty", name);
|
||||
}
|
||||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||
if (!memcmp(sha1, null_sha1, 20)) {
|
||||
free(desc->buffer);
|
||||
desc->alloc = desc->size = 0;
|
||||
desc->buffer = NULL;
|
||||
return 0; /* deletion patch */
|
||||
}
|
||||
|
||||
if (has_sha1_file(sha1)) {
|
||||
/* We already have the postimage */
|
||||
char type[10];
|
||||
unsigned long size;
|
||||
|
||||
free(desc->buffer);
|
||||
desc->buffer = read_sha1_file(sha1, type, &size);
|
||||
if (!desc->buffer)
|
||||
return error("the necessary postimage %s for "
|
||||
"'%s' cannot be read",
|
||||
patch->new_sha1_prefix, name);
|
||||
desc->alloc = desc->size = size;
|
||||
}
|
||||
else {
|
||||
/* We have verified desc matches the preimage;
|
||||
* apply the patch data to it, which is stored
|
||||
* in the patch->fragments->{patch,size}.
|
||||
*/
|
||||
if (apply_binary_fragment(desc, patch))
|
||||
return error("binary patch does not apply to '%s'",
|
||||
name);
|
||||
|
||||
/* verify that the result matches */
|
||||
write_sha1_file_prepare(desc->buffer, desc->size, blob_type,
|
||||
sha1, hdr, &hdrlen);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix))
|
||||
return error("binary patch to '%s' creates incorrect result", name);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||
{
|
||||
struct fragment *frag = patch->fragments;
|
||||
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
|
||||
if (patch->is_binary) {
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (!allow_binary_replacement)
|
||||
return error("cannot apply binary patch to '%s' "
|
||||
"without --allow-binary-replacement",
|
||||
name);
|
||||
|
||||
/* For safety, we require patch index line to contain
|
||||
* full 40-byte textual SHA1 for old and new, at least for now.
|
||||
*/
|
||||
if (strlen(patch->old_sha1_prefix) != 40 ||
|
||||
strlen(patch->new_sha1_prefix) != 40 ||
|
||||
get_sha1_hex(patch->old_sha1_prefix, sha1) ||
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1))
|
||||
return error("cannot apply binary patch to '%s' "
|
||||
"without full index line", name);
|
||||
|
||||
if (patch->old_name) {
|
||||
unsigned char hdr[50];
|
||||
int hdrlen;
|
||||
|
||||
/* See if the old one matches what the patch
|
||||
* applies to.
|
||||
*/
|
||||
write_sha1_file_prepare(desc->buffer, desc->size,
|
||||
blob_type, sha1, hdr, &hdrlen);
|
||||
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||
return error("the patch applies to '%s' (%s), "
|
||||
"which does not match the "
|
||||
"current contents.",
|
||||
name, sha1_to_hex(sha1));
|
||||
}
|
||||
else {
|
||||
/* Otherwise, the old one must be empty. */
|
||||
if (desc->size)
|
||||
return error("the patch applies to an empty "
|
||||
"'%s' but it is not empty", name);
|
||||
}
|
||||
|
||||
/* For now, we do not record post-image data in the patch,
|
||||
* and require the object already present in the recipient's
|
||||
* object database.
|
||||
*/
|
||||
if (desc->buffer) {
|
||||
free(desc->buffer);
|
||||
desc->alloc = desc->size = 0;
|
||||
}
|
||||
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||
|
||||
if (memcmp(sha1, null_sha1, 20)) {
|
||||
char type[10];
|
||||
unsigned long size;
|
||||
|
||||
desc->buffer = read_sha1_file(sha1, type, &size);
|
||||
if (!desc->buffer)
|
||||
return error("the necessary postimage %s for "
|
||||
"'%s' does not exist",
|
||||
patch->new_sha1_prefix, name);
|
||||
desc->alloc = desc->size = size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
if (patch->is_binary)
|
||||
return apply_binary(desc, patch);
|
||||
|
||||
while (frag) {
|
||||
if (apply_one_fragment(desc, frag) < 0)
|
||||
@ -1425,7 +1604,7 @@ static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apply_data(struct patch *patch, struct stat *st)
|
||||
static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
|
||||
{
|
||||
char *buf;
|
||||
unsigned long size, alloc;
|
||||
@ -1434,7 +1613,17 @@ static int apply_data(struct patch *patch, struct stat *st)
|
||||
size = 0;
|
||||
alloc = 0;
|
||||
buf = NULL;
|
||||
if (patch->old_name) {
|
||||
if (cached) {
|
||||
if (ce) {
|
||||
char type[20];
|
||||
buf = read_sha1_file(ce->sha1, type, &size);
|
||||
if (!buf)
|
||||
return error("read of %s failed",
|
||||
patch->old_name);
|
||||
alloc = size;
|
||||
}
|
||||
}
|
||||
else if (patch->old_name) {
|
||||
size = st->st_size;
|
||||
alloc = size + 8192;
|
||||
buf = xmalloc(alloc);
|
||||
@ -1462,16 +1651,21 @@ static int check_patch(struct patch *patch)
|
||||
const char *old_name = patch->old_name;
|
||||
const char *new_name = patch->new_name;
|
||||
const char *name = old_name ? old_name : new_name;
|
||||
struct cache_entry *ce = NULL;
|
||||
|
||||
if (old_name) {
|
||||
int changed;
|
||||
int stat_ret = lstat(old_name, &st);
|
||||
int changed = 0;
|
||||
int stat_ret = 0;
|
||||
unsigned st_mode = 0;
|
||||
|
||||
if (!cached)
|
||||
stat_ret = lstat(old_name, &st);
|
||||
if (check_index) {
|
||||
int pos = cache_name_pos(old_name, strlen(old_name));
|
||||
if (pos < 0)
|
||||
return error("%s: does not exist in index",
|
||||
old_name);
|
||||
ce = active_cache[pos];
|
||||
if (stat_ret < 0) {
|
||||
struct checkout costate;
|
||||
if (errno != ENOENT)
|
||||
@ -1484,40 +1678,46 @@ static int check_patch(struct patch *patch)
|
||||
costate.quiet = 0;
|
||||
costate.not_new = 0;
|
||||
costate.refresh_cache = 1;
|
||||
if (checkout_entry(active_cache[pos],
|
||||
if (checkout_entry(ce,
|
||||
&costate,
|
||||
NULL) ||
|
||||
lstat(old_name, &st))
|
||||
return -1;
|
||||
}
|
||||
|
||||
changed = ce_match_stat(active_cache[pos], &st, 1);
|
||||
if (!cached)
|
||||
changed = ce_match_stat(ce, &st, 1);
|
||||
if (changed)
|
||||
return error("%s: does not match index",
|
||||
old_name);
|
||||
if (cached)
|
||||
st_mode = ntohl(ce->ce_mode);
|
||||
}
|
||||
else if (stat_ret < 0)
|
||||
return error("%s: %s", old_name, strerror(errno));
|
||||
|
||||
if (!cached)
|
||||
st_mode = ntohl(create_ce_mode(st.st_mode));
|
||||
|
||||
if (patch->is_new < 0)
|
||||
patch->is_new = 0;
|
||||
st.st_mode = ntohl(create_ce_mode(st.st_mode));
|
||||
if (!patch->old_mode)
|
||||
patch->old_mode = st.st_mode;
|
||||
if ((st.st_mode ^ patch->old_mode) & S_IFMT)
|
||||
patch->old_mode = st_mode;
|
||||
if ((st_mode ^ patch->old_mode) & S_IFMT)
|
||||
return error("%s: wrong type", old_name);
|
||||
if (st.st_mode != patch->old_mode)
|
||||
if (st_mode != patch->old_mode)
|
||||
fprintf(stderr, "warning: %s has type %o, expected %o\n",
|
||||
old_name, st.st_mode, patch->old_mode);
|
||||
old_name, st_mode, patch->old_mode);
|
||||
}
|
||||
|
||||
if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
|
||||
if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
|
||||
return error("%s: already exists in index", new_name);
|
||||
if (!lstat(new_name, &st))
|
||||
return error("%s: already exists in working directory", new_name);
|
||||
if (errno != ENOENT)
|
||||
return error("%s: %s", new_name, strerror(errno));
|
||||
if (!cached) {
|
||||
if (!lstat(new_name, &st))
|
||||
return error("%s: already exists in working directory", new_name);
|
||||
if (errno != ENOENT)
|
||||
return error("%s: %s", new_name, strerror(errno));
|
||||
}
|
||||
if (!patch->new_mode) {
|
||||
if (patch->is_new)
|
||||
patch->new_mode = S_IFREG | 0644;
|
||||
@ -1534,9 +1734,9 @@ static int check_patch(struct patch *patch)
|
||||
return error("new mode (%o) of %s does not match old mode (%o)%s%s",
|
||||
patch->new_mode, new_name, patch->old_mode,
|
||||
same ? "" : " of ", same ? "" : old_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (apply_data(patch, &st) < 0)
|
||||
if (apply_data(patch, &st, ce) < 0)
|
||||
return error("%s: patch does not apply", name);
|
||||
return 0;
|
||||
}
|
||||
@ -1603,7 +1803,7 @@ static void numstat_patch_list(struct patch *patch)
|
||||
{
|
||||
for ( ; patch; patch = patch->next) {
|
||||
const char *name;
|
||||
name = patch->old_name ? patch->old_name : patch->new_name;
|
||||
name = patch->new_name ? patch->new_name : patch->old_name;
|
||||
printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
|
||||
if (line_termination && quote_c_style(name, NULL, NULL, 0))
|
||||
quote_c_style(name, NULL, stdout, 0);
|
||||
@ -1720,7 +1920,8 @@ static void remove_file(struct patch *patch)
|
||||
die("unable to remove %s from index", patch->old_name);
|
||||
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
|
||||
}
|
||||
unlink(patch->old_name);
|
||||
if (!cached)
|
||||
unlink(patch->old_name);
|
||||
}
|
||||
|
||||
static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
|
||||
@ -1737,9 +1938,11 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
|
||||
memcpy(ce->name, path, namelen);
|
||||
ce->ce_mode = create_ce_mode(mode);
|
||||
ce->ce_flags = htons(namelen);
|
||||
if (lstat(path, &st) < 0)
|
||||
die("unable to stat newly created file %s", path);
|
||||
fill_stat_cache_info(ce, &st);
|
||||
if (!cached) {
|
||||
if (lstat(path, &st) < 0)
|
||||
die("unable to stat newly created file %s", path);
|
||||
fill_stat_cache_info(ce, &st);
|
||||
}
|
||||
if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
|
||||
die("unable to create backing store for newly created file %s", path);
|
||||
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
|
||||
@ -1776,6 +1979,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
|
||||
*/
|
||||
static void create_one_file(char *path, unsigned mode, const char *buf, unsigned long size)
|
||||
{
|
||||
if (cached)
|
||||
return;
|
||||
if (!try_create_file(path, mode, buf, size))
|
||||
return;
|
||||
|
||||
@ -1876,7 +2081,6 @@ static int use_patch(struct patch *p)
|
||||
|
||||
static int apply_patch(int fd, const char *filename)
|
||||
{
|
||||
int newfd;
|
||||
unsigned long offset, size;
|
||||
char *buffer = read_patch_file(fd, &size);
|
||||
struct patch *list = NULL, **listp = &list;
|
||||
@ -1907,12 +2111,11 @@ static int apply_patch(int fd, const char *filename)
|
||||
size -= nr;
|
||||
}
|
||||
|
||||
newfd = -1;
|
||||
if (whitespace_error && (new_whitespace == error_on_whitespace))
|
||||
apply = 0;
|
||||
|
||||
write_index = check_index && apply;
|
||||
if (write_index)
|
||||
if (write_index && newfd < 0)
|
||||
newfd = hold_index_file_for_update(&cache_file, get_index_file());
|
||||
if (check_index) {
|
||||
if (read_cache() < 0)
|
||||
@ -1925,12 +2128,6 @@ static int apply_patch(int fd, const char *filename)
|
||||
if (apply)
|
||||
write_out_results(list, skipped_patch);
|
||||
|
||||
if (write_index) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_index_file(&cache_file))
|
||||
die("Unable to write new cachefile");
|
||||
}
|
||||
|
||||
if (show_index_info)
|
||||
show_index_list(list);
|
||||
|
||||
@ -1993,7 +2190,8 @@ int main(int argc, char **argv)
|
||||
diffstat = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--allow-binary-replacement")) {
|
||||
if (!strcmp(arg, "--allow-binary-replacement") ||
|
||||
!strcmp(arg, "--binary")) {
|
||||
allow_binary_replacement = 1;
|
||||
continue;
|
||||
}
|
||||
@ -2016,6 +2214,11 @@ int main(int argc, char **argv)
|
||||
check_index = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--cached")) {
|
||||
check_index = 1;
|
||||
cached = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--apply")) {
|
||||
apply = 1;
|
||||
continue;
|
||||
@ -2088,5 +2291,12 @@ int main(int argc, char **argv)
|
||||
whitespace_error == 1 ? "" : "s",
|
||||
whitespace_error == 1 ? "s" : "");
|
||||
}
|
||||
|
||||
if (write_index) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_index_file(&cache_file))
|
||||
die("Unable to write new cachefile");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
140
base85.c
Normal file
140
base85.c
Normal file
@ -0,0 +1,140 @@
|
||||
#include "cache.h"
|
||||
|
||||
#undef DEBUG_85
|
||||
|
||||
#ifdef DEBUG_85
|
||||
#define say(a) fprintf(stderr, a)
|
||||
#define say1(a,b) fprintf(stderr, a, b)
|
||||
#define say2(a,b,c) fprintf(stderr, a, b, c)
|
||||
#else
|
||||
#define say(a) do {} while(0)
|
||||
#define say1(a,b) do {} while(0)
|
||||
#define say2(a,b,c) do {} while(0)
|
||||
#endif
|
||||
|
||||
static const char en85[] = {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
||||
'U', 'V', 'W', 'X', 'Y', 'Z',
|
||||
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
||||
'u', 'v', 'w', 'x', 'y', 'z',
|
||||
'!', '#', '$', '%', '&', '(', ')', '*', '+', '-',
|
||||
';', '<', '=', '>', '?', '@', '^', '_', '`', '{',
|
||||
'|', '}', '~'
|
||||
};
|
||||
|
||||
static char de85[256];
|
||||
static void prep_base85(void)
|
||||
{
|
||||
int i;
|
||||
if (de85['Z'])
|
||||
return;
|
||||
for (i = 0; i < ARRAY_SIZE(en85); i++) {
|
||||
int ch = en85[i];
|
||||
de85[ch] = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int decode_85(char *dst, char *buffer, int len)
|
||||
{
|
||||
prep_base85();
|
||||
|
||||
say2("decode 85 <%.*s>", len/4*5, buffer);
|
||||
while (len) {
|
||||
unsigned acc = 0;
|
||||
int de, cnt = 4;
|
||||
unsigned char ch;
|
||||
do {
|
||||
ch = *buffer++;
|
||||
de = de85[ch];
|
||||
if (--de < 0)
|
||||
return error("invalid base85 alphabet %c", ch);
|
||||
acc = acc * 85 + de;
|
||||
} while (--cnt);
|
||||
ch = *buffer++;
|
||||
de = de85[ch];
|
||||
if (--de < 0)
|
||||
return error("invalid base85 alphabet %c", ch);
|
||||
/*
|
||||
* Detect overflow. The largest
|
||||
* 5-letter possible is "|NsC0" to
|
||||
* encode 0xffffffff, and "|NsC" gives
|
||||
* 0x03030303 at this point (i.e.
|
||||
* 0xffffffff = 0x03030303 * 85).
|
||||
*/
|
||||
if (0x03030303 < acc ||
|
||||
0xffffffff - de < (acc *= 85))
|
||||
error("invalid base85 sequence %.5s", buffer-5);
|
||||
acc += de;
|
||||
say1(" %08x", acc);
|
||||
|
||||
cnt = (len < 4) ? len : 4;
|
||||
len -= cnt;
|
||||
do {
|
||||
acc = (acc << 8) | (acc >> 24);
|
||||
*dst++ = acc;
|
||||
} while (--cnt);
|
||||
}
|
||||
say("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void encode_85(char *buf, unsigned char *data, int bytes)
|
||||
{
|
||||
prep_base85();
|
||||
|
||||
say("encode 85");
|
||||
while (bytes) {
|
||||
unsigned acc = 0;
|
||||
int cnt;
|
||||
for (cnt = 24; cnt >= 0; cnt -= 8) {
|
||||
int ch = *data++;
|
||||
acc |= ch << cnt;
|
||||
if (--bytes == 0)
|
||||
break;
|
||||
}
|
||||
say1(" %08x", acc);
|
||||
for (cnt = 4; cnt >= 0; cnt--) {
|
||||
int val = acc % 85;
|
||||
acc /= 85;
|
||||
buf[cnt] = en85[val];
|
||||
}
|
||||
buf += 5;
|
||||
}
|
||||
say("\n");
|
||||
|
||||
*buf = 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_85
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
if (!strcmp(av[1], "-e")) {
|
||||
int len = strlen(av[2]);
|
||||
encode_85(buf, av[2], len);
|
||||
if (len <= 26) len = len + 'A' - 1;
|
||||
else len = len + 'a' - 26 + 1;
|
||||
printf("encoded: %c%s\n", len, buf);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(av[1], "-d")) {
|
||||
int len = *av[2];
|
||||
if ('A' <= len && len <= 'Z') len = len - 'A' + 1;
|
||||
else len = len - 'a' + 26 + 1;
|
||||
decode_85(buf, av[2]+1, len);
|
||||
printf("decoded: %.*s\n", len, buf);
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(av[1], "-t")) {
|
||||
char t[4] = { -1,-1,-1,-1 };
|
||||
encode_85(buf, t, 4);
|
||||
printf("encoded: D%s\n", buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
10
blame.c
10
blame.c
@ -515,9 +515,9 @@ static int compare_tree_path(struct rev_info* revs,
|
||||
paths[1] = NULL;
|
||||
|
||||
diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
|
||||
&revs->diffopt);
|
||||
&revs->pruning);
|
||||
ret = rev_compare_tree(revs, c1->tree, c2->tree);
|
||||
diff_tree_release_paths(&revs->diffopt);
|
||||
diff_tree_release_paths(&revs->pruning);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -531,9 +531,9 @@ static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,
|
||||
paths[1] = NULL;
|
||||
|
||||
diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
|
||||
&revs->diffopt);
|
||||
&revs->pruning);
|
||||
ret = rev_same_tree_as_empty(revs, t1);
|
||||
diff_tree_release_paths(&revs->diffopt);
|
||||
diff_tree_release_paths(&revs->pruning);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -834,7 +834,7 @@ int main(int argc, const char **argv)
|
||||
|
||||
args[0] = filename;
|
||||
args[1] = NULL;
|
||||
diff_tree_setup_paths(args, &rev.diffopt);
|
||||
diff_tree_setup_paths(args, &rev.pruning);
|
||||
prepare_revision_walk(&rev);
|
||||
process_commits(&rev, filename, &initial);
|
||||
|
||||
|
189
builtin-add.c
Normal file
189
builtin-add.c
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* "git add" builtin command
|
||||
*
|
||||
* Copyright (C) 2006 Linus Torvalds
|
||||
*/
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "dir.h"
|
||||
#include "cache-tree.h"
|
||||
|
||||
static const char builtin_add_usage[] =
|
||||
"git-add [-n] [-v] <filepattern>...";
|
||||
|
||||
static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
|
||||
{
|
||||
char *seen;
|
||||
int i, specs;
|
||||
struct dir_entry **src, **dst;
|
||||
|
||||
for (specs = 0; pathspec[specs]; specs++)
|
||||
/* nothing */;
|
||||
seen = xmalloc(specs);
|
||||
memset(seen, 0, specs);
|
||||
|
||||
src = dst = dir->entries;
|
||||
i = dir->nr;
|
||||
while (--i >= 0) {
|
||||
struct dir_entry *entry = *src++;
|
||||
if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
|
||||
free(entry);
|
||||
continue;
|
||||
}
|
||||
*dst++ = entry;
|
||||
}
|
||||
dir->nr = dst - dir->entries;
|
||||
|
||||
for (i = 0; i < specs; i++) {
|
||||
struct stat st;
|
||||
const char *match;
|
||||
if (seen[i])
|
||||
continue;
|
||||
|
||||
/* Existing file? We must have ignored it */
|
||||
match = pathspec[i];
|
||||
if (!match[0] || !lstat(match, &st))
|
||||
continue;
|
||||
die("pathspec '%s' did not match any files", match);
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_directory(struct dir_struct *dir, const char **pathspec)
|
||||
{
|
||||
const char *path, *base;
|
||||
int baselen;
|
||||
|
||||
/* Set up the default git porcelain excludes */
|
||||
memset(dir, 0, sizeof(*dir));
|
||||
dir->exclude_per_dir = ".gitignore";
|
||||
path = git_path("info/exclude");
|
||||
if (!access(path, R_OK))
|
||||
add_excludes_from_file(dir, path);
|
||||
|
||||
/*
|
||||
* Calculate common prefix for the pathspec, and
|
||||
* use that to optimize the directory walk
|
||||
*/
|
||||
baselen = common_prefix(pathspec);
|
||||
path = ".";
|
||||
base = "";
|
||||
if (baselen) {
|
||||
char *common = xmalloc(baselen + 1);
|
||||
common = xmalloc(baselen + 1);
|
||||
memcpy(common, *pathspec, baselen);
|
||||
common[baselen] = 0;
|
||||
path = base = common;
|
||||
}
|
||||
|
||||
/* Read the directory and prune it */
|
||||
read_directory(dir, path, base, baselen);
|
||||
if (pathspec)
|
||||
prune_directory(dir, pathspec, baselen);
|
||||
}
|
||||
|
||||
static int add_file_to_index(const char *path, int verbose)
|
||||
{
|
||||
int size, namelen;
|
||||
struct stat st;
|
||||
struct cache_entry *ce;
|
||||
|
||||
if (lstat(path, &st))
|
||||
die("%s: unable to stat (%s)", path, strerror(errno));
|
||||
|
||||
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
|
||||
die("%s: can only add regular files or symbolic links", path);
|
||||
|
||||
namelen = strlen(path);
|
||||
size = cache_entry_size(namelen);
|
||||
ce = xcalloc(1, size);
|
||||
memcpy(ce->name, path, namelen);
|
||||
ce->ce_flags = htons(namelen);
|
||||
fill_stat_cache_info(ce, &st);
|
||||
|
||||
ce->ce_mode = create_ce_mode(st.st_mode);
|
||||
if (!trust_executable_bit) {
|
||||
/* If there is an existing entry, pick the mode bits
|
||||
* from it.
|
||||
*/
|
||||
int pos = cache_name_pos(path, namelen);
|
||||
if (pos >= 0)
|
||||
ce->ce_mode = active_cache[pos]->ce_mode;
|
||||
}
|
||||
|
||||
if (index_path(ce->sha1, path, &st, 1))
|
||||
die("unable to index file %s", path);
|
||||
if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
|
||||
die("unable to add %s to index",path);
|
||||
if (verbose)
|
||||
printf("add '%s'\n", path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct cache_file cache_file;
|
||||
|
||||
int cmd_add(int argc, const char **argv, char **envp)
|
||||
{
|
||||
int i, newfd;
|
||||
int verbose = 0, show_only = 0;
|
||||
const char *prefix = setup_git_directory();
|
||||
const char **pathspec;
|
||||
struct dir_struct dir;
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
newfd = hold_index_file_for_update(&cache_file, get_index_file());
|
||||
if (newfd < 0)
|
||||
die("unable to create new cachefile");
|
||||
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (arg[0] != '-')
|
||||
break;
|
||||
if (!strcmp(arg, "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
show_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-v")) {
|
||||
verbose = 1;
|
||||
continue;
|
||||
}
|
||||
die(builtin_add_usage);
|
||||
}
|
||||
git_config(git_default_config);
|
||||
pathspec = get_pathspec(prefix, argv + i);
|
||||
|
||||
fill_directory(&dir, pathspec);
|
||||
|
||||
if (show_only) {
|
||||
const char *sep = "", *eof = "";
|
||||
for (i = 0; i < dir.nr; i++) {
|
||||
printf("%s%s", sep, dir.entries[i]->name);
|
||||
sep = " ";
|
||||
eof = "\n";
|
||||
}
|
||||
fputs(eof, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < dir.nr; i++)
|
||||
add_file_to_index(dir.entries[i]->name, verbose);
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_index_file(&cache_file))
|
||||
die("Unable to write new index file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
14
builtin-check-ref-format.c
Normal file
14
builtin-check-ref-format.c
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* GIT - The information manager from hell
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "builtin.h"
|
||||
|
||||
int cmd_check_ref_format(int argc, const char **argv, char **envp)
|
||||
{
|
||||
if (argc != 2)
|
||||
usage("git check-ref-format refname");
|
||||
return !!check_ref_format(argv[1]);
|
||||
}
|
125
builtin-count.c
Normal file
125
builtin-count.c
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Builtin "git count-objects".
|
||||
*
|
||||
* Copyright (c) 2006 Junio C Hamano
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
|
||||
static const char count_objects_usage[] = "git-count-objects [-v]";
|
||||
|
||||
static void count_objects(DIR *d, char *path, int len, int verbose,
|
||||
unsigned long *loose,
|
||||
unsigned long *loose_size,
|
||||
unsigned long *packed_loose,
|
||||
unsigned long *garbage)
|
||||
{
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(d)) != NULL) {
|
||||
char hex[41];
|
||||
unsigned char sha1[20];
|
||||
const char *cp;
|
||||
int bad = 0;
|
||||
|
||||
if ((ent->d_name[0] == '.') &&
|
||||
(ent->d_name[1] == 0 ||
|
||||
((ent->d_name[1] == '.') && (ent->d_name[2] == 0))))
|
||||
continue;
|
||||
for (cp = ent->d_name; *cp; cp++) {
|
||||
int ch = *cp;
|
||||
if (('0' <= ch && ch <= '9') ||
|
||||
('a' <= ch && ch <= 'f'))
|
||||
continue;
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
if (cp - ent->d_name != 38)
|
||||
bad = 1;
|
||||
else {
|
||||
struct stat st;
|
||||
memcpy(path + len + 3, ent->d_name, 38);
|
||||
path[len + 2] = '/';
|
||||
path[len + 41] = 0;
|
||||
if (lstat(path, &st) || !S_ISREG(st.st_mode))
|
||||
bad = 1;
|
||||
else
|
||||
(*loose_size) += st.st_blocks;
|
||||
}
|
||||
if (bad) {
|
||||
if (verbose) {
|
||||
error("garbage found: %.*s/%s",
|
||||
len + 2, path, ent->d_name);
|
||||
(*garbage)++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
(*loose)++;
|
||||
if (!verbose)
|
||||
continue;
|
||||
memcpy(hex, path+len, 2);
|
||||
memcpy(hex+2, ent->d_name, 38);
|
||||
hex[40] = 0;
|
||||
if (get_sha1_hex(hex, sha1))
|
||||
die("internal error");
|
||||
if (has_sha1_pack(sha1))
|
||||
(*packed_loose)++;
|
||||
}
|
||||
}
|
||||
|
||||
int cmd_count_objects(int ac, const char **av, char **ep)
|
||||
{
|
||||
int i;
|
||||
int verbose = 0;
|
||||
const char *objdir = get_object_directory();
|
||||
int len = strlen(objdir);
|
||||
char *path = xmalloc(len + 50);
|
||||
unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
|
||||
unsigned long loose_size = 0;
|
||||
|
||||
for (i = 1; i < ac; i++) {
|
||||
const char *arg = av[i];
|
||||
if (*arg != '-')
|
||||
break;
|
||||
else if (!strcmp(arg, "-v"))
|
||||
verbose = 1;
|
||||
else
|
||||
usage(count_objects_usage);
|
||||
}
|
||||
|
||||
/* we do not take arguments other than flags for now */
|
||||
if (i < ac)
|
||||
usage(count_objects_usage);
|
||||
memcpy(path, objdir, len);
|
||||
if (len && objdir[len-1] != '/')
|
||||
path[len++] = '/';
|
||||
for (i = 0; i < 256; i++) {
|
||||
DIR *d;
|
||||
sprintf(path + len, "%02x", i);
|
||||
d = opendir(path);
|
||||
if (!d)
|
||||
continue;
|
||||
count_objects(d, path, len, verbose,
|
||||
&loose, &loose_size, &packed_loose, &garbage);
|
||||
closedir(d);
|
||||
}
|
||||
if (verbose) {
|
||||
struct packed_git *p;
|
||||
if (!packed_git)
|
||||
prepare_packed_git();
|
||||
for (p = packed_git; p; p = p->next) {
|
||||
if (!p->pack_local)
|
||||
continue;
|
||||
packed += num_packed_objects(p);
|
||||
}
|
||||
printf("count: %lu\n", loose);
|
||||
printf("size: %lu\n", loose_size / 2);
|
||||
printf("in-pack: %lu\n", packed);
|
||||
printf("prune-packable: %lu\n", packed_loose);
|
||||
printf("garbage: %lu\n", garbage);
|
||||
}
|
||||
else
|
||||
printf("%lu objects, %lu kilobytes\n",
|
||||
loose, loose_size / 2);
|
||||
return 0;
|
||||
}
|
370
builtin-diff.c
Normal file
370
builtin-diff.c
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* Builtin "git diff"
|
||||
*
|
||||
* Copyright (c) 2006 Junio C Hamano
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "commit.h"
|
||||
#include "blob.h"
|
||||
#include "tag.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "revision.h"
|
||||
#include "log-tree.h"
|
||||
#include "builtin.h"
|
||||
|
||||
/* NEEDSWORK: struct object has place for name but we _do_
|
||||
* know mode when we extracted the blob out of a tree, which
|
||||
* we currently lose.
|
||||
*/
|
||||
struct blobinfo {
|
||||
unsigned char sha1[20];
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static const char builtin_diff_usage[] =
|
||||
"diff <options> <rev>{0,2} -- <path>*";
|
||||
|
||||
static int builtin_diff_files(struct rev_info *revs,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
int silent = 0;
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp(arg, "--base"))
|
||||
revs->max_count = 1;
|
||||
else if (!strcmp(arg, "--ours"))
|
||||
revs->max_count = 2;
|
||||
else if (!strcmp(arg, "--theirs"))
|
||||
revs->max_count = 3;
|
||||
else if (!strcmp(arg, "-q"))
|
||||
silent = 1;
|
||||
else if (!strcmp(arg, "--raw"))
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
else
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
/*
|
||||
* Make sure there are NO revision (i.e. pending object) parameter,
|
||||
* specified rev.max_count is reasonable (0 <= n <= 3), and
|
||||
* there is no other revision filtering parameter.
|
||||
*/
|
||||
if (revs->pending_objects ||
|
||||
revs->min_age != -1 ||
|
||||
revs->max_age != -1 ||
|
||||
3 < revs->max_count)
|
||||
usage(builtin_diff_usage);
|
||||
if (revs->max_count < 0 &&
|
||||
(revs->diffopt.output_format == DIFF_FORMAT_PATCH))
|
||||
revs->combine_merges = revs->dense_combined_merges = 1;
|
||||
/*
|
||||
* Backward compatibility wart - "diff-files -s" used to
|
||||
* defeat the common diff option "-s" which asked for
|
||||
* DIFF_FORMAT_NO_OUTPUT.
|
||||
*/
|
||||
if (revs->diffopt.output_format == DIFF_FORMAT_NO_OUTPUT)
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
return run_diff_files(revs, silent);
|
||||
}
|
||||
|
||||
static void stuff_change(struct diff_options *opt,
|
||||
unsigned old_mode, unsigned new_mode,
|
||||
const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
const char *old_name,
|
||||
const char *new_name)
|
||||
{
|
||||
struct diff_filespec *one, *two;
|
||||
|
||||
if (memcmp(null_sha1, old_sha1, 20) &&
|
||||
memcmp(null_sha1, new_sha1, 20) &&
|
||||
!memcmp(old_sha1, new_sha1, 20))
|
||||
return;
|
||||
|
||||
if (opt->reverse_diff) {
|
||||
unsigned tmp;
|
||||
const unsigned char *tmp_u;
|
||||
const char *tmp_c;
|
||||
tmp = old_mode; old_mode = new_mode; new_mode = tmp;
|
||||
tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
|
||||
tmp_c = old_name; old_name = new_name; new_name = tmp_c;
|
||||
}
|
||||
one = alloc_filespec(old_name);
|
||||
two = alloc_filespec(new_name);
|
||||
fill_filespec(one, old_sha1, old_mode);
|
||||
fill_filespec(two, new_sha1, new_mode);
|
||||
|
||||
/* NEEDSWORK: shouldn't this part of diffopt??? */
|
||||
diff_queue(&diff_queued_diff, one, two);
|
||||
}
|
||||
|
||||
static int builtin_diff_b_f(struct rev_info *revs,
|
||||
int argc, const char **argv,
|
||||
struct blobinfo *blob,
|
||||
const char *path)
|
||||
{
|
||||
/* Blob vs file in the working tree*/
|
||||
struct stat st;
|
||||
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp(arg, "--raw"))
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
else
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
if (lstat(path, &st))
|
||||
die("'%s': %s", path, strerror(errno));
|
||||
if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
|
||||
die("'%s': not a regular file or symlink", path);
|
||||
stuff_change(&revs->diffopt,
|
||||
canon_mode(st.st_mode), canon_mode(st.st_mode),
|
||||
blob[0].sha1, null_sha1,
|
||||
path, path);
|
||||
diffcore_std(&revs->diffopt);
|
||||
diff_flush(&revs->diffopt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_diff_blobs(struct rev_info *revs,
|
||||
int argc, const char **argv,
|
||||
struct blobinfo *blob)
|
||||
{
|
||||
/* Blobs: the arguments are reversed when setup_revisions()
|
||||
* picked them up.
|
||||
*/
|
||||
unsigned mode = canon_mode(S_IFREG | 0644);
|
||||
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp(arg, "--raw"))
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
else
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
stuff_change(&revs->diffopt,
|
||||
mode, mode,
|
||||
blob[1].sha1, blob[0].sha1,
|
||||
blob[0].name, blob[0].name);
|
||||
diffcore_std(&revs->diffopt);
|
||||
diff_flush(&revs->diffopt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_diff_index(struct rev_info *revs,
|
||||
int argc, const char **argv)
|
||||
{
|
||||
int cached = 0;
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp(arg, "--cached"))
|
||||
cached = 1;
|
||||
else if (!strcmp(arg, "--raw"))
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
else
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
/*
|
||||
* Make sure there is one revision (i.e. pending object),
|
||||
* and there is no revision filtering parameters.
|
||||
*/
|
||||
if (!revs->pending_objects || revs->pending_objects->next ||
|
||||
revs->max_count != -1 || revs->min_age != -1 ||
|
||||
revs->max_age != -1)
|
||||
usage(builtin_diff_usage);
|
||||
return run_diff_index(revs, cached);
|
||||
}
|
||||
|
||||
static int builtin_diff_tree(struct rev_info *revs,
|
||||
int argc, const char **argv,
|
||||
struct object_list *ent)
|
||||
{
|
||||
const unsigned char *(sha1[2]);
|
||||
int swap = 1;
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp(arg, "--raw"))
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
else
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
|
||||
/* We saw two trees, ent[0] and ent[1].
|
||||
* unless ent[0] is unintesting, they are swapped
|
||||
*/
|
||||
if (ent[0].item->flags & UNINTERESTING)
|
||||
swap = 0;
|
||||
sha1[swap] = ent[0].item->sha1;
|
||||
sha1[1-swap] = ent[1].item->sha1;
|
||||
diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
|
||||
log_tree_diff_flush(revs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_diff_combined(struct rev_info *revs,
|
||||
int argc, const char **argv,
|
||||
struct object_list *ent,
|
||||
int ents)
|
||||
{
|
||||
const unsigned char (*parent)[20];
|
||||
int i;
|
||||
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
if (!strcmp(arg, "--raw"))
|
||||
revs->diffopt.output_format = DIFF_FORMAT_RAW;
|
||||
else
|
||||
usage(builtin_diff_usage);
|
||||
argv++; argc--;
|
||||
}
|
||||
if (!revs->dense_combined_merges && !revs->combine_merges)
|
||||
revs->dense_combined_merges = revs->combine_merges = 1;
|
||||
parent = xmalloc(ents * sizeof(*parent));
|
||||
/* Again, the revs are all reverse */
|
||||
for (i = 0; i < ents; i++)
|
||||
memcpy(parent + i, ent[ents - 1 - i].item->sha1, 20);
|
||||
diff_tree_combined(parent[0], parent + 1, ents - 1,
|
||||
revs->dense_combined_merges, revs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_head(struct rev_info *revs)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
struct object *obj;
|
||||
if (get_sha1("HEAD", sha1))
|
||||
return;
|
||||
obj = parse_object(sha1);
|
||||
if (!obj)
|
||||
return;
|
||||
add_object(obj, &revs->pending_objects, NULL, "HEAD");
|
||||
}
|
||||
|
||||
int cmd_diff(int argc, const char **argv, char **envp)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct object_list *list, ent[100];
|
||||
int ents = 0, blobs = 0, paths = 0;
|
||||
const char *path = NULL;
|
||||
struct blobinfo blob[2];
|
||||
|
||||
/*
|
||||
* We could get N tree-ish in the rev.pending_objects list.
|
||||
* Also there could be M blobs there, and P pathspecs.
|
||||
*
|
||||
* N=0, M=0:
|
||||
* cache vs files (diff-files)
|
||||
* N=0, M=2:
|
||||
* compare two random blobs. P must be zero.
|
||||
* N=0, M=1, P=1:
|
||||
* compare a blob with a working tree file.
|
||||
*
|
||||
* N=1, M=0:
|
||||
* tree vs cache (diff-index --cached)
|
||||
*
|
||||
* N=2, M=0:
|
||||
* tree vs tree (diff-tree)
|
||||
*
|
||||
* Other cases are errors.
|
||||
*/
|
||||
|
||||
git_config(git_diff_config);
|
||||
init_revisions(&rev);
|
||||
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
|
||||
|
||||
argc = setup_revisions(argc, argv, &rev, NULL);
|
||||
/* Do we have --cached and not have a pending object, then
|
||||
* default to HEAD by hand. Eek.
|
||||
*/
|
||||
if (!rev.pending_objects) {
|
||||
int i;
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
if (!strcmp(arg, "--"))
|
||||
break;
|
||||
else if (!strcmp(arg, "--cached")) {
|
||||
add_head(&rev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (list = rev.pending_objects; list; list = list->next) {
|
||||
struct object *obj = list->item;
|
||||
const char *name = list->name;
|
||||
int flags = (obj->flags & UNINTERESTING);
|
||||
if (!obj->parsed)
|
||||
obj = parse_object(obj->sha1);
|
||||
obj = deref_tag(obj, NULL, 0);
|
||||
if (!obj)
|
||||
die("invalid object '%s' given.", name);
|
||||
if (!strcmp(obj->type, commit_type))
|
||||
obj = &((struct commit *)obj)->tree->object;
|
||||
if (!strcmp(obj->type, tree_type)) {
|
||||
if (ARRAY_SIZE(ent) <= ents)
|
||||
die("more than %d trees given: '%s'",
|
||||
(int) ARRAY_SIZE(ent), name);
|
||||
obj->flags |= flags;
|
||||
ent[ents].item = obj;
|
||||
ent[ents].name = name;
|
||||
ents++;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(obj->type, blob_type)) {
|
||||
if (2 <= blobs)
|
||||
die("more than two blobs given: '%s'", name);
|
||||
memcpy(blob[blobs].sha1, obj->sha1, 20);
|
||||
blob[blobs].name = name;
|
||||
blobs++;
|
||||
continue;
|
||||
|
||||
}
|
||||
die("unhandled object '%s' given.", name);
|
||||
}
|
||||
if (rev.prune_data) {
|
||||
const char **pathspec = rev.prune_data;
|
||||
while (*pathspec) {
|
||||
if (!path)
|
||||
path = *pathspec;
|
||||
paths++;
|
||||
pathspec++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, do the arguments look reasonable?
|
||||
*/
|
||||
if (!ents) {
|
||||
switch (blobs) {
|
||||
case 0:
|
||||
return builtin_diff_files(&rev, argc, argv);
|
||||
break;
|
||||
case 1:
|
||||
if (paths != 1)
|
||||
usage(builtin_diff_usage);
|
||||
return builtin_diff_b_f(&rev, argc, argv, blob, path);
|
||||
break;
|
||||
case 2:
|
||||
if (paths)
|
||||
usage(builtin_diff_usage);
|
||||
return builtin_diff_blobs(&rev, argc, argv, blob);
|
||||
break;
|
||||
default:
|
||||
usage(builtin_diff_usage);
|
||||
}
|
||||
}
|
||||
else if (blobs)
|
||||
usage(builtin_diff_usage);
|
||||
else if (ents == 1)
|
||||
return builtin_diff_index(&rev, argc, argv);
|
||||
else if (ents == 2)
|
||||
return builtin_diff_tree(&rev, argc, argv, ent);
|
||||
else
|
||||
return builtin_diff_combined(&rev, argc, argv, ent, ents);
|
||||
usage(builtin_diff_usage);
|
||||
}
|
902
builtin-grep.c
Normal file
902
builtin-grep.c
Normal file
@ -0,0 +1,902 @@
|
||||
/*
|
||||
* Builtin "git grep"
|
||||
*
|
||||
* Copyright (c) 2006 Junio C Hamano
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "blob.h"
|
||||
#include "tree.h"
|
||||
#include "commit.h"
|
||||
#include "tag.h"
|
||||
#include "tree-walk.h"
|
||||
#include "builtin.h"
|
||||
#include <regex.h>
|
||||
#include <fnmatch.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
/*
|
||||
* git grep pathspecs are somewhat different from diff-tree pathspecs;
|
||||
* pathname wildcards are allowed.
|
||||
*/
|
||||
static int pathspec_matches(const char **paths, const char *name)
|
||||
{
|
||||
int namelen, i;
|
||||
if (!paths || !*paths)
|
||||
return 1;
|
||||
namelen = strlen(name);
|
||||
for (i = 0; paths[i]; i++) {
|
||||
const char *match = paths[i];
|
||||
int matchlen = strlen(match);
|
||||
const char *cp, *meta;
|
||||
|
||||
if ((matchlen <= namelen) &&
|
||||
!strncmp(name, match, matchlen) &&
|
||||
(match[matchlen-1] == '/' ||
|
||||
name[matchlen] == '\0' || name[matchlen] == '/'))
|
||||
return 1;
|
||||
if (!fnmatch(match, name, 0))
|
||||
return 1;
|
||||
if (name[namelen-1] != '/')
|
||||
continue;
|
||||
|
||||
/* We are being asked if the directory ("name") is worth
|
||||
* descending into.
|
||||
*
|
||||
* Find the longest leading directory name that does
|
||||
* not have metacharacter in the pathspec; the name
|
||||
* we are looking at must overlap with that directory.
|
||||
*/
|
||||
for (cp = match, meta = NULL; cp - match < matchlen; cp++) {
|
||||
char ch = *cp;
|
||||
if (ch == '*' || ch == '[' || ch == '?') {
|
||||
meta = cp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!meta)
|
||||
meta = cp; /* fully literal */
|
||||
|
||||
if (namelen <= meta - match) {
|
||||
/* Looking at "Documentation/" and
|
||||
* the pattern says "Documentation/howto/", or
|
||||
* "Documentation/diff*.txt". The name we
|
||||
* have should match prefix.
|
||||
*/
|
||||
if (!memcmp(match, name, namelen))
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (meta - match < namelen) {
|
||||
/* Looking at "Documentation/howto/" and
|
||||
* the pattern says "Documentation/h*";
|
||||
* match up to "Do.../h"; this avoids descending
|
||||
* into "Documentation/technical/".
|
||||
*/
|
||||
if (!memcmp(match, name, meta - match))
|
||||
return 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct grep_pat {
|
||||
struct grep_pat *next;
|
||||
const char *origin;
|
||||
int no;
|
||||
const char *pattern;
|
||||
regex_t regexp;
|
||||
};
|
||||
|
||||
struct grep_opt {
|
||||
struct grep_pat *pattern_list;
|
||||
struct grep_pat **pattern_tail;
|
||||
regex_t regexp;
|
||||
unsigned linenum:1;
|
||||
unsigned invert:1;
|
||||
unsigned name_only:1;
|
||||
unsigned unmatch_name_only:1;
|
||||
unsigned count:1;
|
||||
unsigned word_regexp:1;
|
||||
unsigned fixed:1;
|
||||
#define GREP_BINARY_DEFAULT 0
|
||||
#define GREP_BINARY_NOMATCH 1
|
||||
#define GREP_BINARY_TEXT 2
|
||||
unsigned binary:2;
|
||||
int regflags;
|
||||
unsigned pre_context;
|
||||
unsigned post_context;
|
||||
};
|
||||
|
||||
static void add_pattern(struct grep_opt *opt, const char *pat,
|
||||
const char *origin, int no)
|
||||
{
|
||||
struct grep_pat *p = xcalloc(1, sizeof(*p));
|
||||
p->pattern = pat;
|
||||
p->origin = origin;
|
||||
p->no = no;
|
||||
*opt->pattern_tail = p;
|
||||
opt->pattern_tail = &p->next;
|
||||
p->next = NULL;
|
||||
}
|
||||
|
||||
static void compile_patterns(struct grep_opt *opt)
|
||||
{
|
||||
struct grep_pat *p;
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
int err = regcomp(&p->regexp, p->pattern, opt->regflags);
|
||||
if (err) {
|
||||
char errbuf[1024];
|
||||
char where[1024];
|
||||
if (p->no)
|
||||
sprintf(where, "In '%s' at %d, ",
|
||||
p->origin, p->no);
|
||||
else if (p->origin)
|
||||
sprintf(where, "%s, ", p->origin);
|
||||
else
|
||||
where[0] = 0;
|
||||
regerror(err, &p->regexp, errbuf, 1024);
|
||||
regfree(&p->regexp);
|
||||
die("%s'%s': %s", where, p->pattern, errbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *end_of_line(char *cp, unsigned long *left)
|
||||
{
|
||||
unsigned long l = *left;
|
||||
while (l && *cp != '\n') {
|
||||
l--;
|
||||
cp++;
|
||||
}
|
||||
*left = l;
|
||||
return cp;
|
||||
}
|
||||
|
||||
static int word_char(char ch)
|
||||
{
|
||||
return isalnum(ch) || ch == '_';
|
||||
}
|
||||
|
||||
static void show_line(struct grep_opt *opt, const char *bol, const char *eol,
|
||||
const char *name, unsigned lno, char sign)
|
||||
{
|
||||
printf("%s%c", name, sign);
|
||||
if (opt->linenum)
|
||||
printf("%d%c", lno, sign);
|
||||
printf("%.*s\n", (int)(eol-bol), bol);
|
||||
}
|
||||
|
||||
/*
|
||||
* NEEDSWORK: share code with diff.c
|
||||
*/
|
||||
#define FIRST_FEW_BYTES 8000
|
||||
static int buffer_is_binary(const char *ptr, unsigned long size)
|
||||
{
|
||||
if (FIRST_FEW_BYTES < size)
|
||||
size = FIRST_FEW_BYTES;
|
||||
if (memchr(ptr, 0, size))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fixmatch(const char *pattern, char *line, regmatch_t *match)
|
||||
{
|
||||
char *hit = strstr(line, pattern);
|
||||
if (!hit) {
|
||||
match->rm_so = match->rm_eo = -1;
|
||||
return REG_NOMATCH;
|
||||
}
|
||||
else {
|
||||
match->rm_so = hit - line;
|
||||
match->rm_eo = match->rm_so + strlen(pattern);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int grep_buffer(struct grep_opt *opt, const char *name,
|
||||
char *buf, unsigned long size)
|
||||
{
|
||||
char *bol = buf;
|
||||
unsigned long left = size;
|
||||
unsigned lno = 1;
|
||||
struct pre_context_line {
|
||||
char *bol;
|
||||
char *eol;
|
||||
} *prev = NULL, *pcl;
|
||||
unsigned last_hit = 0;
|
||||
unsigned last_shown = 0;
|
||||
int binary_match_only = 0;
|
||||
const char *hunk_mark = "";
|
||||
unsigned count = 0;
|
||||
|
||||
if (buffer_is_binary(buf, size)) {
|
||||
switch (opt->binary) {
|
||||
case GREP_BINARY_DEFAULT:
|
||||
binary_match_only = 1;
|
||||
break;
|
||||
case GREP_BINARY_NOMATCH:
|
||||
return 0; /* Assume unmatch */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->pre_context)
|
||||
prev = xcalloc(opt->pre_context, sizeof(*prev));
|
||||
if (opt->pre_context || opt->post_context)
|
||||
hunk_mark = "--\n";
|
||||
|
||||
while (left) {
|
||||
regmatch_t pmatch[10];
|
||||
char *eol, ch;
|
||||
int hit = 0;
|
||||
struct grep_pat *p;
|
||||
|
||||
eol = end_of_line(bol, &left);
|
||||
ch = *eol;
|
||||
*eol = 0;
|
||||
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
if (!opt->fixed) {
|
||||
regex_t *exp = &p->regexp;
|
||||
hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
|
||||
pmatch, 0);
|
||||
}
|
||||
else {
|
||||
hit = !fixmatch(p->pattern, bol, pmatch);
|
||||
}
|
||||
|
||||
if (hit && opt->word_regexp) {
|
||||
/* Match beginning must be either
|
||||
* beginning of the line, or at word
|
||||
* boundary (i.e. the last char must
|
||||
* not be alnum or underscore).
|
||||
*/
|
||||
if ((pmatch[0].rm_so < 0) ||
|
||||
(eol - bol) <= pmatch[0].rm_so ||
|
||||
(pmatch[0].rm_eo < 0) ||
|
||||
(eol - bol) < pmatch[0].rm_eo)
|
||||
die("regexp returned nonsense");
|
||||
if (pmatch[0].rm_so != 0 &&
|
||||
word_char(bol[pmatch[0].rm_so-1]))
|
||||
hit = 0;
|
||||
if (pmatch[0].rm_eo != (eol-bol) &&
|
||||
word_char(bol[pmatch[0].rm_eo]))
|
||||
hit = 0;
|
||||
}
|
||||
if (hit)
|
||||
break;
|
||||
}
|
||||
/* "grep -v -e foo -e bla" should list lines
|
||||
* that do not have either, so inversion should
|
||||
* be done outside.
|
||||
*/
|
||||
if (opt->invert)
|
||||
hit = !hit;
|
||||
if (opt->unmatch_name_only) {
|
||||
if (hit)
|
||||
return 0;
|
||||
goto next_line;
|
||||
}
|
||||
if (hit) {
|
||||
count++;
|
||||
if (binary_match_only) {
|
||||
printf("Binary file %s matches\n", name);
|
||||
return 1;
|
||||
}
|
||||
if (opt->name_only) {
|
||||
printf("%s\n", name);
|
||||
return 1;
|
||||
}
|
||||
/* Hit at this line. If we haven't shown the
|
||||
* pre-context lines, we would need to show them.
|
||||
* When asked to do "count", this still show
|
||||
* the context which is nonsense, but the user
|
||||
* deserves to get that ;-).
|
||||
*/
|
||||
if (opt->pre_context) {
|
||||
unsigned from;
|
||||
if (opt->pre_context < lno)
|
||||
from = lno - opt->pre_context;
|
||||
else
|
||||
from = 1;
|
||||
if (from <= last_shown)
|
||||
from = last_shown + 1;
|
||||
if (last_shown && from != last_shown + 1)
|
||||
printf(hunk_mark);
|
||||
while (from < lno) {
|
||||
pcl = &prev[lno-from-1];
|
||||
show_line(opt, pcl->bol, pcl->eol,
|
||||
name, from, '-');
|
||||
from++;
|
||||
}
|
||||
last_shown = lno-1;
|
||||
}
|
||||
if (last_shown && lno != last_shown + 1)
|
||||
printf(hunk_mark);
|
||||
if (!opt->count)
|
||||
show_line(opt, bol, eol, name, lno, ':');
|
||||
last_shown = last_hit = lno;
|
||||
}
|
||||
else if (last_hit &&
|
||||
lno <= last_hit + opt->post_context) {
|
||||
/* If the last hit is within the post context,
|
||||
* we need to show this line.
|
||||
*/
|
||||
if (last_shown && lno != last_shown + 1)
|
||||
printf(hunk_mark);
|
||||
show_line(opt, bol, eol, name, lno, '-');
|
||||
last_shown = lno;
|
||||
}
|
||||
if (opt->pre_context) {
|
||||
memmove(prev+1, prev,
|
||||
(opt->pre_context-1) * sizeof(*prev));
|
||||
prev->bol = bol;
|
||||
prev->eol = eol;
|
||||
}
|
||||
|
||||
next_line:
|
||||
*eol = ch;
|
||||
bol = eol + 1;
|
||||
if (!left)
|
||||
break;
|
||||
left--;
|
||||
lno++;
|
||||
}
|
||||
|
||||
if (opt->unmatch_name_only) {
|
||||
/* We did not see any hit, so we want to show this */
|
||||
printf("%s\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* NEEDSWORK:
|
||||
* The real "grep -c foo *.c" gives many "bar.c:0" lines,
|
||||
* which feels mostly useless but sometimes useful. Maybe
|
||||
* make it another option? For now suppress them.
|
||||
*/
|
||||
if (opt->count && count)
|
||||
printf("%s:%u\n", name, count);
|
||||
return !!last_hit;
|
||||
}
|
||||
|
||||
static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1, const char *name)
|
||||
{
|
||||
unsigned long size;
|
||||
char *data;
|
||||
char type[20];
|
||||
int hit;
|
||||
data = read_sha1_file(sha1, type, &size);
|
||||
if (!data) {
|
||||
error("'%s': unable to read %s", name, sha1_to_hex(sha1));
|
||||
return 0;
|
||||
}
|
||||
hit = grep_buffer(opt, name, data, size);
|
||||
free(data);
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int grep_file(struct grep_opt *opt, const char *filename)
|
||||
{
|
||||
struct stat st;
|
||||
int i;
|
||||
char *data;
|
||||
if (lstat(filename, &st) < 0) {
|
||||
err_ret:
|
||||
if (errno != ENOENT)
|
||||
error("'%s': %s", filename, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
if (!st.st_size)
|
||||
return 0; /* empty file -- no grep hit */
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return 0;
|
||||
i = open(filename, O_RDONLY);
|
||||
if (i < 0)
|
||||
goto err_ret;
|
||||
data = xmalloc(st.st_size + 1);
|
||||
if (st.st_size != xread(i, data, st.st_size)) {
|
||||
error("'%s': short read %s", filename, strerror(errno));
|
||||
close(i);
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
close(i);
|
||||
i = grep_buffer(opt, filename, data, st.st_size);
|
||||
free(data);
|
||||
return i;
|
||||
}
|
||||
|
||||
static int exec_grep(int argc, const char **argv)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
|
||||
argv[argc] = NULL;
|
||||
pid = fork();
|
||||
if (pid < 0)
|
||||
return pid;
|
||||
if (!pid) {
|
||||
execvp("grep", (char **) argv);
|
||||
exit(255);
|
||||
}
|
||||
while (waitpid(pid, &status, 0) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
if (!WEXITSTATUS(status))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define MAXARGS 1000
|
||||
#define ARGBUF 4096
|
||||
#define push_arg(a) do { \
|
||||
if (nr < MAXARGS) argv[nr++] = (a); \
|
||||
else die("maximum number of args exceeded"); \
|
||||
} while (0)
|
||||
|
||||
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
|
||||
{
|
||||
int i, nr, argc, hit, len;
|
||||
const char *argv[MAXARGS+1];
|
||||
char randarg[ARGBUF];
|
||||
char *argptr = randarg;
|
||||
struct grep_pat *p;
|
||||
|
||||
len = nr = 0;
|
||||
push_arg("grep");
|
||||
if (opt->fixed)
|
||||
push_arg("-F");
|
||||
if (opt->linenum)
|
||||
push_arg("-n");
|
||||
if (opt->regflags & REG_EXTENDED)
|
||||
push_arg("-E");
|
||||
if (opt->word_regexp)
|
||||
push_arg("-w");
|
||||
if (opt->name_only)
|
||||
push_arg("-l");
|
||||
if (opt->unmatch_name_only)
|
||||
push_arg("-L");
|
||||
if (opt->count)
|
||||
push_arg("-c");
|
||||
if (opt->post_context || opt->pre_context) {
|
||||
if (opt->post_context != opt->pre_context) {
|
||||
if (opt->pre_context) {
|
||||
push_arg("-B");
|
||||
len += snprintf(argptr, sizeof(randarg)-len,
|
||||
"%u", opt->pre_context);
|
||||
if (sizeof(randarg) <= len)
|
||||
die("maximum length of args exceeded");
|
||||
push_arg(argptr);
|
||||
argptr += len;
|
||||
}
|
||||
if (opt->post_context) {
|
||||
push_arg("-A");
|
||||
len += snprintf(argptr, sizeof(randarg)-len,
|
||||
"%u", opt->post_context);
|
||||
if (sizeof(randarg) <= len)
|
||||
die("maximum length of args exceeded");
|
||||
push_arg(argptr);
|
||||
argptr += len;
|
||||
}
|
||||
}
|
||||
else {
|
||||
push_arg("-C");
|
||||
len += snprintf(argptr, sizeof(randarg)-len,
|
||||
"%u", opt->post_context);
|
||||
if (sizeof(randarg) <= len)
|
||||
die("maximum length of args exceeded");
|
||||
push_arg(argptr);
|
||||
argptr += len;
|
||||
}
|
||||
}
|
||||
for (p = opt->pattern_list; p; p = p->next) {
|
||||
push_arg("-e");
|
||||
push_arg(p->pattern);
|
||||
}
|
||||
|
||||
/*
|
||||
* To make sure we get the header printed out when we want it,
|
||||
* add /dev/null to the paths to grep. This is unnecessary
|
||||
* (and wrong) with "-l" or "-L", which always print out the
|
||||
* name anyway.
|
||||
*
|
||||
* GNU grep has "-H", but this is portable.
|
||||
*/
|
||||
if (!opt->name_only && !opt->unmatch_name_only)
|
||||
push_arg("/dev/null");
|
||||
|
||||
hit = 0;
|
||||
argc = nr;
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
const char *name;
|
||||
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
|
||||
continue;
|
||||
if (!pathspec_matches(paths, ce->name))
|
||||
continue;
|
||||
name = ce->name;
|
||||
if (name[0] == '-') {
|
||||
int len = ce_namelen(ce);
|
||||
name = xmalloc(len + 3);
|
||||
memcpy(name, "./", 2);
|
||||
memcpy(name + 2, ce->name, len + 1);
|
||||
}
|
||||
argv[argc++] = name;
|
||||
if (argc < MAXARGS)
|
||||
continue;
|
||||
hit += exec_grep(argc, argv);
|
||||
argc = nr;
|
||||
}
|
||||
if (argc > nr)
|
||||
hit += exec_grep(argc, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
|
||||
{
|
||||
int hit = 0;
|
||||
int nr;
|
||||
read_cache();
|
||||
|
||||
#ifdef __unix__
|
||||
/*
|
||||
* Use the external "grep" command for the case where
|
||||
* we grep through the checked-out files. It tends to
|
||||
* be a lot more optimized
|
||||
*/
|
||||
if (!cached) {
|
||||
hit = external_grep(opt, paths, cached);
|
||||
if (hit >= 0)
|
||||
return hit;
|
||||
}
|
||||
#endif
|
||||
|
||||
for (nr = 0; nr < active_nr; nr++) {
|
||||
struct cache_entry *ce = active_cache[nr];
|
||||
if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
|
||||
continue;
|
||||
if (!pathspec_matches(paths, ce->name))
|
||||
continue;
|
||||
if (cached)
|
||||
hit |= grep_sha1(opt, ce->sha1, ce->name);
|
||||
else
|
||||
hit |= grep_file(opt, ce->name);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int grep_tree(struct grep_opt *opt, const char **paths,
|
||||
struct tree_desc *tree,
|
||||
const char *tree_name, const char *base)
|
||||
{
|
||||
unsigned mode;
|
||||
int len;
|
||||
int hit = 0;
|
||||
const char *path;
|
||||
const unsigned char *sha1;
|
||||
char *down;
|
||||
char *path_buf = xmalloc(PATH_MAX + strlen(tree_name) + 100);
|
||||
|
||||
if (tree_name[0]) {
|
||||
int offset = sprintf(path_buf, "%s:", tree_name);
|
||||
down = path_buf + offset;
|
||||
strcat(down, base);
|
||||
}
|
||||
else {
|
||||
down = path_buf;
|
||||
strcpy(down, base);
|
||||
}
|
||||
len = strlen(path_buf);
|
||||
|
||||
while (tree->size) {
|
||||
int pathlen;
|
||||
sha1 = tree_entry_extract(tree, &path, &mode);
|
||||
pathlen = strlen(path);
|
||||
strcpy(path_buf + len, path);
|
||||
|
||||
if (S_ISDIR(mode))
|
||||
/* Match "abc/" against pathspec to
|
||||
* decide if we want to descend into "abc"
|
||||
* directory.
|
||||
*/
|
||||
strcpy(path_buf + len + pathlen, "/");
|
||||
|
||||
if (!pathspec_matches(paths, down))
|
||||
;
|
||||
else if (S_ISREG(mode))
|
||||
hit |= grep_sha1(opt, sha1, path_buf);
|
||||
else if (S_ISDIR(mode)) {
|
||||
char type[20];
|
||||
struct tree_desc sub;
|
||||
void *data;
|
||||
data = read_sha1_file(sha1, type, &sub.size);
|
||||
if (!data)
|
||||
die("unable to read tree (%s)",
|
||||
sha1_to_hex(sha1));
|
||||
sub.buf = data;
|
||||
hit |= grep_tree(opt, paths, &sub, tree_name, down);
|
||||
free(data);
|
||||
}
|
||||
update_tree_entry(tree);
|
||||
}
|
||||
return hit;
|
||||
}
|
||||
|
||||
static int grep_object(struct grep_opt *opt, const char **paths,
|
||||
struct object *obj, const char *name)
|
||||
{
|
||||
if (!strcmp(obj->type, blob_type))
|
||||
return grep_sha1(opt, obj->sha1, name);
|
||||
if (!strcmp(obj->type, commit_type) ||
|
||||
!strcmp(obj->type, tree_type)) {
|
||||
struct tree_desc tree;
|
||||
void *data;
|
||||
int hit;
|
||||
data = read_object_with_reference(obj->sha1, tree_type,
|
||||
&tree.size, NULL);
|
||||
if (!data)
|
||||
die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
|
||||
tree.buf = data;
|
||||
hit = grep_tree(opt, paths, &tree, name, "");
|
||||
free(data);
|
||||
return hit;
|
||||
}
|
||||
die("unable to grep from object of type %s", obj->type);
|
||||
}
|
||||
|
||||
static const char builtin_grep_usage[] =
|
||||
"git-grep <option>* <rev>* [-e] <pattern> [<path>...]";
|
||||
|
||||
int cmd_grep(int argc, const char **argv, char **envp)
|
||||
{
|
||||
int hit = 0;
|
||||
int cached = 0;
|
||||
int seen_dashdash = 0;
|
||||
struct grep_opt opt;
|
||||
struct object_list *list, **tail, *object_list = NULL;
|
||||
const char *prefix = setup_git_directory();
|
||||
const char **paths = NULL;
|
||||
int i;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.pattern_tail = &opt.pattern_list;
|
||||
opt.regflags = REG_NEWLINE;
|
||||
|
||||
/*
|
||||
* If there is no -- then the paths must exist in the working
|
||||
* tree. If there is no explicit pattern specified with -e or
|
||||
* -f, we take the first unrecognized non option to be the
|
||||
* pattern, but then what follows it must be zero or more
|
||||
* valid refs up to the -- (if exists), and then existing
|
||||
* paths. If there is an explicit pattern, then the first
|
||||
* unrecocnized non option is the beginning of the refs list
|
||||
* that continues up to the -- (if exists), and then paths.
|
||||
*/
|
||||
|
||||
tail = &object_list;
|
||||
while (1 < argc) {
|
||||
const char *arg = argv[1];
|
||||
argc--; argv++;
|
||||
if (!strcmp("--cached", arg)) {
|
||||
cached = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-a", arg) ||
|
||||
!strcmp("--text", arg)) {
|
||||
opt.binary = GREP_BINARY_TEXT;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-i", arg) ||
|
||||
!strcmp("--ignore-case", arg)) {
|
||||
opt.regflags |= REG_ICASE;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-I", arg)) {
|
||||
opt.binary = GREP_BINARY_NOMATCH;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-v", arg) ||
|
||||
!strcmp("--invert-match", arg)) {
|
||||
opt.invert = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-E", arg) ||
|
||||
!strcmp("--extended-regexp", arg)) {
|
||||
opt.regflags |= REG_EXTENDED;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-F", arg) ||
|
||||
!strcmp("--fixed-strings", arg)) {
|
||||
opt.fixed = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-G", arg) ||
|
||||
!strcmp("--basic-regexp", arg)) {
|
||||
opt.regflags &= ~REG_EXTENDED;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-n", arg)) {
|
||||
opt.linenum = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-H", arg)) {
|
||||
/* We always show the pathname, so this
|
||||
* is a noop.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-l", arg) ||
|
||||
!strcmp("--files-with-matches", arg)) {
|
||||
opt.name_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-L", arg) ||
|
||||
!strcmp("--files-without-match", arg)) {
|
||||
opt.unmatch_name_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-c", arg) ||
|
||||
!strcmp("--count", arg)) {
|
||||
opt.count = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-w", arg) ||
|
||||
!strcmp("--word-regexp", arg)) {
|
||||
opt.word_regexp = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp("-A", arg, 2) ||
|
||||
!strncmp("-B", arg, 2) ||
|
||||
!strncmp("-C", arg, 2) ||
|
||||
(arg[0] == '-' && '1' <= arg[1] && arg[1] <= '9')) {
|
||||
unsigned num;
|
||||
const char *scan;
|
||||
switch (arg[1]) {
|
||||
case 'A': case 'B': case 'C':
|
||||
if (!arg[2]) {
|
||||
if (argc <= 1)
|
||||
usage(builtin_grep_usage);
|
||||
scan = *++argv;
|
||||
argc--;
|
||||
}
|
||||
else
|
||||
scan = arg + 2;
|
||||
break;
|
||||
default:
|
||||
scan = arg + 1;
|
||||
break;
|
||||
}
|
||||
if (sscanf(scan, "%u", &num) != 1)
|
||||
usage(builtin_grep_usage);
|
||||
switch (arg[1]) {
|
||||
case 'A':
|
||||
opt.post_context = num;
|
||||
break;
|
||||
default:
|
||||
case 'C':
|
||||
opt.post_context = num;
|
||||
case 'B':
|
||||
opt.pre_context = num;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-f", arg)) {
|
||||
FILE *patterns;
|
||||
int lno = 0;
|
||||
char buf[1024];
|
||||
if (argc <= 1)
|
||||
usage(builtin_grep_usage);
|
||||
patterns = fopen(argv[1], "r");
|
||||
if (!patterns)
|
||||
die("'%s': %s", argv[1], strerror(errno));
|
||||
while (fgets(buf, sizeof(buf), patterns)) {
|
||||
int len = strlen(buf);
|
||||
if (buf[len-1] == '\n')
|
||||
buf[len-1] = 0;
|
||||
/* ignore empty line like grep does */
|
||||
if (!buf[0])
|
||||
continue;
|
||||
add_pattern(&opt, strdup(buf), argv[1], ++lno);
|
||||
}
|
||||
fclose(patterns);
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp("-e", arg)) {
|
||||
if (1 < argc) {
|
||||
add_pattern(&opt, argv[1], "-e option", 0);
|
||||
argv++;
|
||||
argc--;
|
||||
continue;
|
||||
}
|
||||
usage(builtin_grep_usage);
|
||||
}
|
||||
if (!strcmp("--", arg))
|
||||
break;
|
||||
if (*arg == '-')
|
||||
usage(builtin_grep_usage);
|
||||
|
||||
/* First unrecognized non-option token */
|
||||
if (!opt.pattern_list) {
|
||||
add_pattern(&opt, arg, "command line", 0);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* We are looking at the first path or rev;
|
||||
* it is found at argv[1] after leaving the
|
||||
* loop.
|
||||
*/
|
||||
argc++; argv--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt.pattern_list)
|
||||
die("no pattern given.");
|
||||
if ((opt.regflags != REG_NEWLINE) && opt.fixed)
|
||||
die("cannot mix --fixed-strings and regexp");
|
||||
if (!opt.fixed)
|
||||
compile_patterns(&opt);
|
||||
|
||||
/* Check revs and then paths */
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
unsigned char sha1[20];
|
||||
/* Is it a rev? */
|
||||
if (!get_sha1(arg, sha1)) {
|
||||
struct object *object = parse_object(sha1);
|
||||
struct object_list *elem;
|
||||
if (!object)
|
||||
die("bad object %s", arg);
|
||||
elem = object_list_insert(object, tail);
|
||||
elem->name = arg;
|
||||
tail = &elem->next;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--")) {
|
||||
i++;
|
||||
seen_dashdash = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* The rest are paths */
|
||||
if (!seen_dashdash) {
|
||||
int j;
|
||||
for (j = i; j < argc; j++)
|
||||
verify_filename(prefix, argv[j]);
|
||||
}
|
||||
|
||||
if (i < argc)
|
||||
paths = get_pathspec(prefix, argv + i);
|
||||
else if (prefix) {
|
||||
paths = xcalloc(2, sizeof(const char *));
|
||||
paths[0] = prefix;
|
||||
paths[1] = NULL;
|
||||
}
|
||||
|
||||
if (!object_list)
|
||||
return !grep_cache(&opt, paths, cached);
|
||||
|
||||
if (cached)
|
||||
die("both --cached and trees are given.");
|
||||
|
||||
for (list = object_list; list; list = list->next) {
|
||||
struct object *real_obj;
|
||||
real_obj = deref_tag(list->item, NULL, 0);
|
||||
if (grep_object(&opt, paths, real_obj, list->name))
|
||||
hit = 1;
|
||||
}
|
||||
return !hit;
|
||||
}
|
@ -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"
|
||||
|
@ -4,6 +4,7 @@
|
||||
* Copyright (C) Linus Torvalds, 2005
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
|
||||
#ifndef DEFAULT_GIT_TEMPLATE_DIR
|
||||
#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates/"
|
||||
@ -116,7 +117,7 @@ static void copy_templates_1(char *path, int baselen,
|
||||
}
|
||||
}
|
||||
|
||||
static void copy_templates(const char *git_dir, int len, char *template_dir)
|
||||
static void copy_templates(const char *git_dir, int len, const char *template_dir)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
char template_path[PATH_MAX];
|
||||
@ -163,7 +164,7 @@ static void copy_templates(const char *git_dir, int len, char *template_dir)
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
static void create_default_files(const char *git_dir, char *template_path)
|
||||
static void create_default_files(const char *git_dir, const char *template_path)
|
||||
{
|
||||
unsigned len = strlen(git_dir);
|
||||
static char path[PATH_MAX];
|
||||
@ -234,15 +235,16 @@ static const char init_db_usage[] =
|
||||
* On the other hand, it might just make lookup slower and messier. You
|
||||
* be the judge. The default case is to have one DB per managed directory.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
int cmd_init_db(int argc, const char **argv, char **envp)
|
||||
{
|
||||
const char *git_dir;
|
||||
const char *sha1_dir;
|
||||
char *path, *template_dir = NULL;
|
||||
const char *template_dir = NULL;
|
||||
char *path;
|
||||
int len, i;
|
||||
|
||||
for (i = 1; i < argc; i++, argv++) {
|
||||
char *arg = argv[1];
|
||||
const char *arg = argv[1];
|
||||
if (!strncmp(arg, "--template=", 11))
|
||||
template_dir = arg+11;
|
||||
else if (!strcmp(arg, "--shared"))
|
@ -19,6 +19,13 @@ static int cmd_log_wc(int argc, const char **argv, char **envp,
|
||||
rev->commit_format = CMIT_FMT_DEFAULT;
|
||||
rev->verbose_header = 1;
|
||||
argc = setup_revisions(argc, argv, rev, "HEAD");
|
||||
if (rev->always_show_header) {
|
||||
if (rev->diffopt.pickaxe || rev->diffopt.filter) {
|
||||
rev->always_show_header = 0;
|
||||
if (rev->diffopt.output_format == DIFF_FORMAT_RAW)
|
||||
rev->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 1)
|
||||
die("unrecognized argument: %s", argv[1]);
|
||||
|
312
builtin-push.c
Normal file
312
builtin-push.c
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* "git push"
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
#include "run-command.h"
|
||||
#include "builtin.h"
|
||||
|
||||
#define MAX_URI (16)
|
||||
|
||||
static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
|
||||
|
||||
static int all = 0, tags = 0, force = 0, thin = 1;
|
||||
static const char *execute = NULL;
|
||||
|
||||
#define BUF_SIZE (2084)
|
||||
static char buffer[BUF_SIZE];
|
||||
|
||||
static const char **refspec = NULL;
|
||||
static int refspec_nr = 0;
|
||||
|
||||
static void add_refspec(const char *ref)
|
||||
{
|
||||
int nr = refspec_nr + 1;
|
||||
refspec = xrealloc(refspec, nr * sizeof(char *));
|
||||
refspec[nr-1] = ref;
|
||||
refspec_nr = nr;
|
||||
}
|
||||
|
||||
static int expand_one_ref(const char *ref, const unsigned char *sha1)
|
||||
{
|
||||
/* Ignore the "refs/" at the beginning of the refname */
|
||||
ref += 5;
|
||||
|
||||
if (strncmp(ref, "tags/", 5))
|
||||
return 0;
|
||||
|
||||
add_refspec(strdup(ref));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void expand_refspecs(void)
|
||||
{
|
||||
if (all) {
|
||||
if (refspec_nr)
|
||||
die("cannot mix '--all' and a refspec");
|
||||
|
||||
/*
|
||||
* No need to expand "--all" - we'll just use
|
||||
* the "--all" flag to send-pack
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if (!tags)
|
||||
return;
|
||||
for_each_ref(expand_one_ref);
|
||||
}
|
||||
|
||||
static void set_refspecs(const char **refs, int nr)
|
||||
{
|
||||
if (nr) {
|
||||
size_t bytes = nr * sizeof(char *);
|
||||
|
||||
refspec = xrealloc(refspec, bytes);
|
||||
memcpy(refspec, refs, bytes);
|
||||
refspec_nr = nr;
|
||||
}
|
||||
expand_refspecs();
|
||||
}
|
||||
|
||||
static int get_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
int n = 0;
|
||||
FILE *f = fopen(git_path("remotes/%s", repo), "r");
|
||||
int has_explicit_refspec = refspec_nr || all || tags;
|
||||
|
||||
if (!f)
|
||||
return -1;
|
||||
while (fgets(buffer, BUF_SIZE, f)) {
|
||||
int is_refspec;
|
||||
char *s, *p;
|
||||
|
||||
if (!strncmp("URL: ", buffer, 5)) {
|
||||
is_refspec = 0;
|
||||
s = buffer + 5;
|
||||
} else if (!strncmp("Push: ", buffer, 6)) {
|
||||
is_refspec = 1;
|
||||
s = buffer + 6;
|
||||
} else
|
||||
continue;
|
||||
|
||||
/* Remove whitespace at the head.. */
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
if (!*s)
|
||||
continue;
|
||||
|
||||
/* ..and at the end */
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
|
||||
if (!is_refspec) {
|
||||
if (n < MAX_URI)
|
||||
uri[n++] = strdup(s);
|
||||
else
|
||||
error("more than %d URL's specified, ignoreing the rest", MAX_URI);
|
||||
}
|
||||
else if (is_refspec && !has_explicit_refspec)
|
||||
add_refspec(strdup(s));
|
||||
}
|
||||
fclose(f);
|
||||
if (!n)
|
||||
die("remote '%s' has no URL", repo);
|
||||
return n;
|
||||
}
|
||||
|
||||
static const char **config_uri;
|
||||
static const char *config_repo;
|
||||
static int config_repo_len;
|
||||
static int config_current_uri;
|
||||
static int config_get_refspecs;
|
||||
|
||||
static int get_remote_config(const char* key, const char* value)
|
||||
{
|
||||
if (!strncmp(key, "remote.", 7) &&
|
||||
!strncmp(key + 7, config_repo, config_repo_len)) {
|
||||
if (!strcmp(key + 7 + config_repo_len, ".url")) {
|
||||
if (config_current_uri < MAX_URI)
|
||||
config_uri[config_current_uri++] = strdup(value);
|
||||
else
|
||||
error("more than %d URL's specified, ignoring the rest", MAX_URI);
|
||||
}
|
||||
else if (config_get_refspecs &&
|
||||
!strcmp(key + 7 + config_repo_len, ".push"))
|
||||
add_refspec(strdup(value));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_config_remotes_uri(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
config_repo_len = strlen(repo);
|
||||
config_repo = repo;
|
||||
config_current_uri = 0;
|
||||
config_uri = uri;
|
||||
config_get_refspecs = !(refspec_nr || all || tags);
|
||||
|
||||
git_config(get_remote_config);
|
||||
return config_current_uri;
|
||||
}
|
||||
|
||||
static int get_branches_uri(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
const char *slash = strchr(repo, '/');
|
||||
int n = slash ? slash - repo : 1000;
|
||||
FILE *f = fopen(git_path("branches/%.*s", n, repo), "r");
|
||||
char *s, *p;
|
||||
int len;
|
||||
|
||||
if (!f)
|
||||
return 0;
|
||||
s = fgets(buffer, BUF_SIZE, f);
|
||||
fclose(f);
|
||||
if (!s)
|
||||
return 0;
|
||||
while (isspace(*s))
|
||||
s++;
|
||||
if (!*s)
|
||||
return 0;
|
||||
p = s + strlen(s);
|
||||
while (isspace(p[-1]))
|
||||
*--p = 0;
|
||||
len = p - s;
|
||||
if (slash)
|
||||
len += strlen(slash);
|
||||
p = xmalloc(len + 1);
|
||||
strcpy(p, s);
|
||||
if (slash)
|
||||
strcat(p, slash);
|
||||
uri[0] = p;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read remotes and branches file, fill the push target URI
|
||||
* list. If there is no command line refspecs, read Push: lines
|
||||
* to set up the *refspec list as well.
|
||||
* return the number of push target URIs
|
||||
*/
|
||||
static int read_config(const char *repo, const char *uri[MAX_URI])
|
||||
{
|
||||
int n;
|
||||
|
||||
if (*repo != '/') {
|
||||
n = get_remotes_uri(repo, uri);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
n = get_config_remotes_uri(repo, uri);
|
||||
if (n > 0)
|
||||
return n;
|
||||
|
||||
n = get_branches_uri(repo, uri);
|
||||
if (n > 0)
|
||||
return n;
|
||||
}
|
||||
|
||||
uri[0] = repo;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int do_push(const char *repo)
|
||||
{
|
||||
const char *uri[MAX_URI];
|
||||
int i, n;
|
||||
int remote;
|
||||
const char **argv;
|
||||
int argc;
|
||||
|
||||
n = read_config(repo, uri);
|
||||
if (n <= 0)
|
||||
die("bad repository '%s'", repo);
|
||||
|
||||
argv = xmalloc((refspec_nr + 10) * sizeof(char *));
|
||||
argv[0] = "dummy-send-pack";
|
||||
argc = 1;
|
||||
if (all)
|
||||
argv[argc++] = "--all";
|
||||
if (force)
|
||||
argv[argc++] = "--force";
|
||||
if (execute)
|
||||
argv[argc++] = execute;
|
||||
if (thin)
|
||||
argv[argc++] = "--thin";
|
||||
remote = argc;
|
||||
argv[argc++] = "dummy-remote";
|
||||
while (refspec_nr--)
|
||||
argv[argc++] = *refspec++;
|
||||
argv[argc] = NULL;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
int error;
|
||||
const char *dest = uri[i];
|
||||
const char *sender = "git-send-pack";
|
||||
if (!strncmp(dest, "http://", 7) ||
|
||||
!strncmp(dest, "https://", 8))
|
||||
sender = "git-http-push";
|
||||
argv[0] = sender;
|
||||
argv[remote] = dest;
|
||||
error = run_command_v(argc, argv);
|
||||
if (!error)
|
||||
continue;
|
||||
switch (error) {
|
||||
case -ERR_RUN_COMMAND_FORK:
|
||||
die("unable to fork for %s", sender);
|
||||
case -ERR_RUN_COMMAND_EXEC:
|
||||
die("unable to exec %s", sender);
|
||||
case -ERR_RUN_COMMAND_WAITPID:
|
||||
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
|
||||
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
|
||||
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
|
||||
die("%s died with strange error", sender);
|
||||
default:
|
||||
return -error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cmd_push(int argc, const char **argv, char **envp)
|
||||
{
|
||||
int i;
|
||||
const char *repo = "origin"; // default repository
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (arg[0] != '-') {
|
||||
repo = arg;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(arg, "--all")) {
|
||||
all = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--tags")) {
|
||||
tags = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--force")) {
|
||||
force = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--thin")) {
|
||||
thin = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "--no-thin")) {
|
||||
thin = 0;
|
||||
continue;
|
||||
}
|
||||
if (!strncmp(arg, "--exec=", 7)) {
|
||||
execute = arg;
|
||||
continue;
|
||||
}
|
||||
usage(push_usage);
|
||||
}
|
||||
set_refspecs(argv + i, argc - i);
|
||||
return do_push(repo);
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
#include "tree-walk.h"
|
||||
#include "diff.h"
|
||||
#include "revision.h"
|
||||
#include "builtin.h"
|
||||
|
||||
/* bits #0-15 in revision.h */
|
||||
|
||||
@ -36,7 +37,7 @@ static const char rev_list_usage[] =
|
||||
" --bisect"
|
||||
;
|
||||
|
||||
struct rev_info revs;
|
||||
static struct rev_info revs;
|
||||
|
||||
static int bisect_list = 0;
|
||||
static int show_timestamp = 0;
|
||||
@ -291,7 +292,7 @@ static void mark_edges_uninteresting(struct commit_list *list)
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
int cmd_rev_list(int argc, const char **argv, char **envp)
|
||||
{
|
||||
struct commit_list *list;
|
||||
int i;
|
152
builtin-rm.c
Normal file
152
builtin-rm.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* "git rm" builtin command
|
||||
*
|
||||
* Copyright (C) Linus Torvalds 2006
|
||||
*/
|
||||
#include "cache.h"
|
||||
#include "builtin.h"
|
||||
#include "dir.h"
|
||||
#include "cache-tree.h"
|
||||
|
||||
static const char builtin_rm_usage[] =
|
||||
"git-rm [-n] [-v] [-f] <filepattern>...";
|
||||
|
||||
static struct {
|
||||
int nr, alloc;
|
||||
const char **name;
|
||||
} list;
|
||||
|
||||
static void add_list(const char *name)
|
||||
{
|
||||
if (list.nr >= list.alloc) {
|
||||
list.alloc = alloc_nr(list.alloc);
|
||||
list.name = xrealloc(list.name, list.alloc * sizeof(const char *));
|
||||
}
|
||||
list.name[list.nr++] = name;
|
||||
}
|
||||
|
||||
static int remove_file(const char *name)
|
||||
{
|
||||
int ret;
|
||||
char *slash;
|
||||
|
||||
ret = unlink(name);
|
||||
if (!ret && (slash = strrchr(name, '/'))) {
|
||||
char *n = strdup(name);
|
||||
do {
|
||||
n[slash - name] = 0;
|
||||
name = n;
|
||||
} while (!rmdir(name) && (slash = strrchr(name, '/')));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct cache_file cache_file;
|
||||
|
||||
int cmd_rm(int argc, const char **argv, char **envp)
|
||||
{
|
||||
int i, newfd;
|
||||
int verbose = 0, show_only = 0, force = 0;
|
||||
const char *prefix = setup_git_directory();
|
||||
const char **pathspec;
|
||||
char *seen;
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
newfd = hold_index_file_for_update(&cache_file, get_index_file());
|
||||
if (newfd < 0)
|
||||
die("unable to create new index file");
|
||||
|
||||
if (read_cache() < 0)
|
||||
die("index file corrupt");
|
||||
|
||||
for (i = 1 ; i < argc ; i++) {
|
||||
const char *arg = argv[i];
|
||||
|
||||
if (*arg != '-')
|
||||
break;
|
||||
if (!strcmp(arg, "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
if (!strcmp(arg, "-n")) {
|
||||
show_only = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-v")) {
|
||||
verbose = 1;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(arg, "-f")) {
|
||||
force = 1;
|
||||
continue;
|
||||
}
|
||||
die(builtin_rm_usage);
|
||||
}
|
||||
pathspec = get_pathspec(prefix, argv + i);
|
||||
|
||||
seen = NULL;
|
||||
if (pathspec) {
|
||||
for (i = 0; pathspec[i] ; i++)
|
||||
/* nothing */;
|
||||
seen = xmalloc(i);
|
||||
memset(seen, 0, i);
|
||||
}
|
||||
|
||||
for (i = 0; i < active_nr; i++) {
|
||||
struct cache_entry *ce = active_cache[i];
|
||||
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
|
||||
continue;
|
||||
add_list(ce->name);
|
||||
}
|
||||
|
||||
if (pathspec) {
|
||||
const char *match;
|
||||
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
|
||||
if (*match && !seen[i])
|
||||
die("pathspec '%s' did not match any files", match);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First remove the names from the index: we won't commit
|
||||
* the index unless all of them succeed
|
||||
*/
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.name[i];
|
||||
printf("rm '%s'\n", path);
|
||||
|
||||
if (remove_file_from_cache(path))
|
||||
die("git rm: unable to remove %s", path);
|
||||
cache_tree_invalidate_path(active_cache_tree, path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Then, if we used "-f", remove the filenames from the
|
||||
* workspace. If we fail to remove the first one, we
|
||||
* abort the "git rm" (but once we've successfully removed
|
||||
* any file at all, we'll go ahead and commit to it all:
|
||||
* by then we've already committed ourself and can't fail
|
||||
* in the middle)
|
||||
*/
|
||||
if (force) {
|
||||
int removed = 0;
|
||||
for (i = 0; i < list.nr; i++) {
|
||||
const char *path = list.name[i];
|
||||
if (!remove_file(path)) {
|
||||
removed = 1;
|
||||
continue;
|
||||
}
|
||||
if (!removed)
|
||||
die("git rm: %s: %s", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
if (active_cache_changed) {
|
||||
if (write_cache(newfd, active_cache, active_nr) ||
|
||||
commit_index_file(&cache_file))
|
||||
die("Unable to write new index file");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
10
builtin.h
10
builtin.h
@ -19,5 +19,15 @@ extern int cmd_version(int argc, const char **argv, char **envp);
|
||||
extern int cmd_whatchanged(int argc, const char **argv, char **envp);
|
||||
extern int cmd_show(int argc, const char **argv, char **envp);
|
||||
extern int cmd_log(int argc, const char **argv, char **envp);
|
||||
extern int cmd_diff(int argc, const char **argv, char **envp);
|
||||
extern int cmd_count_objects(int argc, const char **argv, char **envp);
|
||||
|
||||
extern int cmd_push(int argc, const char **argv, char **envp);
|
||||
extern int cmd_grep(int argc, const char **argv, char **envp);
|
||||
extern int cmd_rm(int argc, const char **argv, char **envp);
|
||||
extern int cmd_add(int argc, const char **argv, char **envp);
|
||||
extern int cmd_rev_list(int argc, const char **argv, char **envp);
|
||||
extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
|
||||
extern int cmd_init_db(int argc, const char **argv, char **envp);
|
||||
|
||||
#endif
|
||||
|
14
cache.h
14
cache.h
@ -136,12 +136,14 @@ 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);
|
||||
extern void verify_non_filename(const char *prefix, const char *name);
|
||||
|
||||
#define alloc_nr(x) (((x)+16)*3/2)
|
||||
|
||||
/* Initialize and use the cache information */
|
||||
extern int read_cache(void);
|
||||
extern int write_cache(int newfd, struct cache_entry **cache, int entries);
|
||||
extern int verify_path(const char *path);
|
||||
extern int cache_name_pos(const char *name, int namelen);
|
||||
#define ADD_CACHE_OK_TO_ADD 1 /* Ok to add */
|
||||
#define ADD_CACHE_OK_TO_REPLACE 2 /* Ok to replace file/directory */
|
||||
@ -158,6 +160,12 @@ extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_o
|
||||
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
|
||||
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
|
||||
|
||||
#define REFRESH_REALLY 0x0001 /* ignore_valid */
|
||||
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
|
||||
#define REFRESH_QUIET 0x0004 /* be quiet about it */
|
||||
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
|
||||
extern int refresh_cache(unsigned int flags);
|
||||
|
||||
struct cache_file {
|
||||
struct cache_file *next;
|
||||
char lockfile[PATH_MAX];
|
||||
@ -169,7 +177,7 @@ extern void rollback_index_file(struct cache_file *);
|
||||
/* Environment bits from configuration mechanism */
|
||||
extern int trust_executable_bit;
|
||||
extern int assume_unchanged;
|
||||
extern int only_use_symrefs;
|
||||
extern int prefer_symlink_refs;
|
||||
extern int warn_ambiguous_refs;
|
||||
extern int diff_rename_limit_default;
|
||||
extern int shared_repository;
|
||||
@ -363,4 +371,8 @@ extern int receive_keep_pack(int fd[2], const char *me, int quiet);
|
||||
/* pager.c */
|
||||
extern void setup_pager(void);
|
||||
|
||||
/* base85 */
|
||||
int decode_85(char *dst, char *line, int linelen);
|
||||
void encode_85(char *buf, unsigned char *data, int bytes);
|
||||
|
||||
#endif /* CACHE_H */
|
||||
|
@ -103,8 +103,10 @@ int main(int argc, char **argv)
|
||||
|
||||
setup_git_directory();
|
||||
git_config(git_default_config);
|
||||
if (argc != 3 || get_sha1(argv[2], sha1))
|
||||
if (argc != 3)
|
||||
usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
|
||||
if (get_sha1(argv[2], sha1))
|
||||
die("Not a valid object name %s", argv[2]);
|
||||
|
||||
opt = 0;
|
||||
if ( argv[1][0] == '-' ) {
|
||||
@ -133,8 +135,7 @@ int main(int argc, char **argv)
|
||||
return !has_sha1_file(sha1);
|
||||
|
||||
case 'p':
|
||||
if (get_sha1(argv[2], sha1) ||
|
||||
sha1_object_info(sha1, type, NULL))
|
||||
if (sha1_object_info(sha1, type, NULL))
|
||||
die("Not a valid object name %s", argv[2]);
|
||||
|
||||
/* custom pretty-print here */
|
||||
|
@ -1,17 +0,0 @@
|
||||
/*
|
||||
* GIT - The information manager from hell
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include "refs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
if (ac != 2)
|
||||
usage("git-check-ref-format refname");
|
||||
if (check_ref_format(av[1]))
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
@ -270,12 +270,16 @@ int main(int argc, char **argv)
|
||||
/* Check out named files first */
|
||||
for ( ; i < argc; i++) {
|
||||
const char *arg = argv[i];
|
||||
const char *p;
|
||||
|
||||
if (all)
|
||||
die("git-checkout-index: don't mix '--all' and explicit filenames");
|
||||
if (read_from_stdin)
|
||||
die("git-checkout-index: don't mix '--stdin' and explicit filenames");
|
||||
checkout_file(prefix_path(prefix, prefix_length, arg));
|
||||
p = prefix_path(prefix, prefix_length, arg);
|
||||
checkout_file(p);
|
||||
if (p < arg || p > arg + strlen(arg))
|
||||
free((char*)p);
|
||||
}
|
||||
|
||||
if (read_from_stdin) {
|
||||
@ -285,6 +289,8 @@ int main(int argc, char **argv)
|
||||
strbuf_init(&buf);
|
||||
while (1) {
|
||||
char *path_name;
|
||||
const char *p;
|
||||
|
||||
read_line(&buf, stdin, line_termination);
|
||||
if (buf.eof)
|
||||
break;
|
||||
@ -292,7 +298,10 @@ int main(int argc, char **argv)
|
||||
path_name = unquote_c_style(buf.buf, NULL);
|
||||
else
|
||||
path_name = buf.buf;
|
||||
checkout_file(prefix_path(prefix, prefix_length, path_name));
|
||||
p = prefix_path(prefix, prefix_length, path_name);
|
||||
checkout_file(p);
|
||||
if (p < path_name || p > path_name + strlen(path_name))
|
||||
free((char *)p);
|
||||
if (path_name != buf.buf)
|
||||
free(path_name);
|
||||
}
|
||||
|
@ -608,6 +608,7 @@ static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
|
||||
int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
|
||||
mmfile_t result_file;
|
||||
|
||||
context = opt->context;
|
||||
/* Read the result of merge first */
|
||||
if (!working_tree_file)
|
||||
result = grab_blob(elem->sha1, &result_size);
|
||||
@ -831,15 +832,16 @@ void show_combined_diff(struct combine_diff_path *p,
|
||||
}
|
||||
}
|
||||
|
||||
void diff_tree_combined_merge(const unsigned char *sha1,
|
||||
int dense, struct rev_info *rev)
|
||||
void diff_tree_combined(const unsigned char *sha1,
|
||||
const unsigned char parent[][20],
|
||||
int num_parent,
|
||||
int dense,
|
||||
struct rev_info *rev)
|
||||
{
|
||||
struct diff_options *opt = &rev->diffopt;
|
||||
struct commit *commit = lookup_commit(sha1);
|
||||
struct diff_options diffopts;
|
||||
struct commit_list *parents;
|
||||
struct combine_diff_path *p, *paths = NULL;
|
||||
int num_parent, i, num_paths;
|
||||
int i, num_paths;
|
||||
int do_diffstat;
|
||||
|
||||
do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
|
||||
@ -849,17 +851,8 @@ void diff_tree_combined_merge(const unsigned char *sha1,
|
||||
diffopts.with_stat = 0;
|
||||
diffopts.recursive = 1;
|
||||
|
||||
/* count parents */
|
||||
for (parents = commit->parents, num_parent = 0;
|
||||
parents;
|
||||
parents = parents->next, num_parent++)
|
||||
; /* nothing */
|
||||
|
||||
/* find set of paths that everybody touches */
|
||||
for (parents = commit->parents, i = 0;
|
||||
parents;
|
||||
parents = parents->next, i++) {
|
||||
struct commit *parent = parents->item;
|
||||
for (i = 0; i < num_parent; i++) {
|
||||
/* show stat against the first parent even
|
||||
* when doing combined diff.
|
||||
*/
|
||||
@ -867,8 +860,7 @@ void diff_tree_combined_merge(const unsigned char *sha1,
|
||||
diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
|
||||
else
|
||||
diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
|
||||
diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
|
||||
&diffopts);
|
||||
diff_tree_sha1(parent[i], sha1, "", &diffopts);
|
||||
diffcore_std(&diffopts);
|
||||
paths = intersect_paths(paths, i, num_parent);
|
||||
|
||||
@ -907,3 +899,25 @@ void diff_tree_combined_merge(const unsigned char *sha1,
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void diff_tree_combined_merge(const unsigned char *sha1,
|
||||
int dense, struct rev_info *rev)
|
||||
{
|
||||
int num_parent;
|
||||
const unsigned char (*parent)[20];
|
||||
struct commit *commit = lookup_commit(sha1);
|
||||
struct commit_list *parents;
|
||||
|
||||
/* count parents */
|
||||
for (parents = commit->parents, num_parent = 0;
|
||||
parents;
|
||||
parents = parents->next, num_parent++)
|
||||
; /* nothing */
|
||||
|
||||
parent = xmalloc(num_parent * sizeof(*parent));
|
||||
for (parents = commit->parents, num_parent = 0;
|
||||
parents;
|
||||
parents = parents->next, num_parent++)
|
||||
memcpy(parent + num_parent, parents->item->object.sha1, 20);
|
||||
diff_tree_combined(sha1, parent, num_parent, dense, rev);
|
||||
}
|
||||
|
@ -91,15 +91,19 @@ int main(int argc, char **argv)
|
||||
|
||||
git_config(git_default_config);
|
||||
|
||||
if (argc < 2 || get_sha1(argv[1], tree_sha1) < 0)
|
||||
if (argc < 2)
|
||||
usage(commit_tree_usage);
|
||||
if (get_sha1(argv[1], tree_sha1))
|
||||
die("Not a valid object name %s", argv[1]);
|
||||
|
||||
check_valid(tree_sha1, tree_type);
|
||||
for (i = 2; i < argc; i += 2) {
|
||||
char *a, *b;
|
||||
a = argv[i]; b = argv[i+1];
|
||||
if (!b || strcmp(a, "-p") || get_sha1(b, parent_sha1[parents]))
|
||||
if (!b || strcmp(a, "-p"))
|
||||
usage(commit_tree_usage);
|
||||
if (get_sha1(b, parent_sha1[parents]))
|
||||
die("Not a valid object name %s", b);
|
||||
check_valid(parent_sha1[parents], commit_type);
|
||||
if (new_parent(parents))
|
||||
parents++;
|
||||
|
38
commit.c
38
commit.c
@ -22,23 +22,33 @@ struct sort_node
|
||||
|
||||
const char *commit_type = "commit";
|
||||
|
||||
struct cmt_fmt_map {
|
||||
const char *n;
|
||||
size_t cmp_len;
|
||||
enum cmit_fmt v;
|
||||
} cmt_fmts[] = {
|
||||
{ "raw", 1, CMIT_FMT_RAW },
|
||||
{ "medium", 1, CMIT_FMT_MEDIUM },
|
||||
{ "short", 1, CMIT_FMT_SHORT },
|
||||
{ "full", 5, CMIT_FMT_FULL },
|
||||
{ "fuller", 5, CMIT_FMT_FULLER },
|
||||
{ "oneline", 1, CMIT_FMT_ONELINE },
|
||||
};
|
||||
|
||||
enum cmit_fmt get_commit_format(const char *arg)
|
||||
{
|
||||
if (!*arg)
|
||||
int i;
|
||||
|
||||
if (!arg || !*arg)
|
||||
return CMIT_FMT_DEFAULT;
|
||||
if (!strcmp(arg, "=raw"))
|
||||
return CMIT_FMT_RAW;
|
||||
if (!strcmp(arg, "=medium"))
|
||||
return CMIT_FMT_MEDIUM;
|
||||
if (!strcmp(arg, "=short"))
|
||||
return CMIT_FMT_SHORT;
|
||||
if (!strcmp(arg, "=full"))
|
||||
return CMIT_FMT_FULL;
|
||||
if (!strcmp(arg, "=fuller"))
|
||||
return CMIT_FMT_FULLER;
|
||||
if (!strcmp(arg, "=oneline"))
|
||||
return CMIT_FMT_ONELINE;
|
||||
die("invalid --pretty format");
|
||||
if (*arg == '=')
|
||||
arg++;
|
||||
for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
|
||||
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len))
|
||||
return cmt_fmts[i].v;
|
||||
}
|
||||
|
||||
die("invalid --pretty format: %s", arg);
|
||||
}
|
||||
|
||||
static struct commit *check_commit(struct object *obj,
|
||||
|
131
config.c
131
config.c
@ -60,6 +60,12 @@ static char *parse_value(void)
|
||||
space = 1;
|
||||
continue;
|
||||
}
|
||||
if (!quote) {
|
||||
if (c == ';' || c == '#') {
|
||||
comment = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (space) {
|
||||
if (len)
|
||||
value[len++] = ' ';
|
||||
@ -93,12 +99,6 @@ static char *parse_value(void)
|
||||
quote = 1-quote;
|
||||
continue;
|
||||
}
|
||||
if (!quote) {
|
||||
if (c == ';' || c == '#') {
|
||||
comment = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
value[len++] = c;
|
||||
}
|
||||
}
|
||||
@ -134,6 +134,41 @@ static int get_value(config_fn_t fn, char *name, unsigned int len)
|
||||
return fn(name, value);
|
||||
}
|
||||
|
||||
static int get_extended_base_var(char *name, int baselen, int c)
|
||||
{
|
||||
do {
|
||||
if (c == '\n')
|
||||
return -1;
|
||||
c = get_next_char();
|
||||
} while (isspace(c));
|
||||
|
||||
/* We require the format to be '[base "extension"]' */
|
||||
if (c != '"')
|
||||
return -1;
|
||||
name[baselen++] = '.';
|
||||
|
||||
for (;;) {
|
||||
int c = get_next_char();
|
||||
if (c == '\n')
|
||||
return -1;
|
||||
if (c == '"')
|
||||
break;
|
||||
if (c == '\\') {
|
||||
c = get_next_char();
|
||||
if (c == '\n')
|
||||
return -1;
|
||||
}
|
||||
name[baselen++] = c;
|
||||
if (baselen > MAXNAME / 2)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Final ']' */
|
||||
if (get_next_char() != ']')
|
||||
return -1;
|
||||
return baselen;
|
||||
}
|
||||
|
||||
static int get_base_var(char *name)
|
||||
{
|
||||
int baselen = 0;
|
||||
@ -144,6 +179,8 @@ static int get_base_var(char *name)
|
||||
return -1;
|
||||
if (c == ']')
|
||||
return baselen;
|
||||
if (isspace(c))
|
||||
return get_extended_base_var(name, baselen, c);
|
||||
if (!isalnum(c) && c != '.')
|
||||
return -1;
|
||||
if (baselen > MAXNAME / 2)
|
||||
@ -227,8 +264,8 @@ int git_default_config(const char *var, const char *value)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!strcmp(var, "core.symrefsonly")) {
|
||||
only_use_symrefs = git_config_bool(var, value);
|
||||
if (!strcmp(var, "core.prefersymlinkrefs")) {
|
||||
prefer_symlink_refs = git_config_bool(var, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -335,16 +372,43 @@ static int store_aux(const char* key, const char* value)
|
||||
store.offset[store.seen] = ftell(config_file);
|
||||
store.state = KEY_SEEN;
|
||||
store.seen++;
|
||||
} else if(!strncmp(key, store.key, store.baselen))
|
||||
store.state = SECTION_SEEN;
|
||||
} else {
|
||||
if (strrchr(key, '.') - key == store.baselen &&
|
||||
!strncmp(key, store.key, store.baselen)) {
|
||||
store.state = SECTION_SEEN;
|
||||
store.offset[store.seen] = ftell(config_file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void store_write_section(int fd, const char* key)
|
||||
{
|
||||
const char *dot = strchr(key, '.');
|
||||
int len1 = store.baselen, len2 = -1;
|
||||
|
||||
dot = strchr(key, '.');
|
||||
if (dot) {
|
||||
int dotlen = dot - key;
|
||||
if (dotlen < len1) {
|
||||
len2 = len1 - dotlen - 1;
|
||||
len1 = dotlen;
|
||||
}
|
||||
}
|
||||
|
||||
write(fd, "[", 1);
|
||||
write(fd, key, store.baselen);
|
||||
write(fd, key, len1);
|
||||
if (len2 >= 0) {
|
||||
write(fd, " \"", 2);
|
||||
while (--len2 >= 0) {
|
||||
unsigned char c = *++dot;
|
||||
if (c == '"')
|
||||
write(fd, "\\", 1);
|
||||
write(fd, &c, 1);
|
||||
}
|
||||
write(fd, "\"", 1);
|
||||
}
|
||||
write(fd, "]\n", 2);
|
||||
}
|
||||
|
||||
@ -418,8 +482,8 @@ int git_config_set(const char* key, const char* value)
|
||||
int git_config_set_multivar(const char* key, const char* value,
|
||||
const char* value_regex, int multi_replace)
|
||||
{
|
||||
int i;
|
||||
int fd, in_fd;
|
||||
int i, dot;
|
||||
int fd = -1, in_fd;
|
||||
int ret;
|
||||
char* config_filename = strdup(git_path("config"));
|
||||
char* lock_file = strdup(git_path("config.lock"));
|
||||
@ -443,16 +507,23 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
* Validate the key and while at it, lower case it for matching.
|
||||
*/
|
||||
store.key = (char*)malloc(strlen(key)+1);
|
||||
for (i = 0; key[i]; i++)
|
||||
if (i != store.baselen &&
|
||||
((!isalnum(key[i]) && key[i] != '.') ||
|
||||
(i == store.baselen+1 && !isalpha(key[i])))) {
|
||||
fprintf(stderr, "invalid key: %s\n", key);
|
||||
free(store.key);
|
||||
ret = 1;
|
||||
goto out_free;
|
||||
} else
|
||||
store.key[i] = tolower(key[i]);
|
||||
dot = 0;
|
||||
for (i = 0; key[i]; i++) {
|
||||
unsigned char c = key[i];
|
||||
if (c == '.')
|
||||
dot = 1;
|
||||
/* Leave the extended basename untouched.. */
|
||||
if (!dot || i > store.baselen) {
|
||||
if (!isalnum(c) || (i == store.baselen+1 && !isalpha(c))) {
|
||||
fprintf(stderr, "invalid key: %s\n", key);
|
||||
free(store.key);
|
||||
ret = 1;
|
||||
goto out_free;
|
||||
}
|
||||
c = tolower(c);
|
||||
}
|
||||
store.key[i] = c;
|
||||
}
|
||||
store.key[i] = 0;
|
||||
|
||||
/*
|
||||
@ -477,15 +548,11 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
if ( ENOENT != errno ) {
|
||||
error("opening %s: %s", config_filename,
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
unlink(lock_file);
|
||||
ret = 3; /* same as "invalid config file" */
|
||||
goto out_free;
|
||||
}
|
||||
/* if nothing to unset, error out */
|
||||
if (value == NULL) {
|
||||
close(fd);
|
||||
unlink(lock_file);
|
||||
ret = 5;
|
||||
goto out_free;
|
||||
}
|
||||
@ -548,8 +615,6 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
/* if nothing to unset, or too many matches, error out */
|
||||
if ((store.seen == 0 && value == NULL) ||
|
||||
(store.seen > 1 && multi_replace == 0)) {
|
||||
close(fd);
|
||||
unlink(lock_file);
|
||||
ret = 5;
|
||||
goto out_free;
|
||||
}
|
||||
@ -598,8 +663,6 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
unlink(config_filename);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
if (rename(lock_file, config_filename) < 0) {
|
||||
fprintf(stderr, "Could not rename the lock file?\n");
|
||||
ret = 4;
|
||||
@ -609,10 +672,14 @@ int git_config_set_multivar(const char* key, const char* value,
|
||||
ret = 0;
|
||||
|
||||
out_free:
|
||||
if (0 <= fd)
|
||||
close(fd);
|
||||
if (config_filename)
|
||||
free(config_filename);
|
||||
if (lock_file)
|
||||
if (lock_file) {
|
||||
unlink(lock_file);
|
||||
free(lock_file);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use vars qw/ $AUTHOR $VERSION
|
||||
$GIT_SVN_INDEX $GIT_SVN
|
||||
$GIT_DIR $REV_DIR/;
|
||||
$AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
|
||||
$VERSION = '0.11.0';
|
||||
$VERSION = '1.0.0';
|
||||
|
||||
use Cwd qw/abs_path/;
|
||||
$GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
|
||||
@ -42,7 +42,8 @@ my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
|
||||
my %cmd = (
|
||||
fetch => [ \&fetch, "Download new revisions from SVN",
|
||||
{ 'revision|r=s' => \$_revision, %fc_opts } ],
|
||||
init => [ \&init, "Initialize and fetch (import)", { } ],
|
||||
init => [ \&init, "Initialize a repo for tracking" .
|
||||
" (requires URL argument)", { } ],
|
||||
commit => [ \&commit, "Commit git revisions to SVN",
|
||||
{ 'stdin|' => \$_stdin,
|
||||
'edit|e' => \$_edit,
|
||||
@ -220,7 +221,8 @@ when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN
|
||||
}
|
||||
|
||||
sub init {
|
||||
$SVN_URL = shift or croak "SVN repository location required\n";
|
||||
$SVN_URL = shift or die "SVN repository location required " .
|
||||
"as a command-line argument\n";
|
||||
unless (-d $GIT_DIR) {
|
||||
sys('git-init-db');
|
||||
}
|
||||
|
@ -36,17 +36,22 @@ COMMANDS
|
||||
--------
|
||||
init::
|
||||
Creates an empty git repository with additional metadata
|
||||
directories for git-svn. The SVN_URL must be specified
|
||||
at this point.
|
||||
directories for git-svn. The Subversion URL must be specified
|
||||
as a command-line argument.
|
||||
|
||||
fetch::
|
||||
Fetch unfetched revisions from the SVN_URL we are tracking.
|
||||
refs/heads/remotes/git-svn will be updated to the latest revision.
|
||||
Fetch unfetched revisions from the Subversion URL we are
|
||||
tracking. refs/remotes/git-svn will be updated to the
|
||||
latest revision.
|
||||
|
||||
Note: You should never attempt to modify the remotes/git-svn branch
|
||||
outside of git-svn. Instead, create a branch from remotes/git-svn
|
||||
and work on that branch. Use the 'commit' command (see below)
|
||||
to write git commits back to remotes/git-svn.
|
||||
Note: You should never attempt to modify the remotes/git-svn
|
||||
branch outside of git-svn. Instead, create a branch from
|
||||
remotes/git-svn and work on that branch. Use the 'commit'
|
||||
command (see below) to write git commits back to
|
||||
remotes/git-svn.
|
||||
|
||||
See 'Additional Fetch Arguments' if you are interested in
|
||||
manually joining branches on commit.
|
||||
|
||||
commit::
|
||||
Commit specified commit or tree objects to SVN. This relies on
|
||||
@ -62,9 +67,9 @@ rebuild::
|
||||
tracked with git-svn. Unfortunately, git-clone does not clone
|
||||
git-svn metadata and the svn working tree that git-svn uses for
|
||||
its operations. This rebuilds the metadata so git-svn can
|
||||
resume fetch operations. SVN_URL may be optionally specified if
|
||||
the directory/repository you're tracking has moved or changed
|
||||
protocols.
|
||||
resume fetch operations. A Subversion URL may be optionally
|
||||
specified at the command-line if the directory/repository you're
|
||||
tracking has moved or changed protocols.
|
||||
|
||||
show-ignore::
|
||||
Recursively finds and lists the svn:ignore property on
|
||||
@ -123,6 +128,24 @@ OPTIONS
|
||||
repo-config key: svn.l
|
||||
repo-config key: svn.findcopiesharder
|
||||
|
||||
-A<filename>::
|
||||
--authors-file=<filename>::
|
||||
|
||||
Syntax is compatible with the files used by git-svnimport and
|
||||
git-cvsimport:
|
||||
|
||||
------------------------------------------------------------------------
|
||||
loginname = Joe User <user@example.com>
|
||||
------------------------------------------------------------------------
|
||||
|
||||
If this option is specified and git-svn encounters an SVN
|
||||
committer name that does not exist in the authors-file, git-svn
|
||||
will abort operation. The user will then have to add the
|
||||
appropriate entry. Re-running the previous git-svn command
|
||||
after the authors-file is modified should continue operation.
|
||||
|
||||
repo-config key: svn.authors-file
|
||||
|
||||
ADVANCED OPTIONS
|
||||
----------------
|
||||
-b<refname>::
|
||||
|
35
contrib/remotes2config.sh
Normal file
35
contrib/remotes2config.sh
Normal file
@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Use this tool to rewrite your .git/remotes/ files into the config.
|
||||
|
||||
. git-sh-setup
|
||||
|
||||
if [ -d "$GIT_DIR"/remotes ]; then
|
||||
echo "Rewriting $GIT_DIR/remotes" >&2
|
||||
error=0
|
||||
# rewrite into config
|
||||
{
|
||||
cd "$GIT_DIR"/remotes
|
||||
ls | while read f; do
|
||||
name=$(echo -n "$f" | tr -c "A-Za-z0-9" ".")
|
||||
sed -n \
|
||||
-e "s/^URL: \(.*\)$/remote.$name.url \1 ./p" \
|
||||
-e "s/^Pull: \(.*\)$/remote.$name.fetch \1 ^$ /p" \
|
||||
-e "s/^Push: \(.*\)$/remote.$name.push \1 ^$ /p" \
|
||||
< "$f"
|
||||
done
|
||||
echo done
|
||||
} | while read key value regex; do
|
||||
case $key in
|
||||
done)
|
||||
if [ $error = 0 ]; then
|
||||
mv "$GIT_DIR"/remotes "$GIT_DIR"/remotes.old
|
||||
fi ;;
|
||||
*)
|
||||
echo "git-repo-config $key "$value" $regex"
|
||||
git-repo-config $key "$value" $regex || error=1 ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
|
@ -321,8 +321,10 @@ int main(int argc, char **argv)
|
||||
|
||||
setup_git_directory();
|
||||
|
||||
if (argc != 2 || get_sha1(argv[1], sha1))
|
||||
if (argc != 2)
|
||||
usage("git-convert-objects <sha1>");
|
||||
if (get_sha1(argv[1], sha1))
|
||||
die("Not a valid object name %s", argv[1]);
|
||||
|
||||
entry = convert_entry(sha1);
|
||||
printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1));
|
||||
|
77
delta.h
77
delta.h
@ -1,12 +1,75 @@
|
||||
#ifndef DELTA_H
|
||||
#define DELTA_H
|
||||
|
||||
/* handling of delta buffers */
|
||||
extern void *diff_delta(void *from_buf, unsigned long from_size,
|
||||
void *to_buf, unsigned long to_size,
|
||||
unsigned long *delta_size, unsigned long max_size);
|
||||
extern void *patch_delta(void *src_buf, unsigned long src_size,
|
||||
void *delta_buf, unsigned long delta_size,
|
||||
/* opaque object for delta index */
|
||||
struct delta_index;
|
||||
|
||||
/*
|
||||
* create_delta_index: compute index data from given buffer
|
||||
*
|
||||
* This returns a pointer to a struct delta_index that should be passed to
|
||||
* subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
|
||||
* is returned on failure. The given buffer must not be freed nor altered
|
||||
* before free_delta_index() is called. The returned pointer must be freed
|
||||
* using free_delta_index().
|
||||
*/
|
||||
extern struct delta_index *
|
||||
create_delta_index(const void *buf, unsigned long bufsize);
|
||||
|
||||
/*
|
||||
* free_delta_index: free the index created by create_delta_index()
|
||||
*
|
||||
* Given pointer must be what create_delta_index() returned, or NULL.
|
||||
*/
|
||||
extern void free_delta_index(struct delta_index *index);
|
||||
|
||||
/*
|
||||
* create_delta: create a delta from given index for the given buffer
|
||||
*
|
||||
* This function may be called multiple times with different buffers using
|
||||
* the same delta_index pointer. If max_delta_size is non-zero and the
|
||||
* resulting delta is to be larger than max_delta_size then NULL is returned.
|
||||
* On success, a non-NULL pointer to the buffer with the delta data is
|
||||
* returned and *delta_size is updated with its size. The returned buffer
|
||||
* must be freed by the caller.
|
||||
*/
|
||||
extern void *
|
||||
create_delta(const struct delta_index *index,
|
||||
const void *buf, unsigned long bufsize,
|
||||
unsigned long *delta_size, unsigned long max_delta_size);
|
||||
|
||||
/*
|
||||
* diff_delta: create a delta from source buffer to target buffer
|
||||
*
|
||||
* If max_delta_size is non-zero and the resulting delta is to be larger
|
||||
* than max_delta_size then NULL is returned. On success, a non-NULL
|
||||
* pointer to the buffer with the delta data is returned and *delta_size is
|
||||
* updated with its size. The returned buffer must be freed by the caller.
|
||||
*/
|
||||
static inline void *
|
||||
diff_delta(const void *src_buf, unsigned long src_bufsize,
|
||||
const void *trg_buf, unsigned long trg_bufsize,
|
||||
unsigned long *delta_size, unsigned long max_delta_size)
|
||||
{
|
||||
struct delta_index *index = create_delta_index(src_buf, src_bufsize);
|
||||
if (index) {
|
||||
void *delta = create_delta(index, trg_buf, trg_bufsize,
|
||||
delta_size, max_delta_size);
|
||||
free_delta_index(index);
|
||||
return delta;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* patch_delta: recreate target buffer given source buffer and delta data
|
||||
*
|
||||
* On success, a non-NULL pointer to the target buffer is returned and
|
||||
* *trg_bufsize is updated with its size. On failure a NULL pointer is
|
||||
* returned. The returned buffer must be freed by the caller.
|
||||
*/
|
||||
extern void *patch_delta(const void *src_buf, unsigned long src_size,
|
||||
const void *delta_buf, unsigned long delta_size,
|
||||
unsigned long *dst_size);
|
||||
|
||||
/* the smallest possible delta size is 4 bytes */
|
||||
@ -14,7 +77,7 @@ extern void *patch_delta(void *src_buf, unsigned long src_size,
|
||||
|
||||
/*
|
||||
* This must be called twice on the delta data buffer, first to get the
|
||||
* expected reference buffer size, and again to get the result buffer size.
|
||||
* expected source buffer size, and again to get the target buffer size.
|
||||
*/
|
||||
static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
|
||||
const unsigned char *top)
|
||||
|
@ -105,11 +105,11 @@ static void describe(char *arg, int last_one)
|
||||
static int initialized = 0;
|
||||
struct commit_name *n;
|
||||
|
||||
if (get_sha1(arg, sha1) < 0)
|
||||
usage(describe_usage);
|
||||
if (get_sha1(arg, sha1))
|
||||
die("Not a valid object name %s", arg);
|
||||
cmit = lookup_commit_reference(sha1);
|
||||
if (!cmit)
|
||||
usage(describe_usage);
|
||||
die("%s is not a valid '%s' object", arg, commit_type);
|
||||
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
|
364
diff-delta.c
364
diff-delta.c
@ -20,69 +20,187 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <zlib.h>
|
||||
#include "delta.h"
|
||||
|
||||
|
||||
/* block size: min = 16, max = 64k, power of 2 */
|
||||
#define BLK_SIZE 16
|
||||
/* maximum hash entry list for the same hash bucket */
|
||||
#define HASH_LIMIT 64
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define RABIN_SHIFT 23
|
||||
#define RABIN_WINDOW 16
|
||||
|
||||
#define GR_PRIME 0x9e370001
|
||||
#define HASH(v, shift) (((unsigned int)(v) * GR_PRIME) >> (shift))
|
||||
|
||||
struct index {
|
||||
const unsigned char *ptr;
|
||||
unsigned int val;
|
||||
struct index *next;
|
||||
static const unsigned int T[256] = {
|
||||
0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344,
|
||||
0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259,
|
||||
0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85,
|
||||
0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2,
|
||||
0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a,
|
||||
0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db,
|
||||
0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753,
|
||||
0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964,
|
||||
0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8,
|
||||
0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5,
|
||||
0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81,
|
||||
0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6,
|
||||
0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e,
|
||||
0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77,
|
||||
0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff,
|
||||
0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8,
|
||||
0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc,
|
||||
0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1,
|
||||
0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d,
|
||||
0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a,
|
||||
0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2,
|
||||
0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02,
|
||||
0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a,
|
||||
0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd,
|
||||
0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61,
|
||||
0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c,
|
||||
0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08,
|
||||
0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f,
|
||||
0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7,
|
||||
0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe,
|
||||
0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76,
|
||||
0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141,
|
||||
0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65,
|
||||
0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78,
|
||||
0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4,
|
||||
0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93,
|
||||
0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b,
|
||||
0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa,
|
||||
0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872,
|
||||
0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645,
|
||||
0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99,
|
||||
0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84,
|
||||
0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811
|
||||
};
|
||||
|
||||
static struct index ** delta_index(const unsigned char *buf,
|
||||
unsigned long bufsize,
|
||||
unsigned long trg_bufsize,
|
||||
unsigned int *hash_shift)
|
||||
{
|
||||
unsigned int i, hsize, hshift, hlimit, entries, *hash_count;
|
||||
const unsigned char *data;
|
||||
struct index *entry, **hash;
|
||||
void *mem;
|
||||
static const unsigned int U[256] = {
|
||||
0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a,
|
||||
0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48,
|
||||
0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511,
|
||||
0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d,
|
||||
0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8,
|
||||
0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe,
|
||||
0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb,
|
||||
0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937,
|
||||
0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e,
|
||||
0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c,
|
||||
0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d,
|
||||
0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1,
|
||||
0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4,
|
||||
0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa,
|
||||
0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef,
|
||||
0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263,
|
||||
0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302,
|
||||
0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000,
|
||||
0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59,
|
||||
0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5,
|
||||
0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90,
|
||||
0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7,
|
||||
0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2,
|
||||
0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e,
|
||||
0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467,
|
||||
0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765,
|
||||
0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604,
|
||||
0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88,
|
||||
0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd,
|
||||
0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3,
|
||||
0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996,
|
||||
0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a,
|
||||
0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b,
|
||||
0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609,
|
||||
0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50,
|
||||
0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc,
|
||||
0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99,
|
||||
0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf,
|
||||
0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa,
|
||||
0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176,
|
||||
0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f,
|
||||
0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d,
|
||||
0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a
|
||||
};
|
||||
|
||||
/* determine index hash size */
|
||||
entries = bufsize / BLK_SIZE;
|
||||
struct index_entry {
|
||||
const unsigned char *ptr;
|
||||
unsigned int val;
|
||||
struct index_entry *next;
|
||||
};
|
||||
|
||||
struct delta_index {
|
||||
const void *src_buf;
|
||||
unsigned long src_size;
|
||||
unsigned int hash_mask;
|
||||
struct index_entry *hash[0];
|
||||
};
|
||||
|
||||
struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
|
||||
{
|
||||
unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
|
||||
const unsigned char *data, *buffer = buf;
|
||||
struct delta_index *index;
|
||||
struct index_entry *entry, **hash;
|
||||
void *mem;
|
||||
unsigned long memsize;
|
||||
|
||||
if (!buf || !bufsize)
|
||||
return NULL;
|
||||
|
||||
/* Determine index hash size. Note that indexing skips the
|
||||
first byte to allow for optimizing the rabin polynomial
|
||||
initialization in create_delta(). */
|
||||
entries = (bufsize - 1) / RABIN_WINDOW;
|
||||
hsize = entries / 4;
|
||||
for (i = 4; (1 << i) < hsize && i < 31; i++);
|
||||
hsize = 1 << i;
|
||||
hshift = 32 - i;
|
||||
*hash_shift = hshift;
|
||||
hmask = hsize - 1;
|
||||
|
||||
/* allocate lookup index */
|
||||
mem = malloc(hsize * sizeof(*hash) + entries * sizeof(*entry));
|
||||
memsize = sizeof(*index) +
|
||||
sizeof(*hash) * hsize +
|
||||
sizeof(*entry) * entries;
|
||||
mem = malloc(memsize);
|
||||
if (!mem)
|
||||
return NULL;
|
||||
index = mem;
|
||||
mem = index + 1;
|
||||
hash = mem;
|
||||
entry = mem + hsize * sizeof(*hash);
|
||||
mem = hash + hsize;
|
||||
entry = mem;
|
||||
|
||||
index->src_buf = buf;
|
||||
index->src_size = bufsize;
|
||||
index->hash_mask = hmask;
|
||||
memset(hash, 0, hsize * sizeof(*hash));
|
||||
|
||||
/* allocate an array to count hash entries */
|
||||
hash_count = calloc(hsize, sizeof(*hash_count));
|
||||
if (!hash_count) {
|
||||
free(hash);
|
||||
free(index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* then populate the index */
|
||||
data = buf + entries * BLK_SIZE - BLK_SIZE;
|
||||
while (data >= buf) {
|
||||
unsigned int val = adler32(0, data, BLK_SIZE);
|
||||
i = HASH(val, hshift);
|
||||
entry->ptr = data;
|
||||
entry->val = val;
|
||||
entry->next = hash[i];
|
||||
hash[i] = entry++;
|
||||
hash_count[i]++;
|
||||
data -= BLK_SIZE;
|
||||
}
|
||||
prev_val = ~0;
|
||||
for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW;
|
||||
data >= buffer;
|
||||
data -= RABIN_WINDOW) {
|
||||
unsigned int val = 0;
|
||||
for (i = 1; i <= RABIN_WINDOW; i++)
|
||||
val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
|
||||
if (val == prev_val) {
|
||||
/* keep the lowest of consecutive identical blocks */
|
||||
entry[-1].ptr = data + RABIN_WINDOW;
|
||||
} else {
|
||||
prev_val = val;
|
||||
i = val & hmask;
|
||||
entry->ptr = data + RABIN_WINDOW;
|
||||
entry->val = val;
|
||||
entry->next = hash[i];
|
||||
hash[i] = entry++;
|
||||
hash_count[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine a limit on the number of entries in the same hash
|
||||
@ -91,27 +209,18 @@ static struct index ** delta_index(const unsigned char *buf,
|
||||
* bucket that would bring us to O(m*n) computing costs (m and n
|
||||
* corresponding to reference and target buffer sizes).
|
||||
*
|
||||
* The more the target buffer is large, the more it is important to
|
||||
* have small entry lists for each hash buckets. With such a limit
|
||||
* the cost is bounded to something more like O(m+n).
|
||||
*/
|
||||
hlimit = (1 << 26) / trg_bufsize;
|
||||
if (hlimit < 4*BLK_SIZE)
|
||||
hlimit = 4*BLK_SIZE;
|
||||
|
||||
/*
|
||||
* Now make sure none of the hash buckets has more entries than
|
||||
* Make sure none of the hash buckets has more entries than
|
||||
* we're willing to test. Otherwise we cull the entry list
|
||||
* uniformly to still preserve a good repartition across
|
||||
* the reference buffer.
|
||||
*/
|
||||
for (i = 0; i < hsize; i++) {
|
||||
if (hash_count[i] < hlimit)
|
||||
if (hash_count[i] < HASH_LIMIT)
|
||||
continue;
|
||||
entry = hash[i];
|
||||
do {
|
||||
struct index *keep = entry;
|
||||
int skip = hash_count[i] / hlimit / 2;
|
||||
struct index_entry *keep = entry;
|
||||
int skip = hash_count[i] / HASH_LIMIT / 2;
|
||||
do {
|
||||
entry = entry->next;
|
||||
} while(--skip && entry);
|
||||
@ -120,32 +229,31 @@ static struct index ** delta_index(const unsigned char *buf,
|
||||
}
|
||||
free(hash_count);
|
||||
|
||||
return hash;
|
||||
return index;
|
||||
}
|
||||
|
||||
/* provide the size of the copy opcode given the block offset and size */
|
||||
#define COPYOP_SIZE(o, s) \
|
||||
(!!(o & 0xff) + !!(o & 0xff00) + !!(o & 0xff0000) + !!(o & 0xff000000) + \
|
||||
!!(s & 0xff) + !!(s & 0xff00) + 1)
|
||||
|
||||
/* the maximum size for any opcode */
|
||||
#define MAX_OP_SIZE COPYOP_SIZE(0xffffffff, 0xffffffff)
|
||||
|
||||
void *diff_delta(void *from_buf, unsigned long from_size,
|
||||
void *to_buf, unsigned long to_size,
|
||||
unsigned long *delta_size,
|
||||
unsigned long max_size)
|
||||
void free_delta_index(struct delta_index *index)
|
||||
{
|
||||
unsigned int i, outpos, outsize, hash_shift;
|
||||
free(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* The maximum size for any opcode sequence, including the initial header
|
||||
* plus rabin window plus biggest copy.
|
||||
*/
|
||||
#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
|
||||
|
||||
void *
|
||||
create_delta(const struct delta_index *index,
|
||||
const void *trg_buf, unsigned long trg_size,
|
||||
unsigned long *delta_size, unsigned long max_size)
|
||||
{
|
||||
unsigned int i, outpos, outsize, val;
|
||||
int inscnt;
|
||||
const unsigned char *ref_data, *ref_top, *data, *top;
|
||||
unsigned char *out;
|
||||
struct index *entry, **hash;
|
||||
|
||||
if (!from_size || !to_size)
|
||||
return NULL;
|
||||
hash = delta_index(from_buf, from_size, to_size, &hash_shift);
|
||||
if (!hash)
|
||||
if (!trg_buf || !trg_size)
|
||||
return NULL;
|
||||
|
||||
outpos = 0;
|
||||
@ -153,64 +261,66 @@ void *diff_delta(void *from_buf, unsigned long from_size,
|
||||
if (max_size && outsize >= max_size)
|
||||
outsize = max_size + MAX_OP_SIZE + 1;
|
||||
out = malloc(outsize);
|
||||
if (!out) {
|
||||
free(hash);
|
||||
if (!out)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref_data = from_buf;
|
||||
ref_top = from_buf + from_size;
|
||||
data = to_buf;
|
||||
top = to_buf + to_size;
|
||||
|
||||
/* store reference buffer size */
|
||||
out[outpos++] = from_size;
|
||||
from_size >>= 7;
|
||||
while (from_size) {
|
||||
out[outpos - 1] |= 0x80;
|
||||
out[outpos++] = from_size;
|
||||
from_size >>= 7;
|
||||
i = index->src_size;
|
||||
while (i >= 0x80) {
|
||||
out[outpos++] = i | 0x80;
|
||||
i >>= 7;
|
||||
}
|
||||
out[outpos++] = i;
|
||||
|
||||
/* store target buffer size */
|
||||
out[outpos++] = to_size;
|
||||
to_size >>= 7;
|
||||
while (to_size) {
|
||||
out[outpos - 1] |= 0x80;
|
||||
out[outpos++] = to_size;
|
||||
to_size >>= 7;
|
||||
i = trg_size;
|
||||
while (i >= 0x80) {
|
||||
out[outpos++] = i | 0x80;
|
||||
i >>= 7;
|
||||
}
|
||||
out[outpos++] = i;
|
||||
|
||||
inscnt = 0;
|
||||
ref_data = index->src_buf;
|
||||
ref_top = ref_data + index->src_size;
|
||||
data = trg_buf;
|
||||
top = trg_buf + trg_size;
|
||||
|
||||
outpos++;
|
||||
val = 0;
|
||||
for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
|
||||
out[outpos++] = *data;
|
||||
val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
|
||||
}
|
||||
inscnt = i;
|
||||
|
||||
while (data < top) {
|
||||
unsigned int moff = 0, msize = 0;
|
||||
if (data + BLK_SIZE <= top) {
|
||||
unsigned int val = adler32(0, data, BLK_SIZE);
|
||||
i = HASH(val, hash_shift);
|
||||
for (entry = hash[i]; entry; entry = entry->next) {
|
||||
const unsigned char *ref = entry->ptr;
|
||||
const unsigned char *src = data;
|
||||
unsigned int ref_size = ref_top - ref;
|
||||
if (entry->val != val)
|
||||
continue;
|
||||
if (ref_size > top - src)
|
||||
ref_size = top - src;
|
||||
if (ref_size > 0x10000)
|
||||
ref_size = 0x10000;
|
||||
if (ref_size <= msize)
|
||||
break;
|
||||
while (ref_size-- && *src++ == *ref)
|
||||
ref++;
|
||||
if (msize < ref - entry->ptr) {
|
||||
/* this is our best match so far */
|
||||
msize = ref - entry->ptr;
|
||||
moff = entry->ptr - ref_data;
|
||||
}
|
||||
struct index_entry *entry;
|
||||
val ^= U[data[-RABIN_WINDOW]];
|
||||
val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
|
||||
i = val & index->hash_mask;
|
||||
for (entry = index->hash[i]; entry; entry = entry->next) {
|
||||
const unsigned char *ref = entry->ptr;
|
||||
const unsigned char *src = data;
|
||||
unsigned int ref_size = ref_top - ref;
|
||||
if (entry->val != val)
|
||||
continue;
|
||||
if (ref_size > top - src)
|
||||
ref_size = top - src;
|
||||
if (ref_size > 0x10000)
|
||||
ref_size = 0x10000;
|
||||
if (ref_size <= msize)
|
||||
break;
|
||||
while (ref_size-- && *src++ == *ref)
|
||||
ref++;
|
||||
if (msize < ref - entry->ptr) {
|
||||
/* this is our best match so far */
|
||||
msize = ref - entry->ptr;
|
||||
moff = entry->ptr - ref_data;
|
||||
}
|
||||
}
|
||||
|
||||
if (!msize || msize < COPYOP_SIZE(moff, msize)) {
|
||||
if (msize < 4) {
|
||||
if (!inscnt)
|
||||
outpos++;
|
||||
out[outpos++] = *data++;
|
||||
@ -222,6 +332,20 @@ void *diff_delta(void *from_buf, unsigned long from_size,
|
||||
} else {
|
||||
unsigned char *op;
|
||||
|
||||
if (msize >= RABIN_WINDOW) {
|
||||
const unsigned char *sk;
|
||||
sk = data + msize - RABIN_WINDOW;
|
||||
val = 0;
|
||||
for (i = 0; i < RABIN_WINDOW; i++)
|
||||
val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
|
||||
} else {
|
||||
const unsigned char *sk = data + 1;
|
||||
for (i = 1; i < msize; i++) {
|
||||
val ^= U[sk[-RABIN_WINDOW]];
|
||||
val = ((val << 8) | *sk++) ^ T[val >> RABIN_SHIFT];
|
||||
}
|
||||
}
|
||||
|
||||
if (inscnt) {
|
||||
while (moff && ref_data[moff-1] == data[-1]) {
|
||||
if (msize == 0x10000)
|
||||
@ -266,12 +390,10 @@ void *diff_delta(void *from_buf, unsigned long from_size,
|
||||
if (max_size && outsize >= max_size)
|
||||
outsize = max_size + MAX_OP_SIZE + 1;
|
||||
if (max_size && outpos > max_size)
|
||||
out = NULL;
|
||||
else
|
||||
out = realloc(out, outsize);
|
||||
break;
|
||||
out = realloc(out, outsize);
|
||||
if (!out) {
|
||||
free(tmp);
|
||||
free(hash);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@ -280,7 +402,11 @@ void *diff_delta(void *from_buf, unsigned long from_size,
|
||||
if (inscnt)
|
||||
out[outpos - inscnt - 1] = inscnt;
|
||||
|
||||
free(hash);
|
||||
if (max_size && outpos > max_size) {
|
||||
free(out);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*delta_size = outpos;
|
||||
return out;
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ COMMON_DIFF_OPTIONS_HELP;
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct rev_info rev;
|
||||
int match_missing = 0;
|
||||
int cached = 0;
|
||||
int i;
|
||||
|
||||
|
@ -138,7 +138,10 @@ int main(int argc, const char **argv)
|
||||
opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
|
||||
DIFF_SETUP_USE_CACHE);
|
||||
while (fgets(line, sizeof(line), stdin))
|
||||
diff_tree_stdin(line);
|
||||
if (line[0] == '\n')
|
||||
fflush(stdout);
|
||||
else
|
||||
diff_tree_stdin(line);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
289
diff.c
289
diff.c
@ -8,6 +8,7 @@
|
||||
#include "quote.h"
|
||||
#include "diff.h"
|
||||
#include "diffcore.h"
|
||||
#include "delta.h"
|
||||
#include "xdiff-interface.h"
|
||||
|
||||
static int use_size_cache;
|
||||
@ -231,11 +232,16 @@ static char *pprint_rename(const char *a, const char *b)
|
||||
* name-a => name-b
|
||||
*/
|
||||
if (pfx_length + sfx_length) {
|
||||
int a_midlen = len_a - pfx_length - sfx_length;
|
||||
int b_midlen = len_b - pfx_length - sfx_length;
|
||||
if (a_midlen < 0) a_midlen = 0;
|
||||
if (b_midlen < 0) b_midlen = 0;
|
||||
|
||||
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_midlen, a + pfx_length,
|
||||
b_midlen, b + pfx_length,
|
||||
a + len_a - sfx_length);
|
||||
}
|
||||
else {
|
||||
@ -296,7 +302,6 @@ 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;
|
||||
@ -318,6 +323,7 @@ static void show_stats(struct diffstat_t* data)
|
||||
}
|
||||
|
||||
for (i = 0; i < data->nr; i++) {
|
||||
char *prefix = "";
|
||||
char *name = data->files[i]->name;
|
||||
int added = data->files[i]->added;
|
||||
int deleted = data->files[i]->deleted;
|
||||
@ -391,6 +397,90 @@ static void show_stats(struct diffstat_t* data)
|
||||
total_files, adds, dels);
|
||||
}
|
||||
|
||||
static unsigned char *deflate_it(char *data,
|
||||
unsigned long size,
|
||||
unsigned long *result_size)
|
||||
{
|
||||
int bound;
|
||||
unsigned char *deflated;
|
||||
z_stream stream;
|
||||
|
||||
memset(&stream, 0, sizeof(stream));
|
||||
deflateInit(&stream, Z_BEST_COMPRESSION);
|
||||
bound = deflateBound(&stream, size);
|
||||
deflated = xmalloc(bound);
|
||||
stream.next_out = deflated;
|
||||
stream.avail_out = bound;
|
||||
|
||||
stream.next_in = (unsigned char *)data;
|
||||
stream.avail_in = size;
|
||||
while (deflate(&stream, Z_FINISH) == Z_OK)
|
||||
; /* nothing */
|
||||
deflateEnd(&stream);
|
||||
*result_size = stream.total_out;
|
||||
return deflated;
|
||||
}
|
||||
|
||||
static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
|
||||
{
|
||||
void *cp;
|
||||
void *delta;
|
||||
void *deflated;
|
||||
void *data;
|
||||
unsigned long orig_size;
|
||||
unsigned long delta_size;
|
||||
unsigned long deflate_size;
|
||||
unsigned long data_size;
|
||||
|
||||
printf("GIT binary patch\n");
|
||||
/* We could do deflated delta, or we could do just deflated two,
|
||||
* whichever is smaller.
|
||||
*/
|
||||
delta = NULL;
|
||||
deflated = deflate_it(two->ptr, two->size, &deflate_size);
|
||||
if (one->size && two->size) {
|
||||
delta = diff_delta(one->ptr, one->size,
|
||||
two->ptr, two->size,
|
||||
&delta_size, deflate_size);
|
||||
if (delta) {
|
||||
void *to_free = delta;
|
||||
orig_size = delta_size;
|
||||
delta = deflate_it(delta, delta_size, &delta_size);
|
||||
free(to_free);
|
||||
}
|
||||
}
|
||||
|
||||
if (delta && delta_size < deflate_size) {
|
||||
printf("delta %lu\n", orig_size);
|
||||
free(deflated);
|
||||
data = delta;
|
||||
data_size = delta_size;
|
||||
}
|
||||
else {
|
||||
printf("literal %lu\n", two->size);
|
||||
free(delta);
|
||||
data = deflated;
|
||||
data_size = deflate_size;
|
||||
}
|
||||
|
||||
/* emit data encoded in base85 */
|
||||
cp = data;
|
||||
while (data_size) {
|
||||
int bytes = (52 < data_size) ? 52 : data_size;
|
||||
char line[70];
|
||||
data_size -= bytes;
|
||||
if (bytes <= 26)
|
||||
line[0] = bytes + 'A' - 1;
|
||||
else
|
||||
line[0] = bytes - 26 + 'a' - 1;
|
||||
encode_85(line + 1, cp, bytes);
|
||||
cp += bytes;
|
||||
puts(line);
|
||||
}
|
||||
printf("\n");
|
||||
free(data);
|
||||
}
|
||||
|
||||
#define FIRST_FEW_BYTES 8000
|
||||
static int mmfile_is_binary(mmfile_t *mf)
|
||||
{
|
||||
@ -407,6 +497,7 @@ static void builtin_diff(const char *name_a,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two,
|
||||
const char *xfrm_msg,
|
||||
struct diff_options *o,
|
||||
int complete_rewrite)
|
||||
{
|
||||
mmfile_t mf1, mf2;
|
||||
@ -451,8 +542,17 @@ static void builtin_diff(const char *name_a,
|
||||
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]);
|
||||
if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
|
||||
/* Quite common confusing case */
|
||||
if (mf1.size == mf2.size &&
|
||||
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
|
||||
goto free_ab_and_return;
|
||||
if (o->binary)
|
||||
emit_binary_diff(&mf1, &mf2);
|
||||
else
|
||||
printf("Binary files %s and %s differ\n",
|
||||
lbl[0], lbl[1]);
|
||||
}
|
||||
else {
|
||||
/* Crazy xdl interfaces.. */
|
||||
const char *diffopts = getenv("GIT_DIFF_OPTS");
|
||||
@ -463,7 +563,7 @@ static void builtin_diff(const char *name_a,
|
||||
|
||||
ecbdata.label_path = lbl;
|
||||
xpp.flags = XDF_NEED_MINIMAL;
|
||||
xecfg.ctxlen = 3;
|
||||
xecfg.ctxlen = o->context;
|
||||
xecfg.flags = XDL_EMIT_FUNCNAMES;
|
||||
if (!diffopts)
|
||||
;
|
||||
@ -485,7 +585,8 @@ static void builtin_diff(const char *name_a,
|
||||
static void builtin_diffstat(const char *name_a, const char *name_b,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two,
|
||||
struct diffstat_t *diffstat)
|
||||
struct diffstat_t *diffstat,
|
||||
int complete_rewrite)
|
||||
{
|
||||
mmfile_t mf1, mf2;
|
||||
struct diffstat_file *data;
|
||||
@ -496,7 +597,13 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
|
||||
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");
|
||||
|
||||
@ -921,6 +1028,7 @@ static void run_diff_cmd(const char *pgm,
|
||||
struct diff_filespec *one,
|
||||
struct diff_filespec *two,
|
||||
const char *xfrm_msg,
|
||||
struct diff_options *o,
|
||||
int complete_rewrite)
|
||||
{
|
||||
if (pgm) {
|
||||
@ -930,7 +1038,7 @@ static void run_diff_cmd(const char *pgm,
|
||||
}
|
||||
if (one && two)
|
||||
builtin_diff(name, other ? other : name,
|
||||
one, two, xfrm_msg, complete_rewrite);
|
||||
one, two, xfrm_msg, o, complete_rewrite);
|
||||
else
|
||||
printf("* Unmerged path %s\n", name);
|
||||
}
|
||||
@ -964,7 +1072,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
|
||||
if (DIFF_PAIR_UNMERGED(p)) {
|
||||
/* unmerged */
|
||||
run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
|
||||
run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1011,14 +1119,12 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
}
|
||||
|
||||
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));
|
||||
abbrev, sha1_to_hex(one->sha1),
|
||||
abbrev, sha1_to_hex(two->sha1));
|
||||
if (one->mode == two->mode)
|
||||
len += snprintf(msg + len, sizeof(msg) - len,
|
||||
" %06o", one->mode);
|
||||
@ -1036,14 +1142,14 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
|
||||
* 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);
|
||||
run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
|
||||
free(null);
|
||||
null = alloc_filespec(one->path);
|
||||
run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
|
||||
run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
|
||||
free(null);
|
||||
}
|
||||
else
|
||||
run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
|
||||
run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
|
||||
complete_rewrite);
|
||||
|
||||
free(name_munged);
|
||||
@ -1055,10 +1161,11 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
|
||||
{
|
||||
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);
|
||||
builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1068,7 +1175,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
|
||||
diff_fill_sha1_info(p->one);
|
||||
diff_fill_sha1_info(p->two);
|
||||
|
||||
builtin_diffstat(name, other, p->one, p->two, diffstat);
|
||||
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)
|
||||
@ -1078,6 +1187,7 @@ void diff_setup(struct diff_options *options)
|
||||
options->line_termination = '\n';
|
||||
options->break_opt = -1;
|
||||
options->rename_limit = -1;
|
||||
options->context = 3;
|
||||
|
||||
options->change = diff_change;
|
||||
options->add_remove = diff_addremove;
|
||||
@ -1118,17 +1228,68 @@ int diff_setup_done(struct diff_options *options)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
|
||||
{
|
||||
char c, *eq;
|
||||
int len;
|
||||
|
||||
if (*arg != '-')
|
||||
return 0;
|
||||
c = *++arg;
|
||||
if (!c)
|
||||
return 0;
|
||||
if (c == arg_short) {
|
||||
c = *++arg;
|
||||
if (!c)
|
||||
return 1;
|
||||
if (val && isdigit(c)) {
|
||||
char *end;
|
||||
int n = strtoul(arg, &end, 10);
|
||||
if (*end)
|
||||
return 0;
|
||||
*val = n;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (c != '-')
|
||||
return 0;
|
||||
arg++;
|
||||
eq = strchr(arg, '=');
|
||||
if (eq)
|
||||
len = eq - arg;
|
||||
else
|
||||
len = strlen(arg);
|
||||
if (!len || strncmp(arg, arg_long, len))
|
||||
return 0;
|
||||
if (eq) {
|
||||
int n;
|
||||
char *end;
|
||||
if (!isdigit(*++eq))
|
||||
return 0;
|
||||
n = strtoul(eq, &end, 10);
|
||||
if (*end)
|
||||
return 0;
|
||||
*val = n;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
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 (opt_arg(arg, 'U', "unified", &options->context))
|
||||
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, "--summary"))
|
||||
options->summary = 1;
|
||||
else if (!strcmp(arg, "--patch-with-stat")) {
|
||||
options->output_format = DIFF_FORMAT_PATCH;
|
||||
options->with_stat = 1;
|
||||
@ -1139,6 +1300,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
|
||||
options->rename_limit = strtoul(arg+2, NULL, 10);
|
||||
else if (!strcmp(arg, "--full-index"))
|
||||
options->full_index = 1;
|
||||
else if (!strcmp(arg, "--binary")) {
|
||||
options->output_format = DIFF_FORMAT_PATCH;
|
||||
options->full_index = options->binary = 1;
|
||||
}
|
||||
else if (!strcmp(arg, "--name-only"))
|
||||
options->output_format = DIFF_FORMAT_NAME;
|
||||
else if (!strcmp(arg, "--name-status"))
|
||||
@ -1595,6 +1760,85 @@ static void flush_one_pair(struct diff_filepair *p,
|
||||
}
|
||||
}
|
||||
|
||||
static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
|
||||
{
|
||||
if (fs->mode)
|
||||
printf(" %s mode %06o %s\n", newdelete, fs->mode, fs->path);
|
||||
else
|
||||
printf(" %s %s\n", newdelete, fs->path);
|
||||
}
|
||||
|
||||
|
||||
static void show_mode_change(struct diff_filepair *p, int show_name)
|
||||
{
|
||||
if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) {
|
||||
if (show_name)
|
||||
printf(" mode change %06o => %06o %s\n",
|
||||
p->one->mode, p->two->mode, p->two->path);
|
||||
else
|
||||
printf(" mode change %06o => %06o\n",
|
||||
p->one->mode, p->two->mode);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_rename_copy(const char *renamecopy, struct diff_filepair *p)
|
||||
{
|
||||
const char *old, *new;
|
||||
|
||||
/* Find common prefix */
|
||||
old = p->one->path;
|
||||
new = p->two->path;
|
||||
while (1) {
|
||||
const char *slash_old, *slash_new;
|
||||
slash_old = strchr(old, '/');
|
||||
slash_new = strchr(new, '/');
|
||||
if (!slash_old ||
|
||||
!slash_new ||
|
||||
slash_old - old != slash_new - new ||
|
||||
memcmp(old, new, slash_new - new))
|
||||
break;
|
||||
old = slash_old + 1;
|
||||
new = slash_new + 1;
|
||||
}
|
||||
/* p->one->path thru old is the common prefix, and old and new
|
||||
* through the end of names are renames
|
||||
*/
|
||||
if (old != p->one->path)
|
||||
printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
|
||||
(int)(old - p->one->path), p->one->path,
|
||||
old, new, (int)(0.5 + p->score * 100.0/MAX_SCORE));
|
||||
else
|
||||
printf(" %s %s => %s (%d%%)\n", renamecopy,
|
||||
p->one->path, p->two->path,
|
||||
(int)(0.5 + p->score * 100.0/MAX_SCORE));
|
||||
show_mode_change(p, 0);
|
||||
}
|
||||
|
||||
static void diff_summary(struct diff_filepair *p)
|
||||
{
|
||||
switch(p->status) {
|
||||
case DIFF_STATUS_DELETED:
|
||||
show_file_mode_name("delete", p->one);
|
||||
break;
|
||||
case DIFF_STATUS_ADDED:
|
||||
show_file_mode_name("create", p->two);
|
||||
break;
|
||||
case DIFF_STATUS_COPIED:
|
||||
show_rename_copy("copy", p);
|
||||
break;
|
||||
case DIFF_STATUS_RENAMED:
|
||||
show_rename_copy("rename", p);
|
||||
break;
|
||||
default:
|
||||
if (p->score) {
|
||||
printf(" rewrite %s (%d%%)\n", p->two->path,
|
||||
(int)(0.5 + p->score * 100.0/MAX_SCORE));
|
||||
show_mode_change(p, 0);
|
||||
} else show_mode_change(p, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void diff_flush(struct diff_options *options)
|
||||
{
|
||||
struct diff_queue_struct *q = &diff_queued_diff;
|
||||
@ -1628,7 +1872,6 @@ void diff_flush(struct diff_options *options)
|
||||
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) {
|
||||
@ -1636,6 +1879,12 @@ void diff_flush(struct diff_options *options)
|
||||
free(diffstat);
|
||||
}
|
||||
|
||||
for (i = 0; i < q->nr; i++) {
|
||||
if (options->summary)
|
||||
diff_summary(q->queue[i]);
|
||||
diff_free_filepair(q->queue[i]);
|
||||
}
|
||||
|
||||
free(q->queue);
|
||||
q->queue = NULL;
|
||||
q->nr = q->alloc = 0;
|
||||
|
7
diff.h
7
diff.h
@ -28,9 +28,12 @@ struct diff_options {
|
||||
with_raw:1,
|
||||
with_stat:1,
|
||||
tree_in_recursive:1,
|
||||
binary:1,
|
||||
full_index:1,
|
||||
silent_on_remove:1,
|
||||
find_copies_harder:1;
|
||||
find_copies_harder:1,
|
||||
summary:1;
|
||||
int context;
|
||||
int break_opt;
|
||||
int detect_rename;
|
||||
int line_termination;
|
||||
@ -75,6 +78,8 @@ struct combine_diff_path {
|
||||
extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
|
||||
int dense, struct rev_info *);
|
||||
|
||||
extern void diff_tree_combined(const unsigned char *sha1, const unsigned char parent[][20], int num_parent, int dense, struct rev_info *rev);
|
||||
|
||||
extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
|
||||
|
||||
extern void diff_addremove(struct diff_options *,
|
||||
|
401
dir.c
Normal file
401
dir.c
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* This handles recursive filename detection with exclude
|
||||
* files, index knowledge etc..
|
||||
*
|
||||
* Copyright (C) Linus Torvalds, 2005-2006
|
||||
* Junio Hamano, 2005-2006
|
||||
*/
|
||||
#include <dirent.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "cache.h"
|
||||
#include "dir.h"
|
||||
|
||||
int common_prefix(const char **pathspec)
|
||||
{
|
||||
const char *path, *slash, *next;
|
||||
int prefix;
|
||||
|
||||
if (!pathspec)
|
||||
return 0;
|
||||
|
||||
path = *pathspec;
|
||||
slash = strrchr(path, '/');
|
||||
if (!slash)
|
||||
return 0;
|
||||
|
||||
prefix = slash - path + 1;
|
||||
while ((next = *++pathspec) != NULL) {
|
||||
int len = strlen(next);
|
||||
if (len >= prefix && !memcmp(path, next, len))
|
||||
continue;
|
||||
for (;;) {
|
||||
if (!len)
|
||||
return 0;
|
||||
if (next[--len] != '/')
|
||||
continue;
|
||||
if (memcmp(path, next, len+1))
|
||||
continue;
|
||||
prefix = len + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
static int match_one(const char *match, const char *name, int namelen)
|
||||
{
|
||||
int matchlen;
|
||||
|
||||
/* If the match was just the prefix, we matched */
|
||||
matchlen = strlen(match);
|
||||
if (!matchlen)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If we don't match the matchstring exactly,
|
||||
* we need to match by fnmatch
|
||||
*/
|
||||
if (strncmp(match, name, matchlen))
|
||||
return !fnmatch(match, name, 0);
|
||||
|
||||
/*
|
||||
* If we did match the string exactly, we still
|
||||
* need to make sure that it happened on a path
|
||||
* component boundary (ie either the last character
|
||||
* of the match was '/', or the next character of
|
||||
* the name was '/' or the terminating NUL.
|
||||
*/
|
||||
return match[matchlen-1] == '/' ||
|
||||
name[matchlen] == '/' ||
|
||||
!name[matchlen];
|
||||
}
|
||||
|
||||
int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
|
||||
{
|
||||
int retval;
|
||||
const char *match;
|
||||
|
||||
name += prefix;
|
||||
namelen -= prefix;
|
||||
|
||||
for (retval = 0; (match = *pathspec++) != NULL; seen++) {
|
||||
if (retval & *seen)
|
||||
continue;
|
||||
match += prefix;
|
||||
if (match_one(match, name, namelen)) {
|
||||
retval = 1;
|
||||
*seen = 1;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *which)
|
||||
{
|
||||
struct exclude *x = xmalloc(sizeof (*x));
|
||||
|
||||
x->pattern = string;
|
||||
x->base = base;
|
||||
x->baselen = baselen;
|
||||
if (which->nr == which->alloc) {
|
||||
which->alloc = alloc_nr(which->alloc);
|
||||
which->excludes = realloc(which->excludes,
|
||||
which->alloc * sizeof(x));
|
||||
}
|
||||
which->excludes[which->nr++] = x;
|
||||
}
|
||||
|
||||
static int add_excludes_from_file_1(const char *fname,
|
||||
const char *base,
|
||||
int baselen,
|
||||
struct exclude_list *which)
|
||||
{
|
||||
int fd, i;
|
||||
long size;
|
||||
char *buf, *entry;
|
||||
|
||||
fd = open(fname, O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto err;
|
||||
size = lseek(fd, 0, SEEK_END);
|
||||
if (size < 0)
|
||||
goto err;
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
if (size == 0) {
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
buf = xmalloc(size+1);
|
||||
if (read(fd, buf, size) != size)
|
||||
goto err;
|
||||
close(fd);
|
||||
|
||||
buf[size++] = '\n';
|
||||
entry = buf;
|
||||
for (i = 0; i < size; i++) {
|
||||
if (buf[i] == '\n') {
|
||||
if (entry != buf + i && entry[0] != '#') {
|
||||
buf[i - (i && buf[i-1] == '\r')] = 0;
|
||||
add_exclude(entry, base, baselen, which);
|
||||
}
|
||||
entry = buf + i + 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (0 <= fd)
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void add_excludes_from_file(struct dir_struct *dir, const char *fname)
|
||||
{
|
||||
if (add_excludes_from_file_1(fname, "", 0,
|
||||
&dir->exclude_list[EXC_FILE]) < 0)
|
||||
die("cannot use %s as an exclude file", fname);
|
||||
}
|
||||
|
||||
static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
|
||||
{
|
||||
char exclude_file[PATH_MAX];
|
||||
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
|
||||
int current_nr = el->nr;
|
||||
|
||||
if (dir->exclude_per_dir) {
|
||||
memcpy(exclude_file, base, baselen);
|
||||
strcpy(exclude_file + baselen, dir->exclude_per_dir);
|
||||
add_excludes_from_file_1(exclude_file, base, baselen, el);
|
||||
}
|
||||
return current_nr;
|
||||
}
|
||||
|
||||
static void pop_exclude_per_directory(struct dir_struct *dir, int stk)
|
||||
{
|
||||
struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
|
||||
|
||||
while (stk < el->nr)
|
||||
free(el->excludes[--el->nr]);
|
||||
}
|
||||
|
||||
/* Scan the list and let the last match determines the fate.
|
||||
* Return 1 for exclude, 0 for include and -1 for undecided.
|
||||
*/
|
||||
static int excluded_1(const char *pathname,
|
||||
int pathlen,
|
||||
struct exclude_list *el)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (el->nr) {
|
||||
for (i = el->nr - 1; 0 <= i; i--) {
|
||||
struct exclude *x = el->excludes[i];
|
||||
const char *exclude = x->pattern;
|
||||
int to_exclude = 1;
|
||||
|
||||
if (*exclude == '!') {
|
||||
to_exclude = 0;
|
||||
exclude++;
|
||||
}
|
||||
|
||||
if (!strchr(exclude, '/')) {
|
||||
/* match basename */
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
basename = (basename) ? basename+1 : pathname;
|
||||
if (fnmatch(exclude, basename, 0) == 0)
|
||||
return to_exclude;
|
||||
}
|
||||
else {
|
||||
/* match with FNM_PATHNAME:
|
||||
* exclude has base (baselen long) implicitly
|
||||
* in front of it.
|
||||
*/
|
||||
int baselen = x->baselen;
|
||||
if (*exclude == '/')
|
||||
exclude++;
|
||||
|
||||
if (pathlen < baselen ||
|
||||
(baselen && pathname[baselen-1] != '/') ||
|
||||
strncmp(pathname, x->base, baselen))
|
||||
continue;
|
||||
|
||||
if (fnmatch(exclude, pathname+baselen,
|
||||
FNM_PATHNAME) == 0)
|
||||
return to_exclude;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1; /* undecided */
|
||||
}
|
||||
|
||||
int excluded(struct dir_struct *dir, const char *pathname)
|
||||
{
|
||||
int pathlen = strlen(pathname);
|
||||
int st;
|
||||
|
||||
for (st = EXC_CMDL; st <= EXC_FILE; st++) {
|
||||
switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_name(struct dir_struct *dir, const char *pathname, int len)
|
||||
{
|
||||
struct dir_entry *ent;
|
||||
|
||||
if (cache_name_pos(pathname, len) >= 0)
|
||||
return;
|
||||
|
||||
if (dir->nr == dir->alloc) {
|
||||
int alloc = alloc_nr(dir->alloc);
|
||||
dir->alloc = alloc;
|
||||
dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
|
||||
}
|
||||
ent = xmalloc(sizeof(*ent) + len + 1);
|
||||
ent->len = len;
|
||||
memcpy(ent->name, pathname, len);
|
||||
ent->name[len] = 0;
|
||||
dir->entries[dir->nr++] = ent;
|
||||
}
|
||||
|
||||
static int dir_exists(const char *dirname, int len)
|
||||
{
|
||||
int pos = cache_name_pos(dirname, len);
|
||||
if (pos >= 0)
|
||||
return 1;
|
||||
pos = -pos-1;
|
||||
if (pos >= active_nr) /* can't */
|
||||
return 0;
|
||||
return !strncmp(active_cache[pos]->name, dirname, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a directory tree. We currently ignore anything but
|
||||
* directories, regular files and symlinks. That's because git
|
||||
* doesn't handle them at all yet. Maybe that will change some
|
||||
* day.
|
||||
*
|
||||
* Also, we ignore the name ".git" (even if it is not a directory).
|
||||
* That likely will not change.
|
||||
*/
|
||||
static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen)
|
||||
{
|
||||
DIR *fdir = opendir(path);
|
||||
int contents = 0;
|
||||
|
||||
if (fdir) {
|
||||
int exclude_stk;
|
||||
struct dirent *de;
|
||||
char fullname[MAXPATHLEN + 1];
|
||||
memcpy(fullname, base, baselen);
|
||||
|
||||
exclude_stk = push_exclude_per_directory(dir, base, baselen);
|
||||
|
||||
while ((de = readdir(fdir)) != NULL) {
|
||||
int len;
|
||||
|
||||
if ((de->d_name[0] == '.') &&
|
||||
(de->d_name[1] == 0 ||
|
||||
!strcmp(de->d_name + 1, ".") ||
|
||||
!strcmp(de->d_name + 1, "git")))
|
||||
continue;
|
||||
len = strlen(de->d_name);
|
||||
memcpy(fullname + baselen, de->d_name, len+1);
|
||||
if (excluded(dir, fullname) != dir->show_ignored) {
|
||||
if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch (DTYPE(de)) {
|
||||
struct stat st;
|
||||
int subdir, rewind_base;
|
||||
default:
|
||||
continue;
|
||||
case DT_UNKNOWN:
|
||||
if (lstat(fullname, &st))
|
||||
continue;
|
||||
if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
|
||||
break;
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
/* fallthrough */
|
||||
case DT_DIR:
|
||||
memcpy(fullname + baselen + len, "/", 2);
|
||||
len++;
|
||||
rewind_base = dir->nr;
|
||||
subdir = read_directory_recursive(dir, fullname, fullname,
|
||||
baselen + len);
|
||||
if (dir->show_other_directories &&
|
||||
(subdir || !dir->hide_empty_directories) &&
|
||||
!dir_exists(fullname, baselen + len)) {
|
||||
// Rewind the read subdirectory
|
||||
while (dir->nr > rewind_base)
|
||||
free(dir->entries[--dir->nr]);
|
||||
break;
|
||||
}
|
||||
contents += subdir;
|
||||
continue;
|
||||
case DT_REG:
|
||||
case DT_LNK:
|
||||
break;
|
||||
}
|
||||
add_name(dir, fullname, baselen + len);
|
||||
contents++;
|
||||
}
|
||||
closedir(fdir);
|
||||
|
||||
pop_exclude_per_directory(dir, exclude_stk);
|
||||
}
|
||||
|
||||
return contents;
|
||||
}
|
||||
|
||||
static int cmp_name(const void *p1, const void *p2)
|
||||
{
|
||||
const struct dir_entry *e1 = *(const struct dir_entry **)p1;
|
||||
const struct dir_entry *e2 = *(const struct dir_entry **)p2;
|
||||
|
||||
return cache_name_compare(e1->name, e1->len,
|
||||
e2->name, e2->len);
|
||||
}
|
||||
|
||||
int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
|
||||
{
|
||||
/*
|
||||
* Make sure to do the per-directory exclude for all the
|
||||
* directories leading up to our base.
|
||||
*/
|
||||
if (baselen) {
|
||||
if (dir->exclude_per_dir) {
|
||||
char *p, *pp = xmalloc(baselen+1);
|
||||
memcpy(pp, base, baselen+1);
|
||||
p = pp;
|
||||
while (1) {
|
||||
char save = *p;
|
||||
*p = 0;
|
||||
push_exclude_per_directory(dir, pp, p-pp);
|
||||
*p++ = save;
|
||||
if (!save)
|
||||
break;
|
||||
p = strchr(p, '/');
|
||||
if (p)
|
||||
p++;
|
||||
else
|
||||
p = pp + baselen;
|
||||
}
|
||||
free(pp);
|
||||
}
|
||||
}
|
||||
|
||||
read_directory_recursive(dir, path, base, baselen);
|
||||
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
|
||||
return dir->nr;
|
||||
}
|
51
dir.h
Normal file
51
dir.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef DIR_H
|
||||
#define DIR_H
|
||||
|
||||
/*
|
||||
* We maintain three exclude pattern lists:
|
||||
* EXC_CMDL lists patterns explicitly given on the command line.
|
||||
* EXC_DIRS lists patterns obtained from per-directory ignore files.
|
||||
* EXC_FILE lists patterns from fallback ignore files.
|
||||
*/
|
||||
#define EXC_CMDL 0
|
||||
#define EXC_DIRS 1
|
||||
#define EXC_FILE 2
|
||||
|
||||
|
||||
struct dir_entry {
|
||||
int len;
|
||||
char name[FLEX_ARRAY]; /* more */
|
||||
};
|
||||
|
||||
struct exclude_list {
|
||||
int nr;
|
||||
int alloc;
|
||||
struct exclude {
|
||||
const char *pattern;
|
||||
const char *base;
|
||||
int baselen;
|
||||
} **excludes;
|
||||
};
|
||||
|
||||
struct dir_struct {
|
||||
int nr, alloc;
|
||||
unsigned int show_ignored:1,
|
||||
show_other_directories:1,
|
||||
hide_empty_directories:1;
|
||||
struct dir_entry **entries;
|
||||
|
||||
/* Exclude info */
|
||||
const char *exclude_per_dir;
|
||||
struct exclude_list exclude_list[3];
|
||||
};
|
||||
|
||||
extern int common_prefix(const char **pathspec);
|
||||
extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
|
||||
|
||||
extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
|
||||
extern int excluded(struct dir_struct *, const char *);
|
||||
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
|
||||
extern void add_exclude(const char *string, const char *base,
|
||||
int baselen, struct exclude_list *which);
|
||||
|
||||
#endif
|
@ -13,7 +13,7 @@ char git_default_email[MAX_GITNAME];
|
||||
char git_default_name[MAX_GITNAME];
|
||||
int trust_executable_bit = 1;
|
||||
int assume_unchanged = 0;
|
||||
int only_use_symrefs = 0;
|
||||
int prefer_symlink_refs = 0;
|
||||
int warn_ambiguous_refs = 1;
|
||||
int repository_format_version = 0;
|
||||
char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
|
||||
|
56
git-add.sh
56
git-add.sh
@ -1,56 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
USAGE='[-n] [-v] <file>...'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
show_only=
|
||||
verbose=
|
||||
while : ; do
|
||||
case "$1" in
|
||||
-n)
|
||||
show_only=true
|
||||
;;
|
||||
-v)
|
||||
verbose=--verbose
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Check misspelled pathspec
|
||||
case "$#" in
|
||||
0) ;;
|
||||
*)
|
||||
git-ls-files --error-unmatch --others --cached -- "$@" >/dev/null || {
|
||||
echo >&2 "Maybe you misspelled it?"
|
||||
exit 1
|
||||
}
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -f "$GIT_DIR/info/exclude"
|
||||
then
|
||||
git-ls-files -z \
|
||||
--exclude-from="$GIT_DIR/info/exclude" \
|
||||
--others --exclude-per-directory=.gitignore -- "$@"
|
||||
else
|
||||
git-ls-files -z \
|
||||
--others --exclude-per-directory=.gitignore -- "$@"
|
||||
fi |
|
||||
case "$show_only" in
|
||||
true)
|
||||
xargs -0 echo ;;
|
||||
*)
|
||||
git-update-index --add $verbose -z --stdin ;;
|
||||
esac
|
80
git-am.sh
80
git-am.sh
@ -14,6 +14,30 @@ stop_here () {
|
||||
exit 1
|
||||
}
|
||||
|
||||
stop_here_user_resolve () {
|
||||
if [ -n "$resolvemsg" ]; then
|
||||
echo "$resolvemsg"
|
||||
stop_here $1
|
||||
fi
|
||||
cmdline=$(basename $0)
|
||||
if test '' != "$interactive"
|
||||
then
|
||||
cmdline="$cmdline -i"
|
||||
fi
|
||||
if test '' != "$threeway"
|
||||
then
|
||||
cmdline="$cmdline -3"
|
||||
fi
|
||||
if test '.dotest' != "$dotest"
|
||||
then
|
||||
cmdline="$cmdline -d=$dotest"
|
||||
fi
|
||||
echo "When you have resolved this problem run \"$cmdline --resolved\"."
|
||||
echo "If you would prefer to skip this patch, instead run \"$cmdline --skip\"."
|
||||
|
||||
stop_here $1
|
||||
}
|
||||
|
||||
go_next () {
|
||||
rm -f "$dotest/$msgnum" "$dotest/msg" "$dotest/msg-clean" \
|
||||
"$dotest/patch" "$dotest/info"
|
||||
@ -35,46 +59,12 @@ fall_back_3way () {
|
||||
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git-write-tree >"$dotest/patch-merge-base+" &&
|
||||
# index has the base tree now.
|
||||
(
|
||||
cd "$dotest/patch-merge-tmp-dir" &&
|
||||
GIT_INDEX_FILE="../patch-merge-tmp-index" \
|
||||
GIT_OBJECT_DIRECTORY="$O_OBJECT" \
|
||||
git-apply $binary --index <../patch
|
||||
)
|
||||
GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
|
||||
git-apply $binary --cached <"$dotest/patch"
|
||||
then
|
||||
echo Using index info to reconstruct a base tree...
|
||||
mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
|
||||
mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
|
||||
else
|
||||
# Otherwise, try nearby trees that can be used to apply the
|
||||
# patch.
|
||||
(
|
||||
N=10
|
||||
|
||||
# Hoping the patch is against our recent commits...
|
||||
git-rev-list --max-count=$N HEAD
|
||||
|
||||
# or hoping the patch is against known tags...
|
||||
git-ls-remote --tags .
|
||||
) |
|
||||
while read base junk
|
||||
do
|
||||
# See if we have it as a tree...
|
||||
git-cat-file tree "$base" >/dev/null 2>&1 || continue
|
||||
|
||||
rm -fr "$dotest"/patch-merge-* &&
|
||||
mkdir "$dotest/patch-merge-tmp-dir" || break
|
||||
(
|
||||
cd "$dotest/patch-merge-tmp-dir" &&
|
||||
GIT_INDEX_FILE=../patch-merge-tmp-index &&
|
||||
GIT_OBJECT_DIRECTORY="$O_OBJECT" &&
|
||||
export GIT_INDEX_FILE GIT_OBJECT_DIRECTORY &&
|
||||
git-read-tree "$base" &&
|
||||
git-apply $binary --index &&
|
||||
mv ../patch-merge-tmp-index ../patch-merge-index &&
|
||||
echo "$base" >../patch-merge-base
|
||||
) <"$dotest/patch" 2>/dev/null && break
|
||||
done
|
||||
fi
|
||||
|
||||
test -f "$dotest/patch-merge-index" &&
|
||||
@ -101,7 +91,7 @@ fall_back_3way () {
|
||||
}
|
||||
|
||||
prec=4
|
||||
dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws=
|
||||
dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
|
||||
|
||||
while case "$#" in 0) break;; esac
|
||||
do
|
||||
@ -137,6 +127,9 @@ do
|
||||
--whitespace=*)
|
||||
ws=$1; shift ;;
|
||||
|
||||
--resolvemsg=*)
|
||||
resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
|
||||
|
||||
--)
|
||||
shift; break ;;
|
||||
-*)
|
||||
@ -165,7 +158,7 @@ then
|
||||
else
|
||||
# Make sure we are not given --skip nor --resolved
|
||||
test ",$skip,$resolved," = ,,, ||
|
||||
die "we are not resuming."
|
||||
die "Resolve operation not in progress, we are not resuming."
|
||||
|
||||
# Start afresh.
|
||||
mkdir -p "$dotest" || exit
|
||||
@ -374,7 +367,14 @@ do
|
||||
if test '' = "$changed"
|
||||
then
|
||||
echo "No changes - did you forget update-index?"
|
||||
stop_here $this
|
||||
stop_here_user_resolve $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_user_resolve $this
|
||||
fi
|
||||
apply_status=0
|
||||
;;
|
||||
@ -400,7 +400,7 @@ do
|
||||
if test $apply_status != 0
|
||||
then
|
||||
echo Patch failed at $msgnum.
|
||||
stop_here $this
|
||||
stop_here_user_resolve $this
|
||||
fi
|
||||
|
||||
if test -x "$GIT_DIR"/hooks/pre-applypatch
|
||||
|
@ -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]$/ ) {
|
||||
|
@ -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>.'
|
||||
@ -82,8 +82,7 @@ done
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
git-rev-parse --symbolic --all |
|
||||
sed -ne 's|^refs/heads/||p' |
|
||||
git-rev-parse --symbolic --branches |
|
||||
sort |
|
||||
while read ref
|
||||
do
|
||||
|
@ -144,7 +144,7 @@ else
|
||||
work=`git write-tree` &&
|
||||
git read-tree --reset $new &&
|
||||
git checkout-index -f -u -q -a &&
|
||||
git read-tree -m -u $old $new $work || exit
|
||||
git read-tree -m -u --aggressive $old $new $work || exit
|
||||
|
||||
if result=`git write-tree 2>/dev/null`
|
||||
then
|
||||
|
17
git-clean.sh
17
git-clean.sh
@ -3,13 +3,15 @@
|
||||
# Copyright (c) 2005-2006 Pavel Roskin
|
||||
#
|
||||
|
||||
USAGE="[-d] [-n] [-q] [-x | -X]"
|
||||
USAGE="[-d] [-n] [-q] [-x | -X] [--] <paths>..."
|
||||
LONG_USAGE='Clean untracked files from the working directory
|
||||
-d remove directories as well
|
||||
-n don'\''t remove anything, just show what would be done
|
||||
-q be quiet, only report errors
|
||||
-x remove ignored files as well
|
||||
-X remove only ignored files as well'
|
||||
-X remove only ignored files
|
||||
When optional <paths>... arguments are given, the paths
|
||||
affected are further limited to those that match them.'
|
||||
SUBDIRECTORY_OK=Yes
|
||||
. git-sh-setup
|
||||
|
||||
@ -44,8 +46,15 @@ do
|
||||
-X)
|
||||
ignoredonly=1
|
||||
;;
|
||||
*)
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
break
|
||||
esac
|
||||
shift
|
||||
done
|
||||
@ -64,7 +73,7 @@ if [ -z "$ignored" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
git-ls-files --others --directory $excl ${excl_info:+"$excl_info"} |
|
||||
git-ls-files --others --directory $excl ${excl_info:+"$excl_info"} -- "$@" |
|
||||
while read -r file; do
|
||||
if [ -d "$file" -a ! -L "$file" ]; then
|
||||
if [ -z "$cleandir" ]; then
|
||||
|
@ -261,11 +261,7 @@ yes,yes)
|
||||
;;
|
||||
yes)
|
||||
mkdir -p "$GIT_DIR/objects/info"
|
||||
{
|
||||
test -f "$repo/objects/info/alternates" &&
|
||||
cat "$repo/objects/info/alternates";
|
||||
echo "$repo/objects"
|
||||
} >"$GIT_DIR/objects/info/alternates"
|
||||
echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
|
||||
;;
|
||||
esac
|
||||
git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
|
||||
|
@ -640,6 +640,8 @@ case "$no_edit" in
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
git-var GIT_AUTHOR_IDENT > /dev/null || die
|
||||
git-var GIT_COMMITTER_IDENT > /dev/null || die
|
||||
${VISUAL:-${EDITOR:-vi}} "$GIT_DIR/COMMIT_EDITMSG"
|
||||
;;
|
||||
esac
|
||||
|
@ -1,31 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
#
|
||||
|
||||
GIT_DIR=`git-rev-parse --git-dir` || exit $?
|
||||
|
||||
dc </dev/null 2>/dev/null || {
|
||||
# This is not a real DC at all -- it just knows how
|
||||
# this script feeds DC and does the computation itself.
|
||||
dc () {
|
||||
while read a b
|
||||
do
|
||||
case $a,$b in
|
||||
0,) acc=0 ;;
|
||||
*,+) acc=$(($acc + $a)) ;;
|
||||
p,) echo "$acc" ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
echo $(find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null | wc -l) objects, \
|
||||
$({
|
||||
echo 0
|
||||
# "no-such" is to help Darwin folks by not using xargs -r.
|
||||
find "$GIT_DIR/objects"/?? -type f -print 2>/dev/null |
|
||||
xargs du -k "$GIT_DIR/objects/no-such" 2>/dev/null |
|
||||
sed -e 's/[ ].*/ +/'
|
||||
echo p
|
||||
} | dc) kilobytes
|
@ -10,9 +10,9 @@ unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
|
||||
die "GIT_DIR is not defined or is unreadable";
|
||||
}
|
||||
|
||||
our ($opt_h, $opt_p, $opt_v, $opt_c );
|
||||
our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_m );
|
||||
|
||||
getopts('hpvc');
|
||||
getopts('hpvcfm:');
|
||||
|
||||
$opt_h && usage();
|
||||
|
||||
@ -77,12 +77,16 @@ if ($parent) {
|
||||
$opt_v && print "Applying to CVS commit $commit from parent $parent\n";
|
||||
|
||||
# grab the commit message
|
||||
`git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`;
|
||||
open(MSG, ">.msg") or die "Cannot open .msg for writing";
|
||||
print MSG $opt_m;
|
||||
close MSG;
|
||||
|
||||
`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
|
||||
$? && die "Error extracting the commit message";
|
||||
|
||||
my (@afiles, @dfiles, @mfiles);
|
||||
my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
|
||||
print @files;
|
||||
#print @files;
|
||||
$? && die "Error in git-diff-tree";
|
||||
foreach my $f (@files) {
|
||||
chomp $f;
|
||||
@ -109,7 +113,7 @@ foreach my $f (@afiles) {
|
||||
if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
|
||||
unless ($status[0] =~ m/Status: Unknown$/) {
|
||||
$dirty = 1;
|
||||
warn "File $f is already known in your CVS checkout!\n";
|
||||
warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n";
|
||||
}
|
||||
}
|
||||
foreach my $f (@mfiles, @dfiles) {
|
||||
@ -122,7 +126,11 @@ foreach my $f (@mfiles, @dfiles) {
|
||||
}
|
||||
}
|
||||
if ($dirty) {
|
||||
die "Exiting: your CVS tree is not clean for this merge.";
|
||||
if ($opt_f) { warn "The tree is not clean -- forced merge\n";
|
||||
$dirty = 0;
|
||||
} else {
|
||||
die "Exiting: your CVS tree is not clean for this merge.";
|
||||
}
|
||||
}
|
||||
|
||||
###
|
||||
@ -215,7 +223,7 @@ if ($opt_c) {
|
||||
}
|
||||
sub usage {
|
||||
print STDERR <<END;
|
||||
Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [ parent ] commit
|
||||
Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
|
||||
END
|
||||
exit(1);
|
||||
}
|
||||
|
@ -350,7 +350,7 @@ sub _line {
|
||||
return $res;
|
||||
} elsif($line =~ s/^E //) {
|
||||
# print STDERR "S: $line\n";
|
||||
} elsif($line =~ /^Remove-entry /i) {
|
||||
} elsif($line =~ /^(Remove-entry|Removed) /i) {
|
||||
$line = $self->readline(); # filename
|
||||
$line = $self->readline(); # OK
|
||||
chomp $line;
|
||||
|
@ -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 )
|
||||
@ -214,8 +214,7 @@ sub req_Globaloption
|
||||
{
|
||||
my ( $cmd, $data ) = @_;
|
||||
$log->debug("req_Globaloption : $data");
|
||||
|
||||
# TODO : is this data useful ???
|
||||
$state->{globaloptions}{$data} = 1;
|
||||
}
|
||||
|
||||
# Valid-responses request-list \n
|
||||
@ -224,7 +223,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 ];
|
||||
@ -267,12 +266,32 @@ sub req_Directory
|
||||
|
||||
$state->{localdir} = $data;
|
||||
$state->{repository} = $repository;
|
||||
$state->{directory} = $repository;
|
||||
$state->{directory} =~ s/^$state->{CVSROOT}\///;
|
||||
$state->{module} = $1 if ($state->{directory} =~ s/^(.*?)(\/|$)//);
|
||||
$state->{path} = $repository;
|
||||
$state->{path} =~ s/^$state->{CVSROOT}\///;
|
||||
$state->{module} = $1 if ($state->{path} =~ s/^(.*?)(\/|$)//);
|
||||
$state->{path} .= "/" if ( $state->{path} =~ /\S/ );
|
||||
|
||||
$state->{directory} = $state->{localdir};
|
||||
$state->{directory} = "" if ( $state->{directory} eq "." );
|
||||
$state->{directory} .= "/" if ( $state->{directory} =~ /\S/ );
|
||||
|
||||
$log->debug("req_Directory : localdir=$data repository=$repository directory=$state->{directory} module=$state->{module}");
|
||||
if ( not defined($state->{prependdir}) and $state->{localdir} eq "." and $state->{path} =~ /\S/ )
|
||||
{
|
||||
$log->info("Setting prepend to '$state->{path}'");
|
||||
$state->{prependdir} = $state->{path};
|
||||
foreach my $entry ( keys %{$state->{entries}} )
|
||||
{
|
||||
$state->{entries}{$state->{prependdir} . $entry} = $state->{entries}{$entry};
|
||||
delete $state->{entries}{$entry};
|
||||
}
|
||||
}
|
||||
|
||||
if ( defined ( $state->{prependdir} ) )
|
||||
{
|
||||
$log->debug("Prepending '$state->{prependdir}' to state|directory");
|
||||
$state->{directory} = $state->{prependdir} . $state->{directory}
|
||||
}
|
||||
$log->debug("req_Directory : localdir=$data repository=$repository path=$state->{path} directory=$state->{directory} module=$state->{module}");
|
||||
}
|
||||
|
||||
# Entry entry-line \n
|
||||
@ -290,7 +309,7 @@ sub req_Entry
|
||||
{
|
||||
my ( $cmd, $data ) = @_;
|
||||
|
||||
$log->debug("req_Entry : $data");
|
||||
#$log->debug("req_Entry : $data");
|
||||
|
||||
my @data = split(/\//, $data);
|
||||
|
||||
@ -300,6 +319,22 @@ sub req_Entry
|
||||
options => $data[4],
|
||||
tag_or_date => $data[5],
|
||||
};
|
||||
|
||||
$log->info("Received entry line '$data' => '" . $state->{directory} . $data[1] . "'");
|
||||
}
|
||||
|
||||
# Questionable filename \n
|
||||
# Response expected: no. Additional data: no. Tell the server to check
|
||||
# whether filename should be ignored, and if not, next time the server
|
||||
# sends responses, send (in a M response) `?' followed by the directory and
|
||||
# filename. filename must not contain `/'; it needs to be a file in the
|
||||
# directory named by the most recent Directory request.
|
||||
sub req_Questionable
|
||||
{
|
||||
my ( $cmd, $data ) = @_;
|
||||
|
||||
$log->debug("req_Questionable : $data");
|
||||
$state->{entries}{$state->{directory}.$data}{questionable} = 1;
|
||||
}
|
||||
|
||||
# add \n
|
||||
@ -332,8 +367,7 @@ sub req_add
|
||||
next;
|
||||
}
|
||||
|
||||
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename);
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
|
||||
|
||||
print "E cvs add: scheduling file `$filename' for addition\n";
|
||||
|
||||
@ -414,7 +448,7 @@ sub req_remove
|
||||
}
|
||||
|
||||
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename);
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
|
||||
|
||||
print "E cvs remove: scheduling `$filename' for removal\n";
|
||||
|
||||
@ -502,22 +536,6 @@ sub req_Unchanged
|
||||
#$log->debug("req_Unchanged : $data");
|
||||
}
|
||||
|
||||
# Questionable filename \n
|
||||
# Response expected: no. Additional data: no.
|
||||
# Tell the server to check whether filename should be ignored,
|
||||
# and if not, next time the server sends responses, send (in
|
||||
# a M response) `?' followed by the directory and filename.
|
||||
# filename must not contain `/'; it needs to be a file in the
|
||||
# directory named by the most recent Directory request.
|
||||
sub req_Questionable
|
||||
{
|
||||
my ( $cmd, $data ) = @_;
|
||||
|
||||
$state->{entries}{$state->{directory}.$data}{questionable} = 1;
|
||||
|
||||
#$log->debug("req_Questionable : $data");
|
||||
}
|
||||
|
||||
# Argument text \n
|
||||
# Response expected: no. Save argument for use in a subsequent command.
|
||||
# Arguments accumulate until an argument-using command is given, at which
|
||||
@ -733,7 +751,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.
|
||||
@ -757,8 +775,7 @@ sub req_update
|
||||
|
||||
$updater->update();
|
||||
|
||||
# if no files were specified, we need to work out what files we should be providing status on ...
|
||||
argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 );
|
||||
argsfromdir($updater);
|
||||
|
||||
#$log->debug("update state : " . Dumper($state));
|
||||
|
||||
@ -767,6 +784,8 @@ sub req_update
|
||||
{
|
||||
$filename = filecleanup($filename);
|
||||
|
||||
$log->debug("Processing file $filename");
|
||||
|
||||
# if we have a -C we should pretend we never saw modified stuff
|
||||
if ( exists ( $state->{opt}{C} ) )
|
||||
{
|
||||
@ -821,13 +840,16 @@ sub req_update
|
||||
|
||||
if ( $meta->{filehash} eq "deleted" )
|
||||
{
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename);
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename,1);
|
||||
|
||||
$log->info("Removing '$filename' from working copy (no longer in the repo)");
|
||||
|
||||
print "E cvs update: `$filename' is no longer in the repository\n";
|
||||
print "Removed $dirpart\n";
|
||||
print "$filepart\n";
|
||||
# Don't want to actually _DO_ the update if -n specified
|
||||
unless ( $state->{globaloptions}{-n} ) {
|
||||
print "Removed $dirpart\n";
|
||||
print "$filepart\n";
|
||||
}
|
||||
}
|
||||
elsif ( not defined ( $state->{entries}{$filename}{modified_hash} )
|
||||
or $state->{entries}{$filename}{modified_hash} eq $oldmeta->{filehash} )
|
||||
@ -840,34 +862,42 @@ sub req_update
|
||||
print "MT newline\n";
|
||||
print "MT -updated\n";
|
||||
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename);
|
||||
$dirpart =~ s/^$state->{directory}//;
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename,1);
|
||||
|
||||
if ( defined ( $wrev ) )
|
||||
{
|
||||
# instruct client we're sending a file to put in this path as a replacement
|
||||
print "Update-existing $dirpart\n";
|
||||
$log->debug("Updating existing file 'Update-existing $dirpart'");
|
||||
} else {
|
||||
# instruct client we're sending a file to put in this path as a new file
|
||||
print "Created $dirpart\n";
|
||||
$log->debug("Creating new file 'Created $dirpart'");
|
||||
}
|
||||
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
|
||||
# Don't want to actually _DO_ the update if -n specified
|
||||
unless ( $state->{globaloptions}{-n} )
|
||||
{
|
||||
if ( defined ( $wrev ) )
|
||||
{
|
||||
# instruct client we're sending a file to put in this path as a replacement
|
||||
print "Update-existing $dirpart\n";
|
||||
$log->debug("Updating existing file 'Update-existing $dirpart'");
|
||||
} else {
|
||||
# instruct client we're sending a file to put in this path as a new file
|
||||
print "Clear-static-directory $dirpart\n";
|
||||
print $state->{CVSROOT} . "/$state->{module}/$dirpart\n";
|
||||
print "Clear-sticky $dirpart\n";
|
||||
print $state->{CVSROOT} . "/$state->{module}/$dirpart\n";
|
||||
|
||||
# this is an "entries" line
|
||||
$log->debug("/$filepart/1.$meta->{revision}///");
|
||||
print "/$filepart/1.$meta->{revision}///\n";
|
||||
$log->debug("Creating new file 'Created $dirpart'");
|
||||
print "Created $dirpart\n";
|
||||
}
|
||||
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
|
||||
|
||||
# permissions
|
||||
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
|
||||
print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
|
||||
# this is an "entries" line
|
||||
$log->debug("/$filepart/1.$meta->{revision}///");
|
||||
print "/$filepart/1.$meta->{revision}///\n";
|
||||
|
||||
# transmit file
|
||||
transmitfile($meta->{filehash});
|
||||
# permissions
|
||||
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
|
||||
print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
|
||||
|
||||
# transmit file
|
||||
transmitfile($meta->{filehash});
|
||||
}
|
||||
} else {
|
||||
$log->info("Updating '$filename'");
|
||||
my ( $filepart, $dirpart ) = filenamesplit($meta->{name});
|
||||
my ( $filepart, $dirpart ) = filenamesplit($meta->{name},1);
|
||||
|
||||
my $dir = tempdir( DIR => $TEMP_DIR, CLEANUP => 1 ) . "/";
|
||||
|
||||
@ -892,19 +922,29 @@ sub req_update
|
||||
$log->info("Merged successfully");
|
||||
print "M M $filename\n";
|
||||
$log->debug("Update-existing $dirpart");
|
||||
print "Update-existing $dirpart\n";
|
||||
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
|
||||
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
|
||||
$log->debug("/$filepart/1.$meta->{revision}///");
|
||||
print "/$filepart/1.$meta->{revision}///\n";
|
||||
|
||||
# Don't want to actually _DO_ the update if -n specified
|
||||
unless ( $state->{globaloptions}{-n} )
|
||||
{
|
||||
print "Update-existing $dirpart\n";
|
||||
$log->debug($state->{CVSROOT} . "/$state->{module}/$filename");
|
||||
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
|
||||
$log->debug("/$filepart/1.$meta->{revision}///");
|
||||
print "/$filepart/1.$meta->{revision}///\n";
|
||||
}
|
||||
}
|
||||
elsif ( $return == 1 )
|
||||
{
|
||||
$log->info("Merged with conflicts");
|
||||
print "M C $filename\n";
|
||||
print "Update-existing $dirpart\n";
|
||||
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
|
||||
print "/$filepart/1.$meta->{revision}/+//\n";
|
||||
|
||||
# Don't want to actually _DO_ the update if -n specified
|
||||
unless ( $state->{globaloptions}{-n} )
|
||||
{
|
||||
print "Update-existing $dirpart\n";
|
||||
print $state->{CVSROOT} . "/$state->{module}/$filename\n";
|
||||
print "/$filepart/1.$meta->{revision}/+//\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -912,17 +952,21 @@ sub req_update
|
||||
next;
|
||||
}
|
||||
|
||||
# permissions
|
||||
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
|
||||
print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
|
||||
# Don't want to actually _DO_ the update if -n specified
|
||||
unless ( $state->{globaloptions}{-n} )
|
||||
{
|
||||
# permissions
|
||||
$log->debug("SEND : u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}");
|
||||
print "u=$meta->{mode},g=$meta->{mode},o=$meta->{mode}\n";
|
||||
|
||||
# transmit file, format is single integer on a line by itself (file
|
||||
# size) followed by the file contents
|
||||
# TODO : we should copy files in blocks
|
||||
my $data = `cat $file_local`;
|
||||
$log->debug("File size : " . length($data));
|
||||
print length($data) . "\n";
|
||||
print $data;
|
||||
# transmit file, format is single integer on a line by itself (file
|
||||
# size) followed by the file contents
|
||||
# TODO : we should copy files in blocks
|
||||
my $data = `cat $file_local`;
|
||||
$log->debug("File size : " . length($data));
|
||||
print length($data) . "\n";
|
||||
print $data;
|
||||
}
|
||||
|
||||
chdir "/";
|
||||
}
|
||||
@ -950,6 +994,7 @@ sub req_ci
|
||||
|
||||
if ( -e $state->{CVSROOT} . "/index" )
|
||||
{
|
||||
$log->warn("file 'index' already exists in the git repository");
|
||||
print "error 1 Index already exists in git repo\n";
|
||||
exit;
|
||||
}
|
||||
@ -957,6 +1002,7 @@ sub req_ci
|
||||
my $lockfile = "$state->{CVSROOT}/refs/heads/$state->{module}.lock";
|
||||
unless ( sysopen(LOCKFILE,$lockfile,O_EXCL|O_CREAT|O_WRONLY) )
|
||||
{
|
||||
$log->warn("lockfile '$lockfile' already exists, please try again");
|
||||
print "error 1 Lock file '$lockfile' already exists, please try again\n";
|
||||
exit;
|
||||
}
|
||||
@ -988,6 +1034,7 @@ sub req_ci
|
||||
# foreach file specified on the commandline ...
|
||||
foreach my $filename ( @{$state->{args}} )
|
||||
{
|
||||
my $committedfile = $filename;
|
||||
$filename = filecleanup($filename);
|
||||
|
||||
next unless ( exists $state->{entries}{$filename}{modified_filename} or not $state->{entries}{$filename}{unchanged} );
|
||||
@ -1022,7 +1069,7 @@ sub req_ci
|
||||
exit;
|
||||
}
|
||||
|
||||
push @committedfiles, $filename;
|
||||
push @committedfiles, $committedfile;
|
||||
$log->info("Committing $filename");
|
||||
|
||||
system("mkdir","-p",$dirpart) unless ( -d $dirpart );
|
||||
@ -1105,7 +1152,7 @@ sub req_ci
|
||||
|
||||
my $meta = $updater->getmeta($filename);
|
||||
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename);
|
||||
my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
|
||||
|
||||
$log->debug("Checked-in $dirpart : $filename");
|
||||
|
||||
@ -1141,7 +1188,7 @@ sub req_status
|
||||
$updater->update();
|
||||
|
||||
# if no files were specified, we need to work out what files we should be providing status on ...
|
||||
argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 );
|
||||
argsfromdir($updater);
|
||||
|
||||
# foreach file specified on the commandline ...
|
||||
foreach my $filename ( @{$state->{args}} )
|
||||
@ -1242,7 +1289,7 @@ sub req_diff
|
||||
$updater->update();
|
||||
|
||||
# if no files were specified, we need to work out what files we should be providing status on ...
|
||||
argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 );
|
||||
argsfromdir($updater);
|
||||
|
||||
# foreach file specified on the commandline ...
|
||||
foreach my $filename ( @{$state->{args}} )
|
||||
@ -1384,7 +1431,7 @@ sub req_log
|
||||
$updater->update();
|
||||
|
||||
# if no files were specified, we need to work out what files we should be providing status on ...
|
||||
argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 );
|
||||
argsfromdir($updater);
|
||||
|
||||
# foreach file specified on the commandline ...
|
||||
foreach my $filename ( @{$state->{args}} )
|
||||
@ -1460,7 +1507,7 @@ sub req_annotate
|
||||
$updater->update();
|
||||
|
||||
# if no files were specified, we need to work out what files we should be providing annotate on ...
|
||||
argsfromdir($updater) if ( scalar ( @{$state->{args}} ) == 0 );
|
||||
argsfromdir($updater);
|
||||
|
||||
# we'll need a temporary checkout dir
|
||||
my $tmpdir = tempdir ( DIR => $TEMP_DIR );
|
||||
@ -1655,13 +1702,36 @@ sub argsfromdir
|
||||
{
|
||||
my $updater = shift;
|
||||
|
||||
$state->{args} = [];
|
||||
$state->{args} = [] if ( scalar(@{$state->{args}}) == 1 and $state->{args}[0] eq "." );
|
||||
|
||||
foreach my $file ( @{$updater->gethead} )
|
||||
return if ( scalar ( @{$state->{args}} ) > 1 );
|
||||
|
||||
if ( scalar(@{$state->{args}}) == 1 )
|
||||
{
|
||||
next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
|
||||
next unless ( $file->{name} =~ s/^$state->{directory}// );
|
||||
push @{$state->{args}}, $file->{name};
|
||||
my $arg = $state->{args}[0];
|
||||
$arg .= $state->{prependdir} if ( defined ( $state->{prependdir} ) );
|
||||
|
||||
$log->info("Only one arg specified, checking for directory expansion on '$arg'");
|
||||
|
||||
foreach my $file ( @{$updater->gethead} )
|
||||
{
|
||||
next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
|
||||
next unless ( $file->{name} =~ /^$arg\// or $file->{name} eq $arg );
|
||||
push @{$state->{args}}, $file->{name};
|
||||
}
|
||||
|
||||
shift @{$state->{args}} if ( scalar(@{$state->{args}}) > 1 );
|
||||
} else {
|
||||
$log->info("Only one arg specified, populating file list automatically");
|
||||
|
||||
$state->{args} = [];
|
||||
|
||||
foreach my $file ( @{$updater->gethead} )
|
||||
{
|
||||
next if ( $file->{filehash} eq "deleted" and not defined ( $state->{entries}{$file->{name}} ) );
|
||||
next unless ( $file->{name} =~ s/^$state->{prependdir}// );
|
||||
push @{$state->{args}}, $file->{name};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1731,16 +1801,22 @@ 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
|
||||
{
|
||||
my $filename = shift;
|
||||
my $fixforlocaldir = shift;
|
||||
|
||||
my ( $filepart, $dirpart ) = ( $filename, "." );
|
||||
( $filepart, $dirpart ) = ( $2, $1 ) if ( $filename =~ /(.*)\/(.*)/ );
|
||||
$dirpart .= "/";
|
||||
|
||||
if ( $fixforlocaldir )
|
||||
{
|
||||
$dirpart =~ s/^$state->{prependdir}//;
|
||||
}
|
||||
|
||||
return ( $filepart, $dirpart );
|
||||
}
|
||||
|
||||
@ -1756,8 +1832,7 @@ sub filecleanup
|
||||
}
|
||||
|
||||
$filename =~ s/^\.\///g;
|
||||
$filename = $state->{directory} . $filename;
|
||||
|
||||
$filename = $state->{prependdir} . $filename;
|
||||
return $filename;
|
||||
}
|
||||
|
||||
@ -1790,7 +1865,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.
|
||||
|
||||
@ -2076,14 +2151,15 @@ sub update
|
||||
# TODO: log processing is memory bound
|
||||
# if we can parse into a 2nd file that is in reverse order
|
||||
# we can probably do something really efficient
|
||||
my @git_log_params = ('--parents', '--topo-order');
|
||||
my @git_log_params = ('--pretty', '--parents', '--topo-order');
|
||||
|
||||
if (defined $lastcommit) {
|
||||
push @git_log_params, "$lastcommit..$self->{module}";
|
||||
} else {
|
||||
push @git_log_params, $self->{module};
|
||||
}
|
||||
open(GITLOG, '-|', 'git-log', @git_log_params) or die "Cannot call git-log: $!";
|
||||
# git-rev-list is the backend / plumbing version of git-log
|
||||
open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
|
||||
|
||||
my @commits;
|
||||
|
||||
@ -2595,7 +2671,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
|
||||
|
74
git-diff.sh
74
git-diff.sh
@ -1,74 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2005 Linus Torvalds
|
||||
# Copyright (c) 2005 Junio C Hamano
|
||||
|
||||
USAGE='[ --diff-options ] <ent>{0,2} [<path>...]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
rev=$(git-rev-parse --revs-only --no-flags --sq "$@") || exit
|
||||
flags=$(git-rev-parse --no-revs --flags --sq "$@")
|
||||
files=$(git-rev-parse --no-revs --no-flags --sq "$@")
|
||||
|
||||
# I often say 'git diff --cached -p' and get scolded by git-diff-files, but
|
||||
# obviously I mean 'git diff --cached -p HEAD' in that case.
|
||||
case "$rev" in
|
||||
'')
|
||||
case " $flags " in
|
||||
*" '--cached' "*)
|
||||
rev='HEAD '
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
||||
# If we have -[123] --ours --theirs --base, don't do --cc by default.
|
||||
case " $flags " in
|
||||
*" '-"[123]"' "* | *" '--ours' "* | *" '--base' "* | *" '--theirs' "*)
|
||||
cc_or_p=-p ;;
|
||||
*)
|
||||
cc_or_p=--cc ;;
|
||||
esac
|
||||
|
||||
# If we do not have --name-status, --name-only, -r, -c or --stat,
|
||||
# default to --cc.
|
||||
case " $flags " in
|
||||
*" '--name-status' "* | *" '--name-only' "* | *" '-r' "* | *" '-c' "* | \
|
||||
*" '--stat' "*)
|
||||
;;
|
||||
*)
|
||||
flags="$flags'$cc_or_p' " ;;
|
||||
esac
|
||||
|
||||
# If we do not have -B, -C, -r, nor -p, default to -M.
|
||||
case " $flags " in
|
||||
*" '-"[BCMrp]* | *" '--find-copies-harder' "*)
|
||||
;; # something like -M50.
|
||||
*)
|
||||
flags="$flags'-M' " ;;
|
||||
esac
|
||||
|
||||
case "$rev" in
|
||||
?*' '?*' '?*)
|
||||
usage
|
||||
;;
|
||||
?*' '^?*)
|
||||
begin=$(expr "$rev" : '.*^.\([0-9a-f]*\).*') &&
|
||||
end=$(expr "$rev" : '.\([0-9a-f]*\). .*') || exit
|
||||
cmd="git-diff-tree $flags $begin $end -- $files"
|
||||
;;
|
||||
?*' '?*)
|
||||
cmd="git-diff-tree $flags $rev -- $files"
|
||||
;;
|
||||
?*' ')
|
||||
cmd="git-diff-index $flags $rev -- $files"
|
||||
;;
|
||||
'')
|
||||
cmd="git-diff-files $flags -- $files"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
|
||||
eval "$cmd"
|
16
git-fetch.sh
16
git-fetch.sh
@ -270,14 +270,22 @@ fetch_main () {
|
||||
if [ -n "$GIT_SSL_NO_VERIFY" ]; then
|
||||
curl_extra_args="-k"
|
||||
fi
|
||||
remote_name_quoted=$(perl -e '
|
||||
max_depth=5
|
||||
depth=0
|
||||
head="ref: $remote_name"
|
||||
while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
|
||||
do
|
||||
remote_name_quoted=$(perl -e '
|
||||
my $u = $ARGV[0];
|
||||
$u =~ s/^ref:\s*//;
|
||||
$u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
|
||||
print "$u";
|
||||
' "$remote_name")
|
||||
head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted") &&
|
||||
' "$head")
|
||||
head=$(curl -nsfL $curl_extra_args "$remote/$remote_name_quoted")
|
||||
depth=$( expr \( $depth + 1 \) )
|
||||
done
|
||||
expr "z$head" : "z$_x40\$" >/dev/null ||
|
||||
die "Failed to fetch $remote_name from $remote"
|
||||
die "Failed to fetch $remote_name from $remote"
|
||||
echo >&2 Fetching "$remote_name from $remote" using http
|
||||
git-http-fetch -v -a "$head" "$remote/" || exit
|
||||
;;
|
||||
|
@ -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";
|
||||
@ -275,7 +274,7 @@ print "\n---\n\n";
|
||||
close FH or die "close $commsg pipe";
|
||||
' "$keep_subject" "$num" "$signoff" "$headers" "$mimemagic" $commsg
|
||||
|
||||
git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
|
||||
git-diff-tree -p --stat --summary $diff_opts "$commit"
|
||||
echo
|
||||
case "$mimemagic" in
|
||||
'');;
|
||||
|
62
git-grep.sh
62
git-grep.sh
@ -1,62 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) Linus Torvalds, 2005
|
||||
#
|
||||
|
||||
USAGE='[<option>...] [-e] <pattern> [<path>...]'
|
||||
SUBDIRECTORY_OK='Yes'
|
||||
. git-sh-setup
|
||||
|
||||
got_pattern () {
|
||||
if [ -z "$no_more_patterns" ]
|
||||
then
|
||||
pattern="$1" no_more_patterns=yes
|
||||
else
|
||||
die "git-grep: do not specify more than one pattern"
|
||||
fi
|
||||
}
|
||||
|
||||
no_more_patterns=
|
||||
pattern=
|
||||
flags=()
|
||||
git_flags=()
|
||||
while : ; do
|
||||
case "$1" in
|
||||
-o|--cached|--deleted|--others|--killed|\
|
||||
--ignored|--modified|--exclude=*|\
|
||||
--exclude-from=*|\--exclude-per-directory=*)
|
||||
git_flags=("${git_flags[@]}" "$1")
|
||||
;;
|
||||
-e)
|
||||
got_pattern "$2"
|
||||
shift
|
||||
;;
|
||||
-A|-B|-C|-D|-d|-f|-m)
|
||||
flags=("${flags[@]}" "$1" "$2")
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
# The rest are git-ls-files paths
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
flags=("${flags[@]}" "$1")
|
||||
;;
|
||||
*)
|
||||
if [ -z "$no_more_patterns" ]
|
||||
then
|
||||
got_pattern "$1"
|
||||
shift
|
||||
fi
|
||||
[ "$1" = -- ] && shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
[ "$pattern" ] || {
|
||||
usage
|
||||
}
|
||||
git-ls-files -z "${git_flags[@]}" -- "$@" |
|
||||
xargs -0 grep "${flags[@]}" -e "$pattern" --
|
@ -55,8 +55,7 @@ finish () {
|
||||
|
||||
case "$no_summary" in
|
||||
'')
|
||||
git-diff-tree -p -M "$head" "$1" |
|
||||
git-apply --stat --summary
|
||||
git-diff-tree -p --stat --summary -M "$head" "$1"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user