зеркало из https://github.com/microsoft/git.git
Merge refs/heads/master from .
This commit is contained in:
Коммит
7a03433742
|
@ -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
|
||||
|
|
@ -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
|
||||
------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
||||
--------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
------
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
3
Makefile
3
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
16
git-cherry
16
git-cherry
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ;;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" ]
|
||||
|
|
130
show-branch.c
130
show-branch.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
;;
|
||||
|
|
632
tools/mailinfo.c
632
tools/mailinfo.c
|
@ -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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче