Merge refs/heads/master from .

This commit is contained in:
Junio C Hamano 2005-08-29 19:09:48 -07:00
commit 7a03433742
22 changed files with 1286 additions and 518 deletions

View File

@ -1,32 +0,0 @@
git-apply-patch-script(1)
=========================
v0.99.4, May 2005
NAME
----
git-apply-patch-script - Sample script to apply the diffs from git-diff-*
SYNOPSIS
--------
'git-apply-patch-script'
DESCRIPTION
-----------
This is a sample script to be used via the 'GIT_EXTERNAL_DIFF'
environment variable to apply the differences that the "git-diff-*"
family of commands report to the current work tree.
Author
------
Written by Junio C Hamano <junkio@cox.net>
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the link:git.html[git] suite

View File

@ -3,26 +3,66 @@ git-applymbox(1)
NAME
----
git-applymbox - Some git command not yet documented.
git-applymbox - Apply a series of patches in a mailbox
SYNOPSIS
--------
'git-applymbox' [ --option ] <args>...
'git-applymbox' [-u] [-k] [-q] ( -c .dotest/<num> | <mbox> ) [ <signoff> ]
DESCRIPTION
-----------
Does something not yet documented.
Splits mail messages in a mailbox into commit log message,
authorship information and patches, and applies them to the
current branch.
OPTIONS
-------
--option::
Some option not yet documented.
-q::
Apply patches interactively. The user will be given
opportunity to edit the log message and the patch before
attempting to apply patch in each e-mail message.
<args>...::
Some argument not yet documented.
-k::
Usually the program 'cleans up' the Subject: header line
to extract the title line for the commit log message,
among which (1) remove 'Re:' or 're:', (2) leading
whitespaces, (3) '[' up to ']', typically '[PATCH]', and
then prepends "[PATCH] ". This flag forbids this
munging, and is most useful when used to read back 'git
format-patch --mbox' output.
-u::
By default, the commit log message, author name and
author email are taken from the e-mail without any
charset conversion, after minimally decoding MIME
transfer encoding. This flag causes the resulting
commit to be encoded in utf-8 by transliterating them.
Note that the patch is always used as is without charset
conversion, even with this flag.
-c .dotest/<num>::
When the patch contained in an e-mail does not cleanly
apply, the command exits with an error message. The
patch and extracted message are found in .dotest/, and
you could re-run 'git applymbox' with '-c .dotest/<num>'
flag to restart the process after inspecting and fixing
them.
<mbox>::
The name of the file that contains the e-mail messages
with patches. This file should be in the UNIX mailbox
format. See 'SubmittingPatches' document to learn about
the formatting convention for e-mail submission.
<signoff>::
The name of the file that contains your "Signed-off-by"
line. See 'SubmittingPatches' document to learn what
"Signed-off-by" line means. You can also just say
'yes', 'true', 'me', or 'please' to use an automatically
generated "Signed-off-by" line based on your committer
identity.
Author
------

View File

@ -3,25 +3,33 @@ git-applypatch(1)
NAME
----
git-applypatch - Some git command not yet documented.
git-applypatch - Apply one patch extracted from an e-mail.
SYNOPSIS
--------
'git-applypatch' [ --option ] <args>...
'git-applypatch' <msg> <patch> <info> [<signoff>]
DESCRIPTION
-----------
Does something not yet documented.
Takes three files <msg>, <patch>, and <info> prepared from an
e-mail message by 'git-mailinfo', and creates a commit. It is
usually not necessary to use this command directly.
OPTIONS
-------
--option::
Some option not yet documented.
<msg>::
Commit log message (sans the first line, which comes
from e-mail Subject stored in <info>).
<args>...::
Some argument not yet documented.
<patch>::
The patch to apply.
<info>:
Author and subject information extracted from e-mail,
used on "author" line and as the first line of the
commit log message.
Author

View File

@ -3,25 +3,69 @@ git-bisect-script(1)
NAME
----
git-bisect-script - Some git command not yet documented.
git-bisect-script - Find the change that introduced a bug
SYNOPSIS
--------
'git-bisect-script' [ --option ] <args>...
'git bisect' start
'git bisect' bad <rev>
'git bisect' good <rev>
'git bisect' reset [<branch>]
DESCRIPTION
-----------
Does something not yet documented.
This command uses 'git-rev-list --bisect' option to help drive
the binary search process to find which change introduced a bug,
given an old "good" commit object name and a later "bad" commit
object name.
The way you use it is:
OPTIONS
-------
--option::
Some option not yet documented.
------------------------------------------------
git bisect start
git bisect bad # Current version is bad
git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version
# tested that was good
------------------------------------------------
<args>...::
Some argument not yet documented.
When you give at least one bad and one good versions, it will
bisect the revision tree and say something like:
------------------------------------------------
Bisecting: 675 revisions left to test after this
------------------------------------------------
and check out the state in the middle. Now, compile that kernel, and boot
it. Now, let's say that this booted kernel works fine, then just do
------------------------------------------------
git bisect good # this one is good
------------------------------------------------
which will now say
------------------------------------------------
Bisecting: 337 revisions left to test after this
------------------------------------------------
and you continue along, compiling that one, testing it, and depending on
whether it is good or bad, you say "git bisect good" or "git bisect bad",
and ask for the next bisection.
Until you have no more left, and you'll have been left with the first bad
kernel rev in "refs/bisect/bad".
Oh, and then after you want to reset to the original head, do a
------------------------------------------------
git bisect reset
------------------------------------------------
to get back to the master branch, instead of being in one of the bisection
branches ("git bisect start" will do that for you too, actually: it will
reset the bisection state, and before it does that it checks that you're
not using some old bisection branch).
Author

View File

@ -0,0 +1,57 @@
git-cherry-pick-script(1)
=========================
v0.99.5 Aug 2005
NAME
----
git-cherry-pick-script - Apply the change introduced by an existing commit.
SYNOPSIS
--------
'git-cherry-pick-script' [-n] [-r] <commit>
DESCRIPTION
-----------
Given one existing commit, apply the change the patch introduces, and record a
new commit that records it. This requires your working tree to be clean (no
modifications from the HEAD commit).
OPTIONS
-------
<commit>::
Commit to cherry-pick.
-r::
Usuall the command appends which commit was
cherry-picked after the original commit message when
making a commit. This option, '--replay', causes it to
use the original commit message intact. This is useful
when you are reordering the patches in your private tree
before publishing, and is used by 'git rebase'.
-n::
Usually the command automatically creates a commit with
a commit log message stating which commit was
cherry-picked. This flag applies the change necessary
to cherry-pick the named commit to your working tree,
but does not make the commit. In addition, when this
option is used, your working tree does not have to match
the HEAD commit. The cherry-pick is done against the
beginning state of your working tree.
This is useful when cherry-picking more than one commits'
effect to your working tree in a row.
Author
------
Written by Junio C Hamano <junkio@cox.net>
Documentation
--------------
Documentation by Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the link:git.html[git] suite

View File

@ -3,30 +3,57 @@ git-mailinfo(1)
NAME
----
git-mailinfo - Some git command not yet documented.
git-mailinfo - Extracts patch from a single e-mail message.
SYNOPSIS
--------
'git-mailinfo' [ --option ] <args>...
'git-mailinfo' [-k] [-u] <msg> <patch>
DESCRIPTION
-----------
Does something not yet documented.
Reading a single e-mail message from the standard input, and
writes the commit log message in <msg> file, and the patches in
<patch> file. The author name, e-mail and e-mail subject are
written out to the standard output to be used by git-applypatch
to create a commit. It is usually not necessary to use this
command directly.
OPTIONS
-------
--option::
Some option not yet documented.
-k::
Usually the program 'cleans up' the Subject: header line
to extract the title line for the commit log message,
among which (1) remove 'Re:' or 're:', (2) leading
whitespaces, (3) '[' up to ']', typically '[PATCH]', and
then prepends "[PATCH] ". This flag forbids this
munging, and is most useful when used to read back 'git
format-patch --mbox' output.
<args>...::
Some argument not yet documented.
-u::
By default, the commit log message, author name and
author email are taken from the e-mail without any
charset conversion, after minimally decoding MIME
transfer encoding. This flag causes the resulting
commit to be encoded in utf-8 by transliterating them.
Note that the patch is always used as is without charset
conversion, even with this flag.
<msg>::
The commit log message extracted from e-mail, usually
except the title line which comes from e-mail Subject.
<patch>::
The patch extracted from e-mail.
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
Written by Linus Torvalds <torvalds@osdl.org> and
Junio C Hamano <junkio@cox.net>
Documentation
--------------

View File

@ -1,6 +1,6 @@
git-prune-script(1)
===================
v0.1, May 2005
v0.99.5, Aug 2005
NAME
----
@ -9,14 +9,24 @@ git-prune-script - Prunes all unreachable objects from the object database
SYNOPSIS
--------
'git-prune-script'
'git-prune-script' [-n]
DESCRIPTION
-----------
This runs "git-fsck-cache --unreachable" program using the heads specified
on the command line (or `$GIT_DIR/refs/heads/\*` and `$GIT_DIR/refs/tags/\*`
if none is specified), and prunes all unreachable objects from the object
database.
This runs `git-fsck-cache --unreachable` using the heads
specified on the command line (or `$GIT_DIR/refs/heads/\*` and
`$GIT_DIR/refs/tags/\*` if none is specified), and prunes all
unreachable objects from the object database. In addition, it
prunes the unpacked objects that are also found in packs by
running `git prune-packed`.
OPTIONS
-------
-n::
Do not remove anything; just report what it would
remove.
Author

View File

@ -1,6 +1,6 @@
git-repack-script(1)
=====================
v0.1, August 2005
v0.99.5, August 2005
NAME
----
@ -10,17 +10,36 @@ objects into pack files.
SYNOPSIS
--------
'git-repack-script'
'git-repack-script' [-a] [-d]
DESCRIPTION
-----------
This script is used to combine all objects that do not currently reside in a
"pack", into a pack.
A pack is a collection of objects, individually compressed, with delta
compression applied, stored in a single file, with an associated index file.
This script is used to combine all objects that do not currently
reside in a "pack", into a pack.
A pack is a collection of objects, individually compressed, with
delta compression applied, stored in a single file, with an
associated index file.
Packs are used to reduce the load on mirror systems, backup
engines, disk storage, etc.
OPTIONS
-------
-a::
Instead of incrementally packing the unpacked objects,
pack everything available into a single pack.
Especially useful when packing a repository that is used
for a private development and there no need to worry
about people fetching via dumb protocols from it. Use
with '-d'.
-d::
After packing, if the newly created packs make some
existing packs redundant, remove the redundant packs.
Packs are used to reduce the load on mirror systems, backup engines, disk storage, etc.
Author
------

View File

@ -7,7 +7,7 @@ git-revert-script - Revert an existing commit.
SYNOPSIS
--------
'git-revert-script' <commit>
'git-revert-script' [-n] <commit>
DESCRIPTION
-----------
@ -20,6 +20,20 @@ OPTIONS
<commit>::
Commit to revert.
-n::
Usually the command automatically creates a commit with
a commit log message stating which commit was reverted.
This flag applies the change necessary to revert the
named commit to your working tree, but does not make the
commit. In addition, when this option is used, your
working tree does not have to match the HEAD commit.
The revert is done against the beginning state of your
working tree.
This is useful when reverting more than one commits'
effect to your working tree in a row.
Author
------
Written by Junio C Hamano <junkio@cox.net>

View File

@ -1,6 +1,6 @@
git-show-branch(1)
==================
v0.99.4, Aug 2005
v0.99.5, Aug 2005
NAME
----
@ -28,7 +28,8 @@ OPTIONS
--more=<n>::
Usually the command stops output upon showing the commit
that is the common ancestor of all the branches. This
flag tells the command to go <n> commits beyond that.
flag tells the command to go <n> more common commits
beyond that.
--merge-base::
Instead of showing the commit list, just act like the

View File

@ -52,49 +52,50 @@ SCMs layered over git.
Manipulation commands
~~~~~~~~~~~~~~~~~~~~~
link:git-apply.html[git-apply]::
Reads a "diff -up1" or git generated patch file and
applies it to the working tree.
link:git-checkout-cache.html[git-checkout-cache]::
Copy files from the cache to the working directory
link:git-commit-tree.html[git-commit-tree]::
Creates a new commit object
link:git-hash-object.html[git-hash-object]::
Computes the object ID from a file.
link:git-init-db.html[git-init-db]::
Creates an empty git object database
link:git-merge-base.html[git-merge-base]::
Finds as good a common ancestor as possible for a merge
link:git-merge-cache.html[git-merge-cache]::
Runs a merge for files needing merging
link:git-mktag.html[git-mktag]::
Creates a tag object
link:git-read-tree.html[git-read-tree]::
Reads tree information into the directory cache
link:git-update-cache.html[git-update-cache]::
Modifies the index or directory cache
link:git-hash-object.html[git-hash-object]::
Computes the object ID from a file.
link:git-write-tree.html[git-write-tree]::
Creates a tree from the current cache
link:git-pack-objects.html[git-pack-objects]::
Creates a packed archive of objects.
link:git-unpack-objects.html[git-unpack-objects]::
Unpacks objects out of a packed archive.
link:git-prune-packed.html[git-prune-packed]::
Remove extra objects that are already in pack files.
link:git-apply.html[git-apply]::
Reads a "diff -up1" or git generated patch file and
applies it to the working tree.
link:git-read-tree.html[git-read-tree]::
Reads tree information into the directory cache
link:git-unpack-objects.html[git-unpack-objects]::
Unpacks objects out of a packed archive.
link:git-update-cache.html[git-update-cache]::
Modifies the index or directory cache
link:git-write-tree.html[git-write-tree]::
Creates a tree from the current cache
Interrogation commands
~~~~~~~~~~~~~~~~~~~~~~
link:git-cat-file.html[git-cat-file]::
Provide content or type information for repository objects
@ -104,12 +105,12 @@ link:git-diff-cache.html[git-diff-cache]::
link:git-diff-files.html[git-diff-files]::
Compares files in the working tree and the cache
link:git-diff-tree.html[git-diff-tree]::
Compares the content and mode of blobs found via two tree objects
link:git-diff-stages.html[git-diff-stages]::
Compares two "merge stages" in the index file.
link:git-diff-tree.html[git-diff-tree]::
Compares the content and mode of blobs found via two tree objects
link:git-export.html[git-export]::
Exports each commit and a diff against each of its parents
@ -122,8 +123,8 @@ link:git-ls-files.html[git-ls-files]::
link:git-ls-tree.html[git-ls-tree]::
Displays a tree object in human readable form
link:git-merge-cache.html[git-merge-cache]::
Runs a merge for files needing merging
link:git-merge-base.html[git-merge-base]::
Finds as good a common ancestor as possible for a merge
link:git-rev-list.html[git-rev-list]::
Lists commit objects in reverse chronological order
@ -131,6 +132,9 @@ link:git-rev-list.html[git-rev-list]::
link:git-rev-tree.html[git-rev-tree]::
Provides the revision tree for one or more commits
link:git-show-index.html[git-show-index]::
Displays contents of a pack idx file.
link:git-tar-tree.html[git-tar-tree]::
Creates a tar archive of the files in the named tree
@ -140,9 +144,6 @@ link:git-unpack-file.html[git-unpack-file]::
link:git-var.html[git-var]::
Displays a git logical variable
link:git-show-index.html[git-show-index]::
Displays contents of a pack idx file.
link:git-verify-pack.html[git-verify-pack]::
Validates packed GIT archive files
@ -153,173 +154,171 @@ touch the working file set - but in general they don't
Synching repositories
~~~~~~~~~~~~~~~~~~~~~
link:git-clone-script.html[git-clone-script]::
Clones a repository into the current repository (user interface)
link:git-clone-pack.html[git-clone-pack]::
Clones a repository into the current repository (engine
for ssh and local transport)
link:git-fetch-pack.html[git-fetch-pack]::
Updates from a remote repository.
link:git-http-pull.html[git-http-pull]::
Downloads a remote GIT repository via HTTP
link:git-local-pull.html[git-local-pull]::
Duplicates another GIT repository on a local system
link:git-ssh-pull.html[git-ssh-pull]::
Pulls from a remote repository over ssh connection
link:git-send-pack.html[git-send-pack]::
Pushes to a remote repository, intelligently.
link:git-peek-remote.html[git-peek-remote]::
Lists references on a remote repository using upload-pack protocol.
link:git-receive-pack.html[git-receive-pack]::
Invoked by 'git-send-pack' to receive what is pushed to it.
link:git-clone-pack.html[git-clone-pack]::
Clones from a remote repository.
link:git-send-pack.html[git-send-pack]::
Pushes to a remote repository, intelligently.
link:git-fetch-pack.html[git-fetch-pack]::
Updates from a remote repository.
link:git-ssh-pull.html[git-ssh-pull]::
Pulls from a remote repository over ssh connection
link:git-peek-remote.html[git-peek-remote]::
Lists references on a remote repository using upload-pack protocol.
link:git-upload-pack.html[git-upload-pack]::
Invoked by 'git-clone-pack' and 'git-fetch-pack' to push
what are asked for.
link:git-ssh-push.html[git-ssh-push]::
Helper "server-side" program used by git-ssh-pull
link:git-update-server-info.html[git-update-server-info]::
Updates auxiliary information on a dumb server to help
clients discover references and packs on it.
link:git-upload-pack.html[git-upload-pack]::
Invoked by 'git-clone-pack' and 'git-fetch-pack' to push
what are asked for.
Porcelain-ish Commands
----------------------
link:git-revert-script.html[git-revert-script]::
Revert an existing commit.
link:git-rebase-script.html[git-rebase-script]::
Rebase local commits to new upstream head.
link:git-add-script.html[git-add-script]::
Add paths to the index file.
link:git-applymbox.html[git-applymbox]::
Apply patches from a mailbox.
link:git-bisect-script.html[git-bisect-script]::
Find the change that introduced a bug.
link:git-branch-script.html[git-branch-script]::
Create and Show branches.
link:git-whatchanged.html[git-whatchanged]::
Shows commit logs and differences they introduce.
link:git-cherry-pick-script.html[git-cherry-pick-script]::
Cherry-pick the effect of an existing commit.
link:git-log-script.html[git-log-script]::
Shows commit logs.
link:git-shortlog.html[git-shortlog]::
Summarizes 'git log' output.
link:git-status-script.html[git-status-script]::
Shows the working tree status.
link:git-fetch-script.html[git-fetch-script]::
Download from a remote repository via various protocols.
link:git-pull-script.html[git-pull-script]::
Fetch from and merge with a remote repository.
link:git-resolve-script.html[git-resolve-script]::
Merge two commits.
link:git-octopus-script.html[git-octopus-script]::
Merge more than two commits.
link:git-push-script.html[git-push-script]::
Update remote refs along with associated objects.
link:git-clone-script.html[git-clone-script]::
Clones a repository into a new directory.
link:git-commit-script.html[git-commit-script]::
Record changes to the repository.
link:git-show-branch.html[git-show-branch]::
Show branches and their commits.
link:git-fetch-script.html[git-fetch-script]::
Download from a remote repository via various protocols.
link:git-repack-script.html[git-repack-script]::
Pack unpacked objects in a repository.
link:git-rename-script.html[git-rename]::
Rename files and directories.
link:git-log-script.html[git-log-script]::
Shows commit logs.
link:git-ls-remote-script.html[git-ls-remote-script]::
Shows references in a remote or local repository.
link:git-octopus-script.html[git-octopus-script]::
Merge more than two commits.
link:git-pull-script.html[git-pull-script]::
Fetch from and merge with a remote repository.
link:git-push-script.html[git-push-script]::
Update remote refs along with associated objects.
link:git-rebase-script.html[git-rebase-script]::
Rebase local commits to new upstream head.
link:git-rename-script.html[git-rename]::
Rename files and directories.
link:git-repack-script.html[git-repack-script]::
Pack unpacked objects in a repository.
link:git-resolve-script.html[git-resolve-script]::
Merge two commits.
link:git-revert-script.html[git-revert-script]::
Revert an existing commit.
link:git-shortlog.html[git-shortlog]::
Summarizes 'git log' output.
link:git-show-branch.html[git-show-branch]::
Show branches and their commits.
link:git-status-script.html[git-status-script]::
Shows the working tree status.
link:git-verify-tag-script.html[git-verify-tag-script]::
Check the GPG signature of tag.
link:git-whatchanged.html[git-whatchanged]::
Shows commit logs and differences they introduce.
Ancillary Commands
------------------
Manipulators:
link:git-relink-script.html[git-relink-script]::
Hardlink common objects in local repositories.
link:git-apply-patch-script.html[git-apply-patch-script]::
Sample script to apply the diffs from git-diff-*
link:git-applypatch.html[git-applypatch]::
Apply one patch extracted from an e-mail.
link:git-convert-cache.html[git-convert-cache]::
Converts old-style GIT repository
link:git-cvsimport-script.html[git-cvsimport-script]::
Salvage your data out of another SCM people love to hate.
link:git-merge-one-file-script.html[git-merge-one-file-script]::
The standard helper program to use with "git-merge-cache"
link:git-prune-script.html[git-prune-script]::
Prunes all unreachable objects from the object database
link:git-tag-script.html[git-tag-script]::
An example script to create a tag object signed with GPG
link:git-cvsimport-script.html[git-cvsimport-script]::
Salvage your data out of another SCM people love to hate.
Interrogators:
link:git-patch-id.html[git-patch-id]::
Compute unique ID for a patch.
link:git-count-objects-script.html[git-count-objects-script]::
Count unpacked number of objects and their disk consumption.
link:git-cherry.html[git-cherry]::
Find commits not merged upstream.
link:git-diff-helper.html[git-diff-helper]::
Generates patch format output for git-diff-*
link:git-ssh-push.html[git-ssh-push]::
Helper "server-side" program used by git-ssh-pull
link:git-send-email-script.html[git-send-email]::
Send patch e-mails out of "format-patch --mbox" output.
link:git-relink-script.html[git-relink-script]::
Hardlink common objects in local repositories.
link:git-sh-setup-script.html[git-sh-setup-script]::
Common git shell script setup code.
link:git-tag-script.html[git-tag-script]::
An example script to create a tag object signed with GPG
Commands not yet documented
---------------------------
link:git-applymbox.html[git-applymbox]::
git-applymbox.
Interrogators:
link:git-applypatch.html[git-applypatch]::
git-applypatch.
link:git-cherry.html[git-cherry]::
Find commits not merged upstream.
link:git-count-objects-script.html[git-count-objects-script]::
Count unpacked number of objects and their disk consumption.
link:git-diff-helper.html[git-diff-helper]::
Generates patch format output for git-diff-*
link:git-mailinfo.html[git-mailinfo]::
git-mailinfo.
Extracts patch from a single e-mail message.
link:git-mailsplit.html[git-mailsplit]::
git-mailsplit.
link:git-bisect-script.html[git-bisect-script]::
git-bisect-script.
link:git-patch-id.html[git-patch-id]::
Compute unique ID for a patch.
link:git-send-email-script.html[git-send-email]::
Send patch e-mails out of "format-patch --mbox" output.
Commands not yet documented
---------------------------
link:git-build-rev-cache.html[git-build-rev-cache]::
git-build-rev-cache.

View File

@ -38,7 +38,7 @@ ancestry graph looked like this:
So I started from master, made a bunch of edits, and committed:
$ git checkout master
$ cd Documentation; ed git.txt git-apply-patch-script.txt ...
$ cd Documentation; ed git.txt ...
$ cd ..; git add Documentation/*.txt
$ git commit -s -v

View File

@ -57,7 +57,7 @@ SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__
SCRIPTS=git git-apply-patch-script git-merge-one-file-script git-prune-script \
SCRIPTS=git git-merge-one-file-script git-prune-script \
git-pull-script git-tag-script git-resolve-script git-whatchanged \
git-fetch-script git-status-script git-commit-script \
git-log-script git-shortlog git-cvsimport-script git-diff-script \
@ -215,6 +215,7 @@ check:
install: $(PROG) $(SCRIPTS)
$(INSTALL) -m755 -d $(DESTDIR)$(bindir)
$(INSTALL) $(PROG) $(SCRIPTS) $(DESTDIR)$(bindir)
$(INSTALL) git-revert-script $(DESTDIR)$(bindir)/git-cherry-pick-script
$(MAKE) -C templates install
$(MAKE) -C tools install

View File

@ -1,144 +0,0 @@
#!/bin/sh
# Copyright (C) 2005 Junio C Hamano
#
# Applying diff between two trees to the work tree can be
# done with the following single command:
#
# GIT_EXTERNAL_DIFF=git-apply-patch-script git-diff-tree -p $tree1 $tree2
#
case "$#" in
1)
echo >&2 "cannot handle unmerged diff on path $1."
exit 1 ;;
8 | 9)
echo >&2 "cannot handle rename diff between $1 and $8 yet."
exit 1 ;;
esac
name="$1" tmp1="$2" hex1="$3" mode1="$4" tmp2="$5" hex2="$6" mode2="$7"
type1=f
case "$mode1" in
*120???) type1=l ;;
*1007??) mode1=+x ;;
*1006??) mode1=-x ;;
.) type1=- ;;
esac
type2=f
case "$mode2" in
*120???) type2=l ;;
*1007??) mode2=+x ;;
*1006??) mode2=-x ;;
.) type2=- ;;
esac
case "$type1,$type2" in
-,?)
dir=$(dirname "$name")
case "$dir" in '' | .) ;; *) mkdir -p "$dir" ;; esac || {
echo >&2 "cannot create leading path for $name."
exit 1
}
if test -e "$name"
then
echo >&2 "path $name to be created already exists."
exit 1
fi
case "$type2" in
f)
# creating a regular file
cat "$tmp2" >"$name" || {
echo >&2 "cannot create a regular file $name."
exit 1
}
case "$mode2" in
+x)
echo >&2 "created a regular file $name with mode +x."
chmod "$mode2" "$name"
;;
-x)
echo >&2 "created a regular file $name."
;;
esac
;;
l)
# creating a symlink
ln -s "$(cat "$tmp2")" "$name" || {
echo >&2 "cannot create a symbolic link $name."
exit 1
}
echo >&2 "created a symbolic link $name."
;;
*)
echo >&2 "do not know how to create $name of type $type2."
exit 1
esac
git-update-cache --add -- "$name" ;;
?,-)
rm -f "$name" || {
echo >&2 "cannot remove $name"
exit 1
}
echo >&2 "deleted $name."
git-update-cache --remove -- "$name" ;;
l,f|f,l)
echo >&2 "cannot change a regular file $name and a symbolic link $name."
exit 1 ;;
l,l)
# symlink to symlink
current=$(readlink "$name") || {
echo >&2 "cannot read the target of the symbolic link $name."
exit 1
}
original=$(cat "$tmp1")
next=$(cat "$tmp2")
test "$original" != "$current" || {
echo >&2 "cannot apply symbolic link target change ($original->$next) to $name which points to $current."
exit 1
}
if test "$next" != "$current"
then
rm -f "$name" && ln -s "$next" "$name" || {
echo >&2 "cannot create symbolic link $name."
exit 1
}
echo >&2 "changed symbolic target of $name."
git-update-cache -- "$name"
fi ;;
f,f)
# changed
test -e "$name" || {
echo >&2 "regular file $name to be patched does not exist."
exit 1
}
dir=$(dirname "$name")
case "$dir" in '' | .) ;; *) mkdir -p "$dir";; esac || {
echo >&2 "cannot create leading path for $name."
exit 1
}
tmp=.git-apply-patch-$$
trap "rm -f $tmp-*" 0 1 2 3 15
# Be careful, in case "$tmp2" is borrowed path from the work tree
# we are looking at...
diff -u -L "a/$name" -L "b/$name" "$tmp1" "$tmp2" >$tmp-patch
# This will say "patching ..." so we do not say anything outselves.
patch -p1 <$tmp-patch || exit
rm -f $tmp-patch
case "$mode1,$mode2" in
"$mode2,$mode1") ;;
*)
chmod "$mode2" "$name"
echo >&2 "changed mode from $mode1 to $mode2."
;;
esac
git-update-cache -- "$name"
esac

View File

@ -14,19 +14,9 @@ usage="usage: $0 "'[-v] <upstream> [<head>]
Each commit between the fork-point and <head> is examined, and
compared against the change each commit between the fork-point and
<upstream> introduces. If the change does not seem to be in the
upstream, it is shown on the standard output.
The output is intended to be used as:
OLD_HEAD=$(git-rev-parse HEAD)
git-rev-parse upstream >${GIT_DIR-.}/HEAD
git-cherry upstream $OLD_HEAD |
while read commit
do
GIT_EXTERNAL_DIFF=git-apply-patch-script git-diff-tree -p "$commit" &&
git-commit-script -C "$commit"
done
<upstream> introduces. If the change seems to be in the upstream,
it is shown on the standard output with prefix "+". Otherwise
it is shown with prefix "-".
'
case "$1" in -v) verbose=t; shift ;; esac

View File

@ -37,25 +37,32 @@ git-rev-parse --verify "$upstream^0" >"$GIT_DIR/HEAD" || exit
tmp=.rebase-tmp$$
fail=$tmp-fail
trap "rm -rf $tmp-*" 0 1 2 3 15
trap "rm -rf $tmp-*" 1 2 3 15
>$fail
git-cherry $upstream $ours |
while read sign commit
git-cherry -v $upstream $ours |
while read sign commit msg
do
case "$sign" in
-) continue ;;
-)
echo >&2 "* Already applied: $msg"
continue ;;
esac
echo >&2 "* Applying: $msg"
S=`cat "$GIT_DIR/HEAD"` &&
GIT_EXTERNAL_DIFF=git-apply-patch-script git-diff-tree -p $commit &&
git-commit-script -C "$commit" || {
git-cherry-pick-script --replay $commit || {
echo >&2 "* Not applying the patch and continuing."
echo $commit >>$fail
git-read-tree --reset -u $S
git-reset-script --hard $S
}
done
if test -s $fail
then
echo Some commits could not be rebased, check by hand:
cat $fail
echo >&2 Some commits could not be rebased, check by hand:
cat >&2 $fail
echo >&2 "(the same list of commits are found in $tmp)"
exit 1
else
rm -f $fail
fi

View File

@ -5,28 +5,63 @@
. git-sh-setup-script || die "Not a git archive"
no_update_info=
no_update_info= all_into_one= remove_redundant=
while case "$#" in 0) break ;; esac
do
case "$1" in
-n) no_update_info=t ;;
-a) all_into_one=t ;;
-d) remove_redandant=t ;;
*) break ;;
esac
shift
done
rm -f .tmp-pack-*
packname=$(git-rev-list --unpacked --objects $(git-rev-parse --all) |
git-pack-objects --non-empty --incremental .tmp-pack) ||
PACKDIR="$GIT_OBJECT_DIRECTORY/pack"
# There will be more repacking strategies to come...
case ",$all_into_one," in
,,)
rev_list='--unpacked'
rev_parse='--all'
pack_objects='--incremental'
;;
,t,)
rev_list=
rev_parse='--all'
pack_objects=
# This part is a stop-gap until we have proper pack redundancy
# checker.
existing=`cd "$PACKDIR" && \
find . -type f \( -name '*.pack' -o -name '*.idx' \) -print`
;;
esac
name=$(git-rev-list --objects $rev_list $(git-rev-parse $rev_parse) |
git-pack-objects --non-empty $pack_objects .tmp-pack) ||
exit 1
if [ -z "$packname" ]; then
echo Nothing new to pack
if [ -z "$name" ]; then
echo Nothing new to pack.
exit 0
fi
echo "Pack pack-$name created."
mkdir -p "$PACKDIR" || exit
mv .tmp-pack-$name.pack "$PACKDIR/pack-$name.pack" &&
mv .tmp-pack-$name.idx "$PACKDIR/pack-$name.idx" ||
exit
if test "$remove_redandant" = t
then
# We know $existing are all redandant only when
# all-into-one is used.
if test "$all_into_one" != '' && test "$existing" != ''
then
( cd "$PACKDIR" && rm -f $existing )
fi
fi
mkdir -p "$GIT_OBJECT_DIRECTORY/pack" &&
mv .tmp-pack-$packname.pack "$GIT_OBJECT_DIRECTORY/pack/pack-$packname.pack" &&
mv .tmp-pack-$packname.idx "$GIT_OBJECT_DIRECTORY/pack/pack-$packname.idx" &&
case "$no_update_info" in
t) : ;;
*) git-update-server-info ;;

View File

@ -1,37 +1,164 @@
#!/bin/sh
#
# Copyright (c) 2005 Linus Torvalds
# Copyright (c) 2005 Junio C Hamano
#
. git-sh-setup-script || die "Not a git archive"
# We want a clean tree and clean index to be able to revert.
status=$(git status)
case "$status" in
'nothing to commit') ;;
case "$0" in
*-revert-* )
me=revert ;;
*-cherry-pick-* )
me=cherry-pick ;;
esac
usage () {
case "$me" in
cherry-pick)
die "usage git $me [-n] [-r] <commit-ish>"
;;
revert)
die "usage git $me [-n] <commit-ish>"
;;
esac
}
no_commit= replay=
while case "$#" in 0) break ;; esac
do
case "$1" in
-n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\
--no-commi|--no-commit)
no_commit=t
;;
-r|--r|--re|--rep|--repl|--repla|--replay)
replay=t
;;
-*)
usage
;;
*)
break
;;
esac
shift
done
test "$me,$replay" = "revert,t" && usage
case "$no_commit" in
t)
# We do not intend to commit immediately. We just want to
# merge the differences in.
head=$(git-write-tree) ||
die "Your index file is unmerged."
;;
*)
echo "$status"
die "Your working tree is dirty; cannot revert a previous patch." ;;
check_clean_tree || die "Cannot run $me from a dirty tree."
head=$(git-rev-parse --verify HEAD) ||
die "You do not have a valid HEAD"
;;
esac
rev=$(git-rev-parse --verify "$@") &&
commit=$(git-rev-parse --verify "$rev^0") || exit
if git-diff-tree -R -M -p $commit | git-apply --index &&
msg=$(git-rev-list --pretty=oneline --max-count=1 $commit)
then
{
echo "$msg" | sed -e '
s/^[^ ]* /Revert "/
s/$/"/'
echo
echo "This reverts $commit commit."
test "$rev" = "$commit" ||
echo "(original 'git revert' arguments: $@)"
} | git commit -F -
else
# Now why did it fail?
parents=`git-cat-file commit "$commit" 2>/dev/null |
sed -ne '/^$/q;/^parent /p' |
wc -l`
case $parents in
0) die "Cannot revert the root commit nor non commit-ish." ;;
1) die "The patch does not apply." ;;
*) die "Cannot revert a merge commit." ;;
esac
fi
commit=$(git-rev-parse --verify "$rev^0") ||
die "Not a single commit $@"
prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
die "Cannot run $me a root commit"
git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
die "Cannot run $me a multi-parent commit."
# "commit" is an existing commit. We would want to apply
# the difference it introduces since its first parent "prev"
# on top of the current HEAD if we are cherry-pick. Or the
# reverse of it if we are revert.
case "$me" in
revert)
git-rev-list --pretty=oneline --max-count=1 $commit |
sed -e '
s/^[^ ]* /Revert "/
s/$/"/'
echo
echo "This reverts $commit commit."
test "$rev" = "$commit" ||
echo "(original 'git revert' arguments: $@)"
base=$commit next=$prev
;;
cherry-pick)
pick_author_script='
/^author /{
h
s/^author \([^<]*\) <[^>]*> .*$/\1/
s/'\''/'\''\'\'\''/g
s/.*/GIT_AUTHOR_NAME='\''&'\''/p
g
s/^author [^<]* <\([^>]*\)> .*$/\1/
s/'\''/'\''\'\'\''/g
s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
g
s/^author [^<]* <[^>]*> \(.*\)$/\1/
s/'\''/'\''\'\'\''/g
s/.*/GIT_AUTHOR_DATE='\''&'\''/p
q
}'
set_author_env=`git-cat-file commit "$commit" |
sed -ne "$pick_author_script"`
eval "$set_author_env"
export GIT_AUTHOR_NAME
export GIT_AUTHOR_EMAIL
export GIT_AUTHOR_DATE
git-cat-file commit $commit | sed -e '1,/^$/d'
case "$replay" in
'')
echo "(cherry picked from $commit commit)"
test "$rev" = "$commit" ||
echo "(original 'git cherry-pick' arguments: $@)"
;;
esac
base=$prev next=$commit
;;
esac >.msg
# This three way merge is an interesting one. We are at
# $head, and would want to apply the change between $commit
# and $prev on top of us (when reverting), or the change between
# $prev and $commit on top of us (when cherry-picking or replaying).
echo >&2 "First trying simple merge strategy to $me."
git-read-tree -m -u $base $head $next &&
result=$(git-write-tree 2>/dev/null) || {
echo >&2 "Simple $me fails; trying Automatic $me."
git-merge-cache -o git-merge-one-file-script -a || {
echo >&2 "Automatic $me failed. After fixing it up,"
echo >&2 "you can use \"git commit -F .msg\""
case "$me" in
cherry-pick)
echo >&2 "You may choose to use the following when making"
echo >&2 "the commit:"
echo >&2 "$set_author_env"
esac
exit 1
}
result=$(git-write-tree) || exit
}
echo >&2 "Finished one $me."
# If we are cherry-pick, and if the merge did not result in
# hand-editing, we will hit this commit and inherit the original
# author date and name.
# If we are revert, or if our cherry-pick results in a hand merge,
# we had better say that the current user is responsible for that.
case "$no_commit" in
'')
git commit -F .msg
rm -f .msg
;;
esac

View File

@ -11,6 +11,17 @@ die() {
exit 1
}
check_clean_tree() {
dirty1_=`git-update-cache -q --refresh` && {
dirty2_=`git-diff-cache --name-only --cached HEAD`
case "$dirty2_" in '') : ;; *) (exit 1) ;; esac
} || {
echo >&2 "$dirty1_"
echo "$dirty2_" | sed >&2 -e 's/^/modified: /'
(exit 1)
}
}
[ -h "$GIT_DIR/HEAD" ] &&
[ -d "$GIT_DIR/refs" ] &&
[ -d "$GIT_OBJECT_DIRECTORY/00" ]

View File

@ -35,25 +35,25 @@ static struct commit *pop_one_commit(struct commit_list **list_p)
}
struct commit_name {
int head_rev; /* which head's ancestor? */
int generation; /* how many parents away from head_rev */
const char *head_name; /* which head's ancestor? */
int generation; /* how many parents away from head_name */
};
/* Name the commit as nth generation ancestor of head_rev;
/* Name the commit as nth generation ancestor of head_name;
* we count only the first-parent relationship for naming purposes.
*/
static void name_commit(struct commit *commit, int head_rev, int nth)
static void name_commit(struct commit *commit, const char *head_name, int nth)
{
struct commit_name *name;
if (!commit->object.util)
commit->object.util = xmalloc(sizeof(struct commit_name));
name = commit->object.util;
name->head_rev = head_rev;
name->head_name = head_name;
name->generation = nth;
}
/* Parent is the first parent of the commit. We may name it
* as (n+1)th generation ancestor of the same head_rev as
* as (n+1)th generation ancestor of the same head_name as
* commit is nth generation ancestore of, if that generation
* number is better than the name it already has.
*/
@ -65,10 +65,88 @@ static void name_parent(struct commit *commit, struct commit *parent)
return;
if (!parent_name ||
commit_name->generation + 1 < parent_name->generation)
name_commit(parent, commit_name->head_rev,
name_commit(parent, commit_name->head_name,
commit_name->generation + 1);
}
static int name_first_parent_chain(struct commit *c)
{
int i = 0;
while (c) {
struct commit *p;
if (!c->object.util)
break;
if (!c->parents)
break;
p = c->parents->item;
if (!p->object.util) {
name_parent(c, p);
i++;
}
c = p;
}
return i;
}
static void name_commits(struct commit_list *list,
struct commit **rev,
char **ref_name,
int num_rev)
{
struct commit_list *cl;
struct commit *c;
int i;
/* First give names to the given heads */
for (cl = list; cl; cl = cl->next) {
c = cl->item;
if (c->object.util)
continue;
for (i = 0; i < num_rev; i++) {
if (rev[i] == c) {
name_commit(c, ref_name[i], 0);
break;
}
}
}
/* Then commits on the first parent ancestry chain */
do {
i = 0;
for (cl = list; cl; cl = cl->next) {
i += name_first_parent_chain(cl->item);
}
} while (i);
/* Finally, any unnamed commits */
do {
i = 0;
for (cl = list; cl; cl = cl->next) {
struct commit_list *parents;
struct commit_name *n;
int nth;
c = cl->item;
if (!c->object.util)
continue;
n = c->object.util;
parents = c->parents;
nth = 0;
while (parents) {
struct commit *p = parents->item;
char newname[1000];
parents = parents->next;
nth++;
if (p->object.util)
continue;
sprintf(newname, "%s^%d", n->head_name, nth);
name_commit(p, strdup(newname), 0);
i++;
name_first_parent_chain(p);
}
}
} while (i);
}
static int mark_seen(struct commit *commit, struct commit_list **seen_p)
{
if (!commit->object.flags) {
@ -89,7 +167,6 @@ static void join_revs(struct commit_list **list_p,
struct commit_list *parents;
struct commit *commit = pop_one_commit(list_p);
int flags = commit->object.flags & all_mask;
int nth_parent = 0;
int still_interesting = !!interesting(*list_p);
if (!still_interesting && extra < 0)
@ -104,10 +181,6 @@ static void join_revs(struct commit_list **list_p,
struct commit *p = parents->item;
int this_flag = p->object.flags;
parents = parents->next;
nth_parent++;
if (nth_parent == 1)
name_parent(commit, p);
if ((this_flag & flags) == flags)
continue;
parse_commit(p);
@ -119,7 +192,7 @@ static void join_revs(struct commit_list **list_p,
}
}
static void show_one_commit(struct commit *commit, char **head_name)
static void show_one_commit(struct commit *commit)
{
char pretty[128], *cp;
struct commit_name *name = commit->object.util;
@ -129,8 +202,8 @@ static void show_one_commit(struct commit *commit, char **head_name)
cp = pretty + 8;
else
cp = pretty;
if (name && head_name) {
printf("[%s", head_name[name->head_rev]);
if (name && name->head_name) {
printf("[%s", name->head_name);
if (name->generation)
printf("~%d", name->generation);
printf("] ");
@ -237,6 +310,7 @@ int main(int ac, char **av)
struct commit_list *list = NULL, *seen = NULL;
int num_rev, i, extra = 0;
int all_heads = 0, all_tags = 0;
int all_mask, all_revs, shown_merge_point;
char head_path[128];
int head_path_len;
unsigned char head_sha1[20];
@ -293,8 +367,6 @@ int main(int ac, char **av)
die("cannot find commit %s (%s)",
ref_name[num_rev], revkey);
parse_commit(commit);
if (!commit->object.util)
name_commit(commit, num_rev, 0);
mark_seen(commit, &seen);
/* rev#0 uses bit REV_SHIFT, rev#1 uses bit REV_SHIFT+1,
@ -328,30 +400,44 @@ int main(int ac, char **av)
for (j = 0; j < i; j++)
putchar(' ');
printf("%c [%s] ", is_head ? '*' : '!', ref_name[i]);
show_one_commit(rev[i], NULL);
show_one_commit(rev[i]);
}
for (i = 0; i < num_rev; i++)
putchar('-');
putchar('\n');
}
label = ref_name;
/* Sort topologically */
sort_in_topological_order(&seen);
/* Give names to commits */
name_commits(seen, rev, ref_name, num_rev);
all_mask = ((1u << (REV_SHIFT + num_rev)) - 1);
all_revs = all_mask & ~((1u << REV_SHIFT) - 1);
shown_merge_point = 0;
while (seen) {
struct commit *commit = pop_one_commit(&seen);
int this_flag = commit->object.flags;
int is_merge_point = (this_flag & all_revs) == all_revs;
static char *obvious[] = { "" };
if ((this_flag & UNINTERESTING) && (--extra < 0))
break;
if (is_merge_point)
shown_merge_point = 1;
if (1 < num_rev) {
for (i = 0; i < num_rev; i++)
putchar((this_flag & (1u << (i + REV_SHIFT)))
? '+' : ' ');
putchar(' ');
}
show_one_commit(commit, label);
show_one_commit(commit);
if (num_rev == 1)
label = obvious;
if (shown_merge_point && is_merge_point)
if (--extra < 0)
break;
}
return 0;
}

View File

@ -9,7 +9,7 @@
## You give it a mbox-format collection of emails, and it will try to
## apply them to the kernel using "applypatch"
##
## applymbox [ -k ] [ -q ] (-c .dotest/msg-number | mail_archive) [Signoff_file]"
## applymbox [-u] [-k] [-q] (-c .dotest/msg-number | mail_archive) [Signoff_file]"
##
## The patch application may fail in the middle. In which case:
## (1) look at .dotest/patch and fix it up to apply
@ -20,10 +20,16 @@
. git-sh-setup-script || die "Not a git archive"
keep_subject= query_apply= continue= resume=t
usage () {
echo >&2 "applymbox [-u] [-k] [-q] (-c .dotest/<num> | mbox) [signoff]"
exit 1
}
keep_subject= query_apply= continue= utf8= resume=t
while case "$#" in 0) break ;; esac
do
case "$1" in
-u) utf8=-u ;;
-k) keep_subject=-k ;;
-q) query_apply=t ;;
-c) continue="$2"; resume=f; shift ;;
@ -64,7 +70,7 @@ do
f,$i) resume=t;;
f,*) continue;;
*)
git-mailinfo $keep_subject \
git-mailinfo $keep_subject $utf8 \
.dotest/msg .dotest/patch <$i >.dotest/info || exit 1
git-stripspace < .dotest/msg > .dotest/msg-clean
;;

View File

@ -2,20 +2,32 @@
* Another stupid program, this one parsing the headers of an
* email to figure out authorship and subject
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <iconv.h>
static FILE *cmitmsg, *patchfile;
static int keep_subject = 0;
static int metainfo_utf8 = 0;
static char line[1000];
static char date[1000];
static char name[1000];
static char email[1000];
static char subject[1000];
static enum {
TE_DONTCARE, TE_QP, TE_BASE64,
} transfer_encoding;
static char charset[256];
static char multipart_boundary[1000];
static int multipart_boundary_len;
static int patch_lines = 0;
static char *sanity_check(char *name, char *email)
{
int len = strlen(name);
@ -40,67 +52,188 @@ static int handle_from(char *line)
if (*email && strchr(at+1, '@'))
return 0;
/* Pick up the string around '@', possibly delimited with <>
* pair; that is the email part. White them out while copying.
*/
while (at > line) {
char c = at[-1];
if (isspace(c) || c == '<')
if (isspace(c))
break;
if (c == '<') {
at[-1] = ' ';
break;
}
at--;
}
dst = email;
for (;;) {
unsigned char c = *at;
if (!c || c == '>' || isspace(c))
if (!c || c == '>' || isspace(c)) {
if (c == '>')
*at = ' ';
break;
}
*at++ = ' ';
*dst++ = c;
}
*dst++ = 0;
/* The remainder is name. It could be "John Doe <john.doe@xz>"
* or "john.doe@xz (John Doe)", but we have whited out the
* email part, so trim from both ends, possibly removing
* the () pair at the end.
*/
at = line + strlen(line);
while (at > line) {
unsigned char c = *--at;
if (isalnum(c))
if (!isspace(c)) {
at[(c == ')') ? 0 : 1] = 0;
break;
*at = 0;
}
}
at = line;
for (;;) {
unsigned char c = *at;
if (!c)
break;
if (isalnum(c))
if (!c || !isspace(c)) {
if (c == '(')
at++;
break;
}
at++;
}
at = sanity_check(at, email);
strcpy(name, at);
return 1;
}
static void handle_date(char *line)
static int handle_date(char *line)
{
strcpy(date, line);
return 0;
}
static void handle_subject(char *line)
static int handle_subject(char *line)
{
strcpy(subject, line);
return 0;
}
static void check_line(char *line, int len)
/* NOTE NOTE NOTE. We do not claim we do full MIME. We just attempt
* to have enough heuristics to grok MIME encoded patches often found
* on our mailing lists. For example, we do not even treat header lines
* case insensitively.
*/
static int slurp_attr(const char *line, const char *name, char *attr)
{
if (!memcmp(line, "From:", 5) && isspace(line[5]))
handle_from(line+6);
else if (!memcmp(line, "Date:", 5) && isspace(line[5]))
handle_date(line+6);
else if (!memcmp(line, "Subject:", 8) && isspace(line[8]))
handle_subject(line+9);
char *ends, *ap = strcasestr(line, name);
size_t sz;
if (!ap) {
*attr = 0;
return 0;
}
ap += strlen(name);
if (*ap == '"') {
ap++;
ends = "\"";
}
else
ends = "; \t";
sz = strcspn(ap, ends);
memcpy(attr, ap, sz);
attr[sz] = 0;
return 1;
}
static char * cleanup_subject(char *subject)
static int handle_subcontent_type(char *line)
{
/* We do not want to mess with boundary. Note that we do not
* handle nested multipart.
*/
slurp_attr(line, "charset=", charset);
if (*charset) {
int i, c;
for (i = 0; (c = charset[i]) != 0; i++)
charset[i] = tolower(c);
}
return 0;
}
static int handle_content_type(char *line)
{
*multipart_boundary = 0;
if (slurp_attr(line, "boundary=", multipart_boundary + 2)) {
memcpy(multipart_boundary, "--", 2);
multipart_boundary_len = strlen(multipart_boundary);
}
slurp_attr(line, "charset=", charset);
return 0;
}
static int handle_content_transfer_encoding(char *line)
{
if (strcasestr(line, "base64"))
transfer_encoding = TE_BASE64;
else if (strcasestr(line, "quoted-printable"))
transfer_encoding = TE_QP;
else
transfer_encoding = TE_DONTCARE;
return 0;
}
static int is_multipart_boundary(const char *line)
{
return (!memcmp(line, multipart_boundary, multipart_boundary_len));
}
static int eatspace(char *line)
{
int len = strlen(line);
while (len > 0 && isspace(line[len-1]))
line[--len] = 0;
return len;
}
#define SEEN_FROM 01
#define SEEN_DATE 02
#define SEEN_SUBJECT 04
/* First lines of body can have From:, Date:, and Subject: */
static int handle_inbody_header(int *seen, char *line)
{
if (!memcmp("From:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_FROM) && handle_from(line+6)) {
*seen |= SEEN_FROM;
return 1;
}
}
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
if (!(*seen & SEEN_DATE)) {
handle_date(line+6);
*seen |= SEEN_DATE;
return 1;
}
}
if (!memcmp("Subject:", line, 8) && isspace(line[8])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line+9);
*seen |= SEEN_SUBJECT;
return 1;
}
}
if (!memcmp("[PATCH]", line, 7) && isspace(line[7])) {
if (!(*seen & SEEN_SUBJECT)) {
handle_subject(line);
*seen |= SEEN_SUBJECT;
return 1;
}
}
return 0;
}
static char *cleanup_subject(char *subject)
{
if (keep_subject)
return subject;
@ -153,69 +286,53 @@ static void cleanup_space(char *buf)
}
}
static void handle_rest(void)
typedef int (*header_fn_t)(char *);
struct header_def {
const char *name;
header_fn_t func;
int namelen;
};
static void check_header(char *line, int len, struct header_def *header)
{
FILE *out = cmitmsg;
char *sub = cleanup_subject(subject);
cleanup_space(name);
cleanup_space(date);
cleanup_space(email);
cleanup_space(sub);
printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n", name, email, sub, date);
int i;
do {
if (!memcmp("diff -", line, 6) ||
!memcmp("---", line, 3) ||
!memcmp("Index: ", line, 7))
out = patchfile;
fputs(line, out);
} while (fgets(line, sizeof(line), stdin) != NULL);
if (out == cmitmsg) {
fprintf(stderr, "No patch found\n");
exit(1);
if (header[0].namelen <= 0) {
for (i = 0; header[i].name; i++)
header[i].namelen = strlen(header[i].name);
}
for (i = 0; header[i].name; i++) {
int len = header[i].namelen;
if (!strncasecmp(line, header[i].name, len) &&
line[len] == ':' && isspace(line[len + 1])) {
header[i].func(line + len + 2);
break;
}
}
fclose(cmitmsg);
fclose(patchfile);
}
static int eatspace(char *line)
static void check_subheader_line(char *line, int len)
{
int len = strlen(line);
while (len > 0 && isspace(line[len-1]))
line[--len] = 0;
return len;
static struct header_def header[] = {
{ "Content-Type", handle_subcontent_type },
{ "Content-Transfer-Encoding",
handle_content_transfer_encoding },
{ NULL },
};
check_header(line, len, header);
}
static void handle_body(void)
static void check_header_line(char *line, int len)
{
int has_from = 0;
int has_date = 0;
/* First lines of body can have From: and Date: */
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = eatspace(line);
if (!len)
continue;
if (!memcmp("From:", line, 5) && isspace(line[5])) {
if (!has_from && handle_from(line+6)) {
has_from = 1;
continue;
}
}
if (!memcmp("Date:", line, 5) && isspace(line[5])) {
if (!has_date) {
handle_date(line+6);
has_date = 1;
continue;
}
}
line[len] = '\n';
handle_rest();
break;
}
static struct header_def header[] = {
{ "From", handle_from },
{ "Date", handle_date },
{ "Subject", handle_subject },
{ "Content-Type", handle_content_type },
{ "Content-Transfer-Encoding",
handle_content_transfer_encoding },
{ NULL },
};
check_header(line, len, header);
}
static int read_one_header_line(char *line, int sz, FILE *in)
@ -239,23 +356,365 @@ static int read_one_header_line(char *line, int sz, FILE *in)
return ofs;
}
static void usage(void)
static unsigned hexval(int c)
{
fprintf(stderr, "mailinfo msg-file patch-file < email\n");
exit(1);
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return ~0;
}
static int decode_q_segment(char *in, char *ot, char *ep)
{
int c;
while ((c = *in++) != 0 && (in <= ep)) {
if (c == '=') {
int d = *in++;
if (d == '\n' || !d)
break; /* drop trailing newline */
*ot++ = ((hexval(d) << 4) | hexval(*in++));
}
else
*ot++ = c;
}
*ot = 0;
return 0;
}
static int decode_b_segment(char *in, char *ot, char *ep)
{
/* Decode in..ep, possibly in-place to ot */
int c, pos = 0, acc = 0;
while ((c = *in++) != 0 && (in <= ep)) {
if (c == '+')
c = 62;
else if (c == '/')
c = 63;
else if ('A' <= c && c <= 'Z')
c -= 'A';
else if ('a' <= c && c <= 'z')
c -= 'a' - 26;
else if ('0' <= c && c <= '9')
c -= '0' - 52;
else if (c == '=') {
/* padding is almost like (c == 0), except we do
* not output NUL resulting only from it;
* for now we just trust the data.
*/
c = 0;
}
else
continue; /* garbage */
switch (pos++) {
case 0:
acc = (c << 2);
break;
case 1:
*ot++ = (acc | (c >> 4));
acc = (c & 15) << 4;
break;
case 2:
*ot++ = (acc | (c >> 2));
acc = (c & 3) << 6;
break;
case 3:
*ot++ = (acc | c);
acc = pos = 0;
break;
}
}
*ot = 0;
return 0;
}
static void convert_to_utf8(char *line, char *charset)
{
if (*charset) {
char *in, *out;
size_t insize, outsize, nrc;
char outbuf[4096]; /* cheat */
iconv_t conv = iconv_open("utf-8", charset);
if (conv == (iconv_t) -1) {
fprintf(stderr, "cannot convert from %s to utf-8\n",
charset);
*charset = 0;
return;
}
in = line;
insize = strlen(in);
out = outbuf;
outsize = sizeof(outbuf);
nrc = iconv(conv, &in, &insize, &out, &outsize);
iconv_close(conv);
if (nrc == (size_t) -1)
return;
*out = 0;
strcpy(line, outbuf);
}
}
static void decode_header_bq(char *it)
{
char *in, *out, *ep, *cp, *sp;
char outbuf[1000];
in = it;
out = outbuf;
while ((ep = strstr(in, "=?")) != NULL) {
int sz, encoding;
char charset_q[256], piecebuf[256];
if (in != ep) {
sz = ep - in;
memcpy(out, in, sz);
out += sz;
in += sz;
}
/* E.g.
* ep : "=?iso-2022-jp?B?GyR...?= foo"
* ep : "=?ISO-8859-1?Q?Foo=FCbar?= baz"
*/
ep += 2;
cp = strchr(ep, '?');
if (!cp)
return; /* no munging */
for (sp = ep; sp < cp; sp++)
charset_q[sp - ep] = tolower(*sp);
charset_q[cp - ep] = 0;
encoding = cp[1];
if (!encoding || cp[2] != '?')
return; /* no munging */
ep = strstr(cp + 3, "?=");
if (!ep)
return; /* no munging */
switch (tolower(encoding)) {
default:
return; /* no munging */
case 'b':
sz = decode_b_segment(cp + 3, piecebuf, ep);
break;
case 'q':
sz = decode_q_segment(cp + 3, piecebuf, ep);
break;
}
if (sz < 0)
return;
if (metainfo_utf8)
convert_to_utf8(piecebuf, charset_q);
strcpy(out, piecebuf);
out += strlen(out);
in = ep + 2;
}
strcpy(out, in);
strcpy(it, outbuf);
}
static void decode_transfer_encoding(char *line)
{
char *ep;
switch (transfer_encoding) {
case TE_QP:
ep = line + strlen(line);
decode_q_segment(line, line, ep);
break;
case TE_BASE64:
ep = line + strlen(line);
decode_b_segment(line, line, ep);
break;
case TE_DONTCARE:
break;
}
}
static void handle_info(void)
{
char *sub;
static int done_info = 0;
if (done_info)
return;
done_info = 1;
sub = cleanup_subject(subject);
cleanup_space(name);
cleanup_space(date);
cleanup_space(email);
cleanup_space(sub);
/* Unwrap inline B and Q encoding, and optionally
* normalize the meta information to utf8.
*/
decode_header_bq(name);
decode_header_bq(date);
decode_header_bq(email);
decode_header_bq(sub);
printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
name, email, sub, date);
}
/* We are inside message body and have read line[] already.
* Spit out the commit log.
*/
static int handle_commit_msg(void)
{
if (!cmitmsg)
return 0;
do {
if (!memcmp("diff -", line, 6) ||
!memcmp("---", line, 3) ||
!memcmp("Index: ", line, 7))
break;
if ((multipart_boundary[0] && is_multipart_boundary(line))) {
/* We come here when the first part had only
* the commit message without any patch. We
* pretend we have not seen this line yet, and
* go back to the loop.
*/
return 1;
}
/* Unwrap transfer encoding and optionally
* normalize the log message to UTF-8.
*/
decode_transfer_encoding(line);
if (metainfo_utf8)
convert_to_utf8(line, charset);
fputs(line, cmitmsg);
} while (fgets(line, sizeof(line), stdin) != NULL);
fclose(cmitmsg);
cmitmsg = NULL;
return 0;
}
/* We have done the commit message and have the first
* line of the patch in line[].
*/
static void handle_patch(void)
{
do {
if (multipart_boundary[0] && is_multipart_boundary(line))
break;
/* Only unwrap transfer encoding but otherwise do not
* do anything. We do *NOT* want UTF-8 conversion
* here; we are dealing with the user payload.
*/
decode_transfer_encoding(line);
fputs(line, patchfile);
patch_lines++;
} while (fgets(line, sizeof(line), stdin) != NULL);
}
/* multipart boundary and transfer encoding are set up for us, and we
* are at the end of the sub header. do equivalent of handle_body up
* to the next boundary without closing patchfile --- we will expect
* that the first part to contain commit message and a patch, and
* handle other parts as pure patches.
*/
static int handle_multipart_one_part(void)
{
int seen = 0;
int n = 0;
int len;
while (fgets(line, sizeof(line), stdin) != NULL) {
again:
len = eatspace(line);
n++;
if (!len)
continue;
if (is_multipart_boundary(line))
break;
if (0 <= seen && handle_inbody_header(&seen, line))
continue;
seen = -1; /* no more inbody headers */
line[len] = '\n';
handle_info();
if (handle_commit_msg())
goto again;
handle_patch();
break;
}
if (n == 0)
return -1;
return 0;
}
static void handle_multipart_body(void)
{
int part_num = 0;
/* Skip up to the first boundary */
while (fgets(line, sizeof(line), stdin) != NULL)
if (is_multipart_boundary(line)) {
part_num = 1;
break;
}
if (!part_num)
return;
/* We are on boundary line. Start slurping the subhead. */
while (1) {
int len = read_one_header_line(line, sizeof(line), stdin);
if (!len) {
if (handle_multipart_one_part() < 0)
return;
}
else
check_subheader_line(line, len);
}
fclose(patchfile);
if (!patch_lines) {
fprintf(stderr, "No patch found\n");
exit(1);
}
}
/* Non multipart message */
static void handle_body(void)
{
int seen = 0;
while (fgets(line, sizeof(line), stdin) != NULL) {
int len = eatspace(line);
if (!len)
continue;
if (0 <= seen && handle_inbody_header(&seen, line))
continue;
seen = -1; /* no more inbody headers */
line[len] = '\n';
handle_info();
handle_commit_msg();
handle_patch();
break;
}
fclose(patchfile);
if (!patch_lines) {
fprintf(stderr, "No patch found\n");
exit(1);
}
}
static const char mailinfo_usage[] =
"git-mailinfo [-k] msg patch <mail >info";
int main(int argc, char ** argv)
"git-mailinfo [-k] [-u] msg patch <mail >info";
static void usage(void) {
fprintf(stderr, "%s\n", mailinfo_usage);
exit(1);
}
int main(int argc, char **argv)
{
while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "-k"))
keep_subject = 1;
else {
fprintf(stderr, "usage: %s\n", mailinfo_usage);
exit(1);
}
else if (!strcmp(argv[1], "-u"))
metainfo_utf8 = 1;
else
usage();
argc--; argv++;
}
@ -274,10 +733,13 @@ int main(int argc, char ** argv)
while (1) {
int len = read_one_header_line(line, sizeof(line), stdin);
if (!len) {
handle_body();
if (multipart_boundary[0])
handle_multipart_body();
else
handle_body();
break;
}
check_line(line, len);
check_header_line(line, len);
}
return 0;
}