-GIT v1.5.1.1 Release Notes (draft)
+GIT v1.5.1.1 Release Notes
==========================
Fixes since v1.5.1
- The documentation for cvsimport has been majorly improved.
+ - "git-show-ref --exclude-existing" was documented.
+
* Bugfixes
+ - The implementation of -p option in "git cvsexportcommit" had
+ the meaning of -C (context reduction) option wrong, and
+ loosened the context requirements when it was told to be
+ strict.
+
+ - "git cvsserver" did not behave like the real cvsserver when
+ client side removed a file from the working tree without
+ doing anything else on the path. In such a case, it should
+ restore it from the checked out revision.
+
+ - "git fsck" issued an alarming error message on detached
+ HEAD. It is not an error since at least 1.5.0.
+
- "git send-email" produced of References header of unbounded length;
fixed this with line-folding.
- gitweb did not show type-changing patch correctly in the
blobdiff view.
-* Performance Tweaks
+ - git-svn did not error out with incorrect command line options.
+
+ - git-svn fell into an infinite loop when insanely long commit
+ message was found.
---
-exec >/var/tmp/1
-O=v1.5.1-26-ge94a4f6
-echo O=`git describe refs/heads/maint`
-git shortlog --no-merges $O..refs/heads/maint
+ - git-svn dcommit and rebase was confused by patches that were
+ merged from another branch that is managed by git-svn.
- provide additional information (which is unsuitable for
the commit message) between the "---" and the diffstat
- send the patch to the list _and_ the maintainer
+ - if you change, add, or remove a command line option or
+ make some other user interface change, the associated
+ documentation should be updated as well.
Long version:
--- /dev/null
+-b::
+ Show blank SHA-1 for boundary commits. This can also
+ be controlled via the `blame.blankboundary` config option.
+
+--root::
+ Do not treat root commits as boundaries. This can also be
+ controlled via the `blame.showroot` config option.
+
+--show-stats::
+ Include additional statistics at the end of blame output.
+
+-L n,m::
+ Annotate only the specified line range (lines count from 1).
+
+-l::
+ Show long rev (Default: off).
+
+-t::
+ Show raw timestamp (Default: off).
+
+-S <revs-file>::
+ Use revs from revs-file instead of calling gitlink:git-rev-list[1].
+
+-p, --porcelain::
+ Show in a format designed for machine consumption.
+
+--incremental::
+ Show the result incrementally in a format designed for
+ machine consumption.
+
+--contents <file>::
+ When <rev> is not specified, the command annotates the
+ changes starting backwards from the working tree copy.
+ This flag makes the command pretend as if the working
+ tree copy has the contents of he named file (specify
+ `-` to make the command read from the standard input).
+
+-M|<num>|::
+ Detect moving lines in the file as well. When a commit
+ moves a block of lines in a file (e.g. the original file
+ has A and then B, and the commit changes it to B and
+ then A), traditional 'blame' algorithm typically blames
+ the lines that were moved up (i.e. B) to the parent and
+ assigns blame to the lines that were moved down (i.e. A)
+ to the child commit. With this option, both groups of lines
+ are blamed on the parent.
+
+ <num> is optional but it is the lower bound on the number of
+ alphanumeric characters that git must detect as moving
+ within a file for it to associate those lines with the parent
+ commit.
+
+-C|<num>|::
+ In addition to `-M`, detect lines copied from other
+ files that were modified in the same commit. This is
+ useful when you reorganize your program and move code
+ around across files. When this option is given twice,
+ the command looks for copies from all other files in the
+ parent for the commit that creates the file in addition.
+
+ <num> is optional but it is the lower bound on the number of
+ alphanumeric characters that git must detect as moving
+ between files for it to associate those lines with the parent
+ commit.
+
+-h, --help::
+ Show help message.
the working copy are ignored; useful on broken filesystems like FAT.
See gitlink:git-update-index[1]. True by default.
+core.autocrlf::
+ If true, makes git convert `CRLF` at the end of lines in text files to
+ `LF` when reading from the filesystem, and convert in reverse when
+ writing to the filesystem. The variable can be set to
+ 'input', in which case the conversion happens only while
+ reading from the filesystem but files are written out with
+ `LF` at the end of lines. Currently, which paths to consider
+ "text" (i.e. be subjected to the autocrlf mechanism) is
+ decided purely based on the contents.
+
core.symlinks::
If false, symbolic links are checked out as small plain files that
contain the link text. gitlink:git-update-index[1] and
The default is 15 days. See gitlink:git-rerere[1].
gitcvs.enabled::
- Whether the cvs pserver interface is enabled for this repository.
+ Whether the cvs server interface is enabled for this repository.
See gitlink:git-cvsserver[1].
gitcvs.logfile::
- Path to a log file where the cvs pserver interface well... logs
+ Path to a log file where the cvs server interface well... logs
various stuff. See gitlink:git-cvsserver[1].
+gitcvs.allbinary::
+ If true, all files are sent to the client in mode '-kb'. This
+ causes the client to treat all files as binary files which suppresses
+ any newline munging it otherwise might do. A work-around for the
+ fact that there is no way yet to set single files to mode '-kb'.
+ See gitlink:git-cvsserver[1].
+
http.sslVerify::
Whether to verify the SSL certificate when fetching or pushing
over HTTPS. Can be overridden by the 'GIT_SSL_NO_VERIFY' environment
http.noEPSV::
A boolean which disables using of EPSV ftp command by curl.
- This can helpful with some "poor" ftp servers which doesn't
+ This can helpful with some "poor" ftp servers which don't
support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
environment variable. Default is false (curl will use EPSV).
The list of mailbox files to read patches from. If you do not
supply this argument, reads from the standard input.
---signoff::
+-s, --signoff::
Add `Signed-off-by:` line to the commit message, using
the committer identity of yourself.
---dotest=<dir>::
+-d=<dir>, --dotest=<dir>::
Instead of `.dotest` directory, use <dir> as a working
area to store extracted patches.
---keep::
+-k, --keep::
Pass `-k` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
---utf8::
+-u, --utf8::
Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
The proposed commit log message taken from the e-mail
are re-coded into UTF-8 encoding (configuration variable
default. You could use `--no-utf8` to override this.
--no-utf8::
- Do not pass `-u` flag to `git-mailinfo` (see
+ Pass `-n` flag to `git-mailinfo` (see
gitlink:git-mailinfo[1]).
---binary::
+-b, --binary::
Pass `--allow-binary-replacement` flag to `git-apply`
(see gitlink:git-apply[1]).
---3way::
+-3, --3way::
When the patch does not apply cleanly, fall back on
3-way merge, if the patch records the identity of blobs
it is supposed to apply to, and we have those blobs
These flags are passed to the `git-apply` program that applies
the patch.
---interactive::
+-i, --interactive::
Run interactively, just like git-applymbox.
---resolved::
+-r, --resolved::
After a patch failure (e.g. attempting to apply
conflicting patch), the user has applied it by hand and
the index file stores the result of the application.
extracted from the e-mail message and the current index
file, and continue.
+--resolvemsg=<msg>::
+ When a patch failure occurs, <msg> will be printed
+ to the screen before exiting. This overrides the
+ standard message informing you to use `--resolved`
+ or `--skip` to handle the failure. This is solely
+ for internal use between `git-rebase` and `git-am`.
+
DISCUSSION
----------
OPTIONS
-------
--l, --long::
- Show long rev (Defaults off).
-
--t, --time::
- Show raw timestamp (Defaults off).
-
--r, --rename::
- Follow renames (Defaults on).
-
--S, --rev-file <revs-file>::
- Use revs from revs-file instead of calling git-rev-list.
-
--h, --help::
- Show help message.
+include::blame-options.txt[]
SEE ALSO
--------
SYNOPSIS
--------
[verse]
-'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply]
- [--no-add] [--index-info] [--allow-binary-replacement | --binary]
- [-R | --reverse] [--reject] [-z] [-pNUM] [-CNUM] [--inaccurate-eof]
- [--whitespace=<nowarn|warn|error|error-all|strip>] [--exclude=PATH]
- [--cached] [--verbose] [<patch>...]
+'git-apply' [--stat] [--numstat] [--summary] [--check] [--index]
+ [--apply] [--no-add] [--index-info] [-R | --reverse]
+ [--allow-binary-replacement | --binary] [--reject] [-z]
+ [-pNUM] [-CNUM] [--inaccurate-eof] [--cached]
+ [--whitespace=<nowarn|warn|error|error-all|strip>]
+ [--exclude=PATH] [--verbose] [<patch>...]
DESCRIPTION
-----------
correctly. This option adds support for applying such patches by
working around this bug.
---verbose::
+-v, --verbose::
Report progress to stderr. By default, only a message about the
current patch being applied will be printed. This option will cause
additional information to be reported.
and the current tree.
-u::
- The commit log message, author name and author email are
- taken from the e-mail, and after minimally decoding MIME
- transfer encoding, re-coded in UTF-8 by transliterating
- them. This used to be optional but now it is the default.
+ Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
+ The proposed commit log message taken from the e-mail
+ are re-coded into UTF-8 encoding (configuration variable
+ `i18n.commitencoding` can be used to specify project's
+ preferred encoding if it is not UTF-8). This used to be
+ optional but now it is the default.
+
Note that the patch is always used as-is without charset
conversion, even with this flag.
+-n::
+ Pass `-n` flag to `git-mailinfo` (see
+ gitlink:git-mailinfo[1]).
+
-c .dotest/<num>::
When the patch contained in an e-mail does not cleanly
apply, the command exits with an error message. The
SYNOPSIS
--------
[verse]
-'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m] [-S <revs-file>]
- [-M] [-C] [-C] [--since=<date>] [<rev> | --contents <file>] [--] <file>
+'git-blame' [-c] [-l] [-t] [-f] [-n] [-p] [--incremental] [-L n,m]
+ [-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
+ [<rev> | --contents <file>] [--] <file>
DESCRIPTION
-----------
OPTIONS
-------
--c, --compatibility::
- Use the same output mode as gitlink:git-annotate[1] (Default: off).
-
--L n,m::
- Annotate only the specified line range (lines count from 1).
-
--l, --long::
- Show long rev (Default: off).
+include::blame-options.txt[]
--t, --time::
- Show raw timestamp (Default: off).
+-c::
+ Use the same output mode as gitlink:git-annotate[1] (Default: off).
--S, --rev-file <revs-file>::
- Use revs from revs-file instead of calling gitlink:git-rev-list[1].
+--score-debug::
+ Include debugging information related to the movement of
+ lines between files (see `-C`) and lines moved within a
+ file (see `-M`). The first number listed is the score.
+ This is the number of alphanumeric characters detected
+ to be moved between or within files. This must be above
+ a certain threshold for git-blame to consider those lines
+ of code to have been moved.
-f, --show-name::
Show filename in the original commit. By default
-n, --show-number::
Show line number in the original commit (Default: off).
--p, --porcelain::
- Show in a format designed for machine consumption.
-
---incremental::
- Show the result incrementally in a format designed for
- machine consumption.
-
---contents <file>::
- When <rev> is not specified, the command annotates the
- changes starting backwards from the working tree copy.
- This flag makes the command pretend as if the working
- tree copy has the contents of he named file (specify
- `-` to make the command read from the standard input).
-
--M::
- Detect moving lines in the file as well. When a commit
- moves a block of lines in a file (e.g. the original file
- has A and then B, and the commit changes it to B and
- then A), traditional 'blame' algorithm typically blames
- the lines that were moved up (i.e. B) to the parent and
- assigns blame to the lines that were moved down (i.e. A)
- to the child commit. With this option, both groups of
- lines are blamed on the parent.
-
--C::
- In addition to `-M`, detect lines copied from other
- files that were modified in the same commit. This is
- useful when you reorganize your program and move code
- around across files. When this option is given twice,
- the command looks for copies from all other files in the
- parent for the commit that creates the file in addition.
-
--h, --help::
- Show help message.
-
-
THE PORCELAIN FORMAT
--------------------
When <paths> are given, this command does *not* switch
branches. It updates the named paths in the working tree from
-the index file (i.e. it runs `git-checkout-index -f -u`), or a
-named commit. In
-this case, `-f` and `-b` options are meaningless and giving
+the index file (i.e. it runs `git-checkout-index -f -u`), or
+from a named commit. In
+this case, the `-f` and `-b` options are meaningless and giving
either of them results in an error. <tree-ish> argument can be
used to specify a specific tree-ish (i.e. commit, tag or tree)
to update the index for the given paths before updating the
Quiet, supress feedback messages.
-f::
- Force a re-read of everything.
+ Proceed even if the index or the working tree differs
+ from HEAD. This is used to throw away local changes.
-b::
Create a new branch named <new_branch> and start it at
--track::
When -b is given and a branch is created off a remote branch,
- setup so that git-pull will automatically retrieve data from
- the remote branch.
+ set up configuration so that git-pull will automatically
+ retrieve data from the remote branch. Set the
+ branch.autosetupmerge configuration variable to true if you
+ want git-checkout and git-branch to always behave as if
+ '--track' were given.
--no-track::
When -b is given and a branch is created off a remote branch,
- force that git-pull will automatically retrieve data from
- the remote branch independent of the configuration settings.
+ set up configuration so that git-pull will not retrieve data
+ from the remote branch, ignoring the branch.autosetupmerge
+ configuration variable.
-l::
Create the new branch's ref log. This activates recording of
--------
[verse]
'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--thread]
- [--attach[=<boundary>] | --inline[=<boundary>]]
- [-s | --signoff] [<common diff options>] [--start-number <n>]
- [--in-reply-to=Message-Id] [--suffix=.<sfx>]
- [--ignore-if-in-upstream]
- <since>[..<until>]
+ [--attach[=<boundary>] | --inline[=<boundary>]]
+ [-s | --signoff] [<common diff options>] [--start-number <n>]
+ [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+ [--ignore-if-in-upstream]
+ [--subject-prefix=Subject-Prefix]
+ <since>[..<until>]
DESCRIPTION
-----------
patches being generated, and any patch that matches is
ignored.
+--subject-prefix=<Subject-Prefix>::
+ Instead of the standard '[PATCH]' prefix in the subject
+ line, instead use '[<Subject-Prefix>]'. This
+ allows for useful naming of a patch series, and can be
+ combined with the --numbered option.
+
--suffix=.<sfx>::
Instead of using `.patch` as the suffix for generated
filenames, use specifed suffix. A common alternative is
-p::
Show the change the commit introduces in a patch form.
+-g, \--walk-reflogs::
+ Show commits as they were recorded in the reflog. The log contains
+ a record about how the tip of a reference was changed.
+ See also gitlink:git-reflog[1].
+
<paths>...::
Show only commits that affect the specified paths.
DESCRIPTION
-----------
Finds dangling commits and tags from the object database, and
-creates refs to them in .git/lost-found/ directory. Commits and
-tags that dereference to commits go to .git/lost-found/commit
-and others are stored in .git/lost-found/other directory.
+creates refs to them in the .git/lost-found/ directory. Commits and
+tags that dereference to commits are stored in .git/lost-found/commit,
+and other objects are stored in .git/lost-found/other.
OUTPUT
------
-One line description from the commit and tag found along with
-their object name are printed on the standard output.
-
+Prints to standard output the object names and one-line descriptions
+of any commits or tags found.
EXAMPLE
-------
-Suppose you run 'git tag -f' and mistyped the tag to overwrite.
+Suppose you run 'git tag -f' and mistype the tag to overwrite.
The ref to your tag is overwritten, but until you run 'git
-prune', it is still there.
+prune', the tag itself is still there.
------------
$ git lost-found
...
------------
-Also you can use gitk to browse how they relate to each other
-and existing (probably old) tags.
+Also you can use gitk to browse how any tags found relate to each
+other.
------------
$ gitk $(cd .git/lost-found/commit && echo ??*)
------------
-After making sure that it is the object you are looking for, you
-can reconnect it to your regular .git/refs hierarchy.
+After making sure you know which the object is the tag you are looking
+for, you can reconnect it to your regular .git/refs hierarchy.
------------
$ git cat-file -t 1ef2b196
[ \--topo-order ]
[ \--parents ]
[ \--left-right ]
+ [ \--cherry-pick ]
[ \--encoding[=<encoding>] ]
[ \--(author|committer|grep)=<pattern> ]
[ [\--objects | \--objects-edge] [ \--unpacked ] ]
In addition to the '<commit>' listed on the command
line, read them from the standard input.
+--cherry-pick::
+
+ Omit any commit that introduces the same change as
+ another commit on the "other side" when the set of
+ commits are limited with symmetric difference.
++
+For example, if you have two branches, `A` and `B`, a usual way
+to list all commits on only one side of them is with
+`--left-right`, like the example above in the description of
+that option. It however shows the commits that were cherry-picked
+from the other branch (for example, "3rd on b" may be cherry-picked
+from branch A). With this option, such pairs of commits are
+excluded from the output.
+
-g, --walk-reflogs::
Instead of walking the commit ancestry chain, walk
SYNOPSIS
--------
-'git-rm' [-f] [-n] [-r] [--cached] [--] <file>...
+'git-rm' [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
DESCRIPTION
-----------
the paths only from the index, leaving working tree
files.
+\--ignore-unmatch::
+ Exit with a zero status even if no files matched.
+
+\--quiet::
+ git-rm normally outputs one line (in the form of an "rm" command)
+ for each file removed. This option suppresses that output.
+
DISCUSSION
----------
[verse]
'git-show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
[-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
+'git-show-ref' --exclude-existing[=pattern]
DESCRIPTION
-----------
dereferenced into object IDs. Additionally, it can be used to test whether a
particular ref exists.
+The --exclude-existing form is a filter that does the inverse, it shows the
+refs from stdin that don't exist in the local repository.
+
Use of this utility is encouraged in favor of directly accessing files under
in the `.git` directory.
Do not print any results to stdout. When combined with '--verify' this
can be used to silently check if a reference exists.
+--exclude-existing, --exclude-existing=pattern::
+
+ Make git-show-ref act as a filter that reads refs from stdin of the
+ form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the
+ following actions on each:
+ (1) strip "^{}" at the end of line if any;
+ (2) ignore if pattern is provided and does not head-match refname;
+ (3) warn if refname is not a well-formed refname and skip;
+ (4) ignore if refname is a ref that exists in the local repository;
+ (5) otherwise output the line.
+
+
<pattern>::
Show references matching one or more patterns.
- '%Cgreen': switch color to green
- '%Cblue': switch color to blue
- '%Creset': reset color
+- '%m': left, right or boundary mark
- '%n': newline
* master
------------------------------------------------
-A freshly cloned repository contains a single branch head, named
-"master", and working directory is initialized to the state of
-the project referred to by "master".
+A freshly cloned repository contains a single branch head, by default
+named "master", with the working directory initialized to the state of
+the project referred to by that branch head.
Most projects also use <<def_tag,tags>>. Tags, like heads, are
references into the project's history, and can be listed using the
create a new branch <new> referencing <start-point>, and
check it out.
-It is also useful to know that the special symbol "HEAD" can always
-be used to refer to the current branch.
+The special symbol "HEAD" can always be used to refer to the current
+branch. In fact, git uses a file named "HEAD" in the .git directory to
+remember which branch is current:
+
+------------------------------------------------
+$ cat .git/HEAD
+ref: refs/heads/master
+------------------------------------------------
+
+[[detached-head]]
+Examining an old version without creating a new branch
+------------------------------------------------------
+
+The git-checkout command normally expects a branch head, but will also
+accept an arbitrary commit; for example, you can check out the commit
+referenced by a tag:
+
+------------------------------------------------
+$ git checkout v2.6.17
+Note: moving to "v2.6.17" which isn't a local branch
+If you want to create a new branch from this checkout, you may do so
+(now or later) by using -b with the checkout command again. Example:
+ git checkout -b <new_branch_name>
+HEAD is now at 427abfa... Linux v2.6.17
+------------------------------------------------
+
+The HEAD then refers to the SHA1 of the commit instead of to a branch,
+and git branch shows that you are no longer on a branch:
+
+------------------------------------------------
+$ cat .git/HEAD
+427abfa28afedffadfca9dd8b067eb6d36bac53f
+git branch
+* (no branch)
+ master
+------------------------------------------------
+
+In this case we say that the HEAD is "detached".
+
+This can be an easy way to check out a particular version without having
+to make up a name for a new branch. However, keep in mind that when you
+switch away from the (for example, by checking out something else), you
+can lose track of what the HEAD used to point to.
Examining branches from a remote repository
-------------------------------------------
(Either gitk or git-log may be useful for finding the commit.)
-Then check out a new branch at that commit, edit it, and rebase the rest of
-the series on top of it:
+Then check out that commit, edit it, and rebase the rest of the series
+on top of it (note that we could check out the commit on a temporary
+branch, but instead we're using a <<detached-head,detached head>>):
-------------------------------------------------
-$ git checkout -b TMP bad
+$ git checkout bad
$ # make changes here and update the index
$ git commit --amend
-$ git rebase --onto TMP bad mywork
+$ git rebase --onto HEAD bad mywork
-------------------------------------------------
-When you're done, you'll be left with mywork checked out, with the top patches
-on mywork reapplied on top of the modified commit you created in TMP. You can
+When you're done, you'll be left with mywork checked out, with the top
+patches on mywork reapplied on top of your modified commit. You can
then clean up with
-------------------------------------------------
-$ git branch -d TMP
$ git tag -d bad
-------------------------------------------------
Git internals
=============
-There are two object abstractions: the "object database", and the
-"current directory cache" aka "index".
+Git depends on two fundamental abstractions: the "object database", and
+the "current directory cache" aka "index".
The Object Database
-------------------
determined at object creation time, and which identifies the format of
the object (i.e. how it is used, and how it can refer to other
objects). There are currently four different object types: "blob",
-"tree", "commit" and "tag".
+"tree", "commit", and "tag".
-A "blob" object cannot refer to any other object, and is, like the type
-implies, a pure storage object containing some user data. It is used to
-actually store the file data, i.e. a blob object is associated with some
-particular version of some file.
+A <<def_blob_object,"blob" object>> cannot refer to any other object,
+and is, as the name implies, a pure storage object containing some
+user data. It is used to actually store the file data, i.e. a blob
+object is associated with some particular version of some file.
-A "tree" object is an object that ties one or more "blob" objects into a
-directory structure. In addition, a tree object can refer to other tree
-objects, thus creating a directory hierarchy.
+A <<def_tree_object,"tree" object>> is an object that ties one or more
+"blob" objects into a directory structure. In addition, a tree object
+can refer to other tree objects, thus creating a directory hierarchy.
-A "commit" object ties such directory hierarchies together into
-a DAG of revisions - each "commit" is associated with exactly one tree
-(the directory hierarchy at the time of the commit). In addition, a
-"commit" refers to one or more "parent" commit objects that describe the
-history of how we arrived at that directory hierarchy.
+A <<def_commit_object,"commit" object>> ties such directory hierarchies
+together into a <<def_DAG,directed acyclic graph>> of revisions - each
+"commit" is associated with exactly one tree (the directory hierarchy at
+the time of the commit). In addition, a "commit" refers to one or more
+"parent" commit objects that describe the history of how we arrived at
+that directory hierarchy.
As a special case, a commit object with no parents is called the "root"
object, and is the point of an initial project commit. Each project
just going to confuse people. So aim for the notion of "one root object
per project", even if git itself does not enforce that.
-A "tag" object symbolically identifies and can be used to sign other
-objects. It contains the identifier and type of another object, a
-symbolic name (of course!) and, optionally, a signature.
+A <<def_tag_object,"tag" object>> symbolically identifies and can be
+used to sign other objects. It contains the identifier and type of
+another object, a symbolic name (of course!) and, optionally, a
+signature.
Regardless of object type, all objects share the following
characteristics: they are all deflated with zlib, and have a header
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.1.GIT
+DEF_VER=v1.5.1.1.GIT
LF='
'
diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
- utf8.h reflog-walk.h
+ utf8.h reflog-walk.h patch-ids.h decorate.h
DIFF_OBJS = \
diff.o diff-lib.o diffcore-break.o diffcore-order.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o \
interpolate.o \
lockfile.o \
+ patch-ids.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
reachable.o reflog-walk.o \
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
write_or_die.o trace.o list-objects.o grep.o match-trees.o \
alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
- convert.o
+ convert.o decorate.o
BUILTIN_OBJS = \
builtin-add.o \
help.o: common-cmds.h
git-merge-subtree$X: git-merge-recursive$X
- rm -f $@ && ln git-merge-recursive$X $@
+ $(QUIET_BUILT_IN)rm -f $@ && ln git-merge-recursive$X $@
$(BUILT_INS): git$X
$(QUIET_BUILT_IN)rm -f $@ && ln git$X $@
}
}
-int decode_85(char *dst, char *buffer, int len)
+int decode_85(char *dst, const char *buffer, int len)
{
prep_base85();
return 0;
}
-void encode_85(char *buf, unsigned char *data, int bytes)
+void encode_85(char *buf, const unsigned char *data, int bytes)
{
prep_base85();
static char blame_usage[] =
"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [--contents <filename>] [--incremental] [commit] [--] file\n"
-" -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
+" -c Use the same output mode as git-annotate (Default: off)\n"
" -b Show blank SHA-1 for boundary commits (Default: off)\n"
-" -l, --long Show long commit SHA1 (Default: off)\n"
+" -l Show long commit SHA1 (Default: off)\n"
" --root Do not treat root commits as boundaries (Default: off)\n"
-" -t, --time Show raw timestamp (Default: off)\n"
+" -t Show raw timestamp (Default: off)\n"
" -f, --show-name Show original filename (Default: auto)\n"
" -n, --show-number Show original linenumber (Default: off)\n"
" -p, --porcelain Show in a format designed for machine consumption\n"
commit->buffer = xmalloc(400);
ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
- sprintf(commit->buffer,
+ snprintf(commit->buffer, 400,
"tree 0000000000000000000000000000000000000000\n"
"parent %s\n"
"author %s\n"
* "show_unreachable" flag.
*/
if (!default_refs) {
- error("No default references");
+ fprintf(stderr, "notice: No default references\n");
show_unreachable = 0;
}
}
{
unsigned char sha1[20];
int flag;
- const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag);
-
- if (!head_points_at || !(flag & REF_ISSYMREF))
- return error("HEAD is not a symbolic ref");
- if (prefixcmp(head_points_at, "refs/heads/"))
+ int null_is_error = 0;
+ const char *head_points_at = resolve_ref("HEAD", sha1, 0, &flag);
+
+ if (!head_points_at)
+ return error("Invalid HEAD");
+ if (!strcmp(head_points_at, "HEAD"))
+ /* detached HEAD */
+ null_is_error = 1;
+ else if (prefixcmp(head_points_at, "refs/heads/"))
return error("HEAD points to something strange (%s)",
head_points_at);
- if (is_null_sha1(sha1))
- return error("HEAD: not a valid git pointer");
+ if (is_null_sha1(sha1)) {
+ if (null_is_error)
+ return error("HEAD: detached HEAD points at nothing");
+ fprintf(stderr, "notice: HEAD points to an unborn branch (%s)\n",
+ head_points_at + 11);
+ }
return 0;
}
static const char emsg_missing_argument[] =
"option requires an argument -%s";
-static int strtoul_ui(char const *s, unsigned int *result)
-{
- unsigned long ul;
- char *p;
-
- errno = 0;
- ul = strtoul(s, &p, 10);
- if (errno || *p || p == s || (unsigned int) ul != ul)
- return -1;
- *result = ul;
- return 0;
-}
-
int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
scan = arg + 1;
break;
}
- if (strtoul_ui(scan, &num))
+ if (strtoul_ui(scan, 10, &num))
die(emsg_invalid_context_len, scan);
switch (arg[1]) {
case 'A':
#include "builtin.h"
#include "tag.h"
#include "reflog-walk.h"
+#include "patch-ids.h"
+#include "refs.h"
static int default_show_root = 1;
/* this is in builtin-diff.c */
void add_head(struct rev_info *revs);
+static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+{
+ int plen = strlen(prefix);
+ int nlen = strlen(name);
+ struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
+ memcpy(res->name, prefix, plen);
+ memcpy(res->name + plen, name, nlen + 1);
+ res->next = add_decoration(&name_decoration, obj, res);
+}
+
+static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+ struct object *obj = parse_object(sha1);
+ if (!obj)
+ return 0;
+ add_name_decoration("", refname, obj);
+ while (obj->type == OBJ_TAG) {
+ obj = ((struct tag *)obj)->tagged;
+ if (!obj)
+ break;
+ add_name_decoration("tag: ", refname, obj);
+ }
+ return 0;
+}
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev)
{
int i;
+ int decorate = 0;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
git_log_output_encoding = xstrdup(arg);
else
git_log_output_encoding = "";
- }
- else
+ } else if (!strcmp(arg, "--decorate")) {
+ if (!decorate)
+ for_each_ref(add_ref_decoration, NULL);
+ decorate = 1;
+ } else
die("unrecognized argument: %s", arg);
}
}
}
-static int get_patch_id(struct commit *commit, struct diff_options *options,
- unsigned char *sha1)
-{
- if (commit->parents)
- diff_tree_sha1(commit->parents->item->object.sha1,
- commit->object.sha1, "", options);
- else
- diff_root_tree_sha1(commit->object.sha1, "", options);
- diffcore_std(options);
- return diff_flush_patch_id(options, sha1);
-}
-
-static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix)
+static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids, const char *prefix)
{
struct rev_info check_rev;
struct commit *commit;
struct object *o1, *o2;
unsigned flags1, flags2;
- unsigned char sha1[20];
if (rev->pending.nr != 2)
die("Need exactly one range.");
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die("Not a range.");
- diff_setup(options);
- options->recursive = 1;
- if (diff_setup_done(options) < 0)
- die("diff_setup_done failed");
+ init_patch_ids(ids);
/* given a range a..b get all patch ids for b..a */
init_revisions(&check_rev, prefix);
if (commit->parents && commit->parents->next)
continue;
- if (!get_patch_id(commit, options, sha1))
- created_object(sha1, xcalloc(1, sizeof(struct object)));
+ add_commit_patch_id(commit, ids);
}
/* reset for next revision walk */
int numbered = 0;
int start_number = -1;
int keep_subject = 0;
+ int subject_prefix = 0;
int ignore_if_in_upstream = 0;
int thread = 0;
const char *in_reply_to = NULL;
- struct diff_options patch_id_opts;
+ struct patch_ids ids;
char *add_signoff = NULL;
char message_id[1024];
char ref_message_id[1024];
if (i == argc)
die("Need a Message-Id for --in-reply-to");
in_reply_to = argv[i];
- }
- else if (!prefixcmp(argv[i], "--suffix="))
+ } else if (!prefixcmp(argv[i], "--subject-prefix=")) {
+ subject_prefix = 1;
+ rev.subject_prefix = argv[i] + 17;
+ } else if (!prefixcmp(argv[i], "--suffix="))
fmt_patch_suffix = argv[i] + 9;
else
argv[j++] = argv[i];
start_number = 1;
if (numbered && keep_subject)
die ("-n and -k are mutually exclusive.");
+ if (keep_subject && subject_prefix)
+ die ("--subject-prefix and -k are mutually exclusive.");
argc = setup_revisions(argc, argv, &rev, "HEAD");
if (argc > 1)
}
if (ignore_if_in_upstream)
- get_patch_ids(&rev, &patch_id_opts, prefix);
+ get_patch_ids(&rev, &ids, prefix);
if (!use_stdout)
realstdout = fdopen(dup(1), "w");
prepare_revision_walk(&rev);
while ((commit = get_revision(&rev)) != NULL) {
- unsigned char sha1[20];
-
/* ignore merges */
if (commit->parents && commit->parents->next)
continue;
if (ignore_if_in_upstream &&
- !get_patch_id(commit, &patch_id_opts, sha1) &&
- lookup_object(sha1))
+ has_commit_patch_id(commit, &ids))
continue;
nr++;
fclose(stdout);
}
free(list);
+ if (ignore_if_in_upstream)
+ free_patch_ids(&ids);
return 0;
}
int cmd_cherry(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
- struct diff_options patch_id_opts;
+ struct patch_ids ids;
struct commit *commit;
struct commit_list *list = NULL;
const char *upstream;
return 0;
}
- get_patch_ids(&revs, &patch_id_opts, prefix);
+ get_patch_ids(&revs, &ids, prefix);
if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
die("Unknown commit %s", limit);
}
while (list) {
- unsigned char sha1[20];
char sign = '+';
commit = list->item;
- if (!get_patch_id(commit, &patch_id_opts, sha1) &&
- lookup_object(sha1))
+ if (has_commit_patch_id(commit, &ids))
sign = '-';
if (verbose) {
list = list->next;
}
+ free_patch_ids(&ids);
return 0;
}
#include "tree-walk.h"
static const char builtin_rm_usage[] =
-"git-rm [-f] [-n] [-r] [--cached] [--] <file>...";
+"git-rm [-f] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...";
static struct {
int nr, alloc;
int cmd_rm(int argc, const char **argv, const char *prefix)
{
int i, newfd;
- int show_only = 0, force = 0, index_only = 0, recursive = 0;
+ int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
+ int ignore_unmatch = 0;
const char **pathspec;
char *seen;
force = 1;
else if (!strcmp(arg, "-r"))
recursive = 1;
+ else if (!strcmp(arg, "--quiet"))
+ quiet = 1;
+ else if (!strcmp(arg, "--ignore-unmatch"))
+ ignore_unmatch = 1;
else
usage(builtin_rm_usage);
}
if (pathspec) {
const char *match;
+ int seen_any = 0;
for (i = 0; (match = pathspec[i]) != NULL ; i++) {
- if (!seen[i])
- die("pathspec '%s' did not match any files",
- match);
+ if (!seen[i]) {
+ if (!ignore_unmatch) {
+ die("pathspec '%s' did not match any files",
+ match);
+ }
+ }
+ else {
+ seen_any = 1;
+ }
if (!recursive && seen[i] == MATCHED_RECURSIVELY)
die("not removing '%s' recursively without -r",
*match ? match : ".");
}
+
+ if (! seen_any)
+ exit(0);
}
/*
* must match; but the file can already been removed, since
* this sequence is a natural "novice" way:
*
- * rm F; git fm F
+ * rm F; git rm F
*
* Further, if HEAD commit exists, "diff-index --cached" must
* report no changes unless forced.
*/
for (i = 0; i < list.nr; i++) {
const char *path = list.name[i];
- printf("rm '%s'\n", path);
+ if (!quiet)
+ printf("rm '%s'\n", path);
if (remove_file_from_cache(path))
die("git-rm: unable to remove %s", path);
#include "diff.h"
#include "path-list.h"
#include "revision.h"
+#include "utf8.h"
static const char shortlog_usage[] =
"git-shortlog [-n] [-s] [<commit-id>... ]";
}
+static int parse_uint(char const **arg, int comma)
+{
+ unsigned long ul;
+ int ret;
+ char *endp;
+
+ ul = strtoul(*arg, &endp, 10);
+ if (endp != *arg && *endp && *endp != comma)
+ return -1;
+ ret = (int) ul;
+ if (ret != ul)
+ return -1;
+ *arg = endp;
+ if (**arg)
+ (*arg)++;
+ return ret;
+}
+
+static const char wrap_arg_usage[] = "-w[<width>[,<indent1>[,<indent2>]]]";
+#define DEFAULT_WRAPLEN 76
+#define DEFAULT_INDENT1 6
+#define DEFAULT_INDENT2 9
+
+static void parse_wrap_args(const char *arg, int *in1, int *in2, int *wrap)
+{
+ arg += 2; /* skip -w */
+
+ *wrap = parse_uint(&arg, ',');
+ if (*wrap < 0)
+ die(wrap_arg_usage);
+ *in1 = parse_uint(&arg, ',');
+ if (*in1 < 0)
+ die(wrap_arg_usage);
+ *in2 = parse_uint(&arg, '\0');
+ if (*in2 < 0)
+ die(wrap_arg_usage);
+
+ if (!*wrap)
+ *wrap = DEFAULT_WRAPLEN;
+ if (!*in1)
+ *in1 = DEFAULT_INDENT1;
+ if (!*in2)
+ *in2 = DEFAULT_INDENT2;
+ if (*wrap &&
+ ((*in1 && *wrap <= *in1) ||
+ (*in2 && *wrap <= *in2)))
+ die(wrap_arg_usage);
+}
+
int cmd_shortlog(int argc, const char **argv, const char *prefix)
{
struct rev_info rev;
struct path_list list = { NULL, 0, 0, 1 };
int i, j, sort_by_number = 0, summary = 0;
+ int wrap_lines = 0;
+ int wrap = DEFAULT_WRAPLEN;
+ int in1 = DEFAULT_INDENT1;
+ int in2 = DEFAULT_INDENT2;
/* since -n is a shadowed rev argument, parse our args first */
while (argc > 1) {
else if (!strcmp(argv[1], "-s") ||
!strcmp(argv[1], "--summary"))
summary = 1;
+ else if (!prefixcmp(argv[1], "-w")) {
+ wrap_lines = 1;
+ parse_wrap_args(argv[1], &in1, &in2, &wrap);
+ }
else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
usage(shortlog_usage);
else
printf("%s: %d\n", list.items[i].path, onelines->nr);
} else {
printf("%s (%d):\n", list.items[i].path, onelines->nr);
- for (j = onelines->nr - 1; j >= 0; j--)
- printf(" %s\n", onelines->items[j].path);
- printf("\n");
+ for (j = onelines->nr - 1; j >= 0; j--) {
+ const char *msg = onelines->items[j].path;
+
+ if (wrap_lines) {
+ int col = print_wrapped_text(msg, in1, in2, wrap);
+ if (col != wrap)
+ putchar('\n');
+ }
+ else
+ printf(" %s\n", msg);
+ }
+ putchar('\n');
}
onelines->strdup_paths = 1;
char *path_name;
unsigned char sha1[20];
unsigned int mode;
+ unsigned long ul;
int stage;
/* This reads lines formatted in one of three formats:
if (buf.eof)
break;
- mode = strtoul(buf.buf, &ptr, 8);
- if (ptr == buf.buf || *ptr != ' ')
+ errno = 0;
+ ul = strtoul(buf.buf, &ptr, 8);
+ if (ptr == buf.buf || *ptr != ' '
+ || errno || (unsigned int) ul != ul)
goto bad_line;
+ mode = ul;
tab = strchr(ptr, '\t');
if (!tab || tab - ptr < 41)
if (i+3 >= argc)
die("git-update-index: --cacheinfo <mode> <sha1> <path>");
- if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
+ if ((strtoul_ui(argv[i+1], 8, &mode) != 1) ||
get_sha1_hex(argv[i+2], sha1) ||
add_cacheinfo(mode, sha1, argv[i+3], 0))
die("git-update-index: --cacheinfo"
extern int pager_use_color;
/* base85 */
-int decode_85(char *dst, char *line, int linelen);
-void encode_85(char *buf, unsigned char *data, int bytes);
+int decode_85(char *dst, const char *line, int linelen);
+void encode_85(char *buf, const unsigned char *data, int bytes);
/* alloc.c */
struct blob;
#include "pkt-line.h"
#include "utf8.h"
#include "interpolate.h"
+#include "diff.h"
+#include "revision.h"
int save_commit_buffer = 1;
{ "%Cgreen" }, /* green */
{ "%Cblue" }, /* blue */
{ "%Creset" }, /* reset color */
- { "%n" } /* newline */
+ { "%n" }, /* newline */
+ { "%m" }, /* left/right/bottom */
};
enum interp_index {
IHASH = 0, IHASH_ABBREV,
ISUBJECT,
IBODY,
IRED, IGREEN, IBLUE, IRESET_COLOR,
- INEWLINE
+ INEWLINE,
+ ILEFT_RIGHT,
};
struct commit_list *p;
char parents[1024];
int i;
enum { HEADER, SUBJECT, BODY } state;
- if (INEWLINE + 1 != ARRAY_SIZE(table))
+ if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
die("invalid interp table!");
/* these are independent of the commit */
interp_set_entry(table, ITREE_ABBREV,
find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
+ interp_set_entry(table, ILEFT_RIGHT,
+ (commit->object.flags & BOUNDARY)
+ ? "-"
+ : (commit->object.flags & SYMMETRIC_LEFT)
+ ? "<"
+ : ">");
parents[1] = 0;
for (i = 0, p = commit->parents;
#include "object.h"
#include "tree.h"
+#include "decorate.h"
struct commit_list {
struct commit *item;
extern int save_commit_buffer;
extern const char *commit_type;
+/* While we can decorate any object with a name, it's only used for commits.. */
+extern struct decoration name_decoration;
+struct name_decoration {
+ struct name_decoration *next;
+ char name[1];
+};
+
struct commit *lookup_commit(const unsigned char *sha1);
struct commit *lookup_commit_reference(const unsigned char *sha1);
struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
unsigned int mode;
char *slash, *origpath;
- if (!path || sscanf(buffer, "%o", &mode) != 1)
+ if (!path || strtoul_ui(buffer, 8, &mode) != 1)
die("bad tree conversion");
mode = convert_mode(mode);
path++;
--- /dev/null
+/*
+ * decorate.c - decorate a git object with some arbitrary
+ * data.
+ */
+#include "cache.h"
+#include "object.h"
+#include "decorate.h"
+
+static unsigned int hash_obj(struct object *obj, unsigned int n)
+{
+ unsigned int hash = *(unsigned int *)obj->sha1;
+ return hash % n;
+}
+
+static void *insert_decoration(struct decoration *n, struct object *base, void *decoration)
+{
+ int size = n->size;
+ struct object_decoration *hash = n->hash;
+ int j = hash_obj(base, size);
+
+ while (hash[j].base) {
+ if (hash[j].base == base) {
+ void *old = hash[j].decoration;
+ hash[j].decoration = decoration;
+ return old;
+ }
+ j++;
+ if (++j >= size)
+ j = 0;
+ }
+ hash[j].base = base;
+ hash[j].decoration = decoration;
+ n->nr++;
+ return NULL;
+}
+
+static void grow_decoration(struct decoration *n)
+{
+ int i;
+ int old_size = n->size;
+ struct object_decoration *old_hash;
+
+ old_size = n->size;
+ old_hash = n->hash;
+
+ n->size = (old_size + 1000) * 3 / 2;
+ n->hash = xcalloc(n->size, sizeof(struct object_decoration));
+ n->nr = 0;
+
+ for (i = 0; i < old_size; i++) {
+ struct object *base = old_hash[i].base;
+ void *decoration = old_hash[i].decoration;
+
+ if (!base)
+ continue;
+ insert_decoration(n, base, decoration);
+ }
+ free(old_hash);
+}
+
+/* Add a decoration pointer, return any old one */
+void *add_decoration(struct decoration *n, struct object *obj, void *decoration)
+{
+ int nr = n->nr + 1;
+
+ if (nr > n->size * 2 / 3)
+ grow_decoration(n);
+ return insert_decoration(n, obj, decoration);
+}
+
+/* Lookup a decoration pointer */
+void *lookup_decoration(struct decoration *n, struct object *obj)
+{
+ int j;
+
+ /* nothing to lookup */
+ if (!n->size)
+ return NULL;
+ j = hash_obj(obj, n->size);
+ for (;;) {
+ struct object_decoration *ref = n->hash + j;
+ if (ref->base == obj)
+ return ref->decoration;
+ if (!ref->base)
+ return NULL;
+ if (++j == n->size)
+ j = 0;
+ }
+}
--- /dev/null
+#ifndef DECORATE_H
+#define DECORATE_H
+
+struct object_decoration {
+ struct object *base;
+ void *decoration;
+};
+
+struct decoration {
+ const char *name;
+ unsigned int size, nr;
+ struct object_decoration *hash;
+};
+
+extern void *add_decoration(struct decoration *n, struct object *obj, void *decoration);
+extern void *lookup_decoration(struct decoration *n, struct object *obj);
+
+#endif
}
}
+/*
+ * Does the path name a blob in the working tree, or a directory
+ * in the working tree?
+ */
static int is_in_index(const char *path)
{
- int len = strlen(path);
- int pos = cache_name_pos(path, len);
- char c;
-
- if (pos < 0)
- return 0;
- if (strncmp(active_cache[pos]->name, path, len))
- return 0;
- c = active_cache[pos]->name[len];
- return c == '\0' || c == '/';
+ int len, pos;
+ struct cache_entry *ce;
+
+ len = strlen(path);
+ while (path[len-1] == '/')
+ len--;
+ if (!len)
+ return 1; /* "." */
+ pos = cache_name_pos(path, len);
+ if (0 <= pos)
+ return 1;
+ pos = -1 - pos;
+ while (pos < active_nr) {
+ ce = active_cache[pos++];
+ if (ce_namelen(ce) <= len ||
+ strncmp(ce->name, path, len) ||
+ (ce->name[len] > '/'))
+ break; /* path cannot be a prefix */
+ if (ce->name[len] == '/')
+ return 1;
+ }
+ return 0;
}
static int handle_diff_files_args(struct rev_info *revs,
done
sq "$@" >"$GIT_DIR/BISECT_NAMES"
- {
- printf "git-bisect start"
- echo "$orig_args"
- } >>"$GIT_DIR/BISECT_LOG"
+ echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
bisect_auto_next
}
fi
if test -z "$quiet"
then
+ commit=`git-diff-tree --always --shortstat --pretty="format:%h: %s"\
+ --summary --root HEAD --`
echo "Created${initial_commit:+ initial} commit $commit"
- git-diff-tree --shortstat --summary --root --no-commit-id HEAD --
fi
fi
return strncmp(str, prefix, strlen(prefix));
}
+static inline int strtoul_ui(char const *s, int base, unsigned int *result)
+{
+ unsigned long ul;
+ char *p;
+
+ errno = 0;
+ ul = strtoul(s, &p, base);
+ if (errno || *p || p == s || (unsigned int) ul != ul)
+ return -1;
+ *result = ul;
+ return 0;
+}
+
#endif
`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
## apply non-binary changes
-my $fuzz = $opt_p ? 0 : 2;
+
+# In pedantic mode require all lines of context to match. In normal
+# mode, be compatible with diff/patch: assume 3 lines of context and
+# require at least one line match, i.e. ignore at most 2 lines of
+# context, like diff/patch do by default.
+my $context = $opt_p ? '' : '-C1';
print "Checking if patch will apply\n";
my @stat;
-open APPLY, "GIT_DIR= git-apply -C$fuzz --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_DIR= git-apply $context --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
@stat=<APPLY>;
close APPLY || die "Cannot patch";
my (@bfiles,@files,@afiles,@dfiles);
}
print "Applying\n";
-`GIT_DIR= git-apply -C$fuzz --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+`GIT_DIR= git-apply $context --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
print "Patch applied successfully. Adding new files and directories to CVS\n";
my $dirtypatch = 0;
if ( defined ( $wrev )
and defined($meta->{revision})
and $wrev == $meta->{revision}
+ and defined($state->{entries}{$filename}{modified_hash})
and not exists ( $state->{opt}{C} ) )
{
$log->info("Tell the client the file is modified");
for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
echo $patch_name
(cat $QUILT_PATCHES/$patch_name | git-mailinfo "$tmp_msg" "$tmp_patch" > "$tmp_info") || exit 3
- test -s $dotest/patch || {
+ test -s .dotest/patch || {
echo "Patch is empty. Was is split wrong?"
- stop_here $this
+ exit 1
}
# Parse the author information
echo "$prev_head" > "$dotest/prev_head"
msgnum=0
-for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
- | @@PERL@@ -e 'print reverse <>'`
+for cmt in `git-rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/cmt.$msgnum"
my ($name, $addr) = ($from =~ /^(.*?)(\s+<.*)/);
$from = "\"$name\"$addr";
}
+ my $ccline = "";
+ if ($cc ne '') {
+ $ccline = "\nCc: $cc";
+ }
my $header = "From: $from
-To: $to
-Cc: $cc
+To: $to${ccline}
Subject: $subject
Date: $date
Message-Id: $message_id
setenv(GIT_DIR_ENVIRONMENT, (*argv)[1], 1);
(*argv)++;
(*argc)--;
+ handled++;
} else if (!prefixcmp(cmd, "--git-dir=")) {
setenv(GIT_DIR_ENVIRONMENT, cmd + 10, 1);
} else if (!strcmp(cmd, "--bare")) {
frame .bright.mode
radiobutton .bright.mode.patch -text "Patch" \
-command reselectline -variable cmitmode -value "patch"
+ .bright.mode.patch configure -font $uifont
radiobutton .bright.mode.tree -text "Tree" \
-command reselectline -variable cmitmode -value "tree"
+ .bright.mode.tree configure -font $uifont
grid .bright.mode.patch .bright.mode.tree -sticky ew
pack .bright.mode -side top -fill x
set cflist .bright.cfiles
}
proc about {} {
+ global uifont
set w .about
if {[winfo exists $w]} {
raise $w
Copyright © 2005-2006 Paul Mackerras
Use and redistribute under the terms of the GNU General Public License} \
- -justify center -aspect 400
- pack $w.m -side top -fill x -padx 20 -pady 20
- button $w.ok -text Close -command "destroy $w"
+ -justify center -aspect 400 -border 2 -bg white -relief groove
+ pack $w.m -side top -fill x -padx 2 -pady 2
+ $w.m configure -font $uifont
+ button $w.ok -text Close -command "destroy $w" -default active
pack $w.ok -side bottom
+ $w.ok configure -font $uifont
+ bind $w <Visibility> "focus $w.ok"
+ bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> "destroy $w"
}
proc keys {} {
+ global uifont
set w .keys
if {[winfo exists $w]} {
raise $w
<Ctrl-minus> Decrease font size
<F5> Update
} \
- -justify left -bg white -border 2 -relief sunken
- pack $w.m -side top -fill both
- button $w.ok -text Close -command "destroy $w"
+ -justify left -bg white -border 2 -relief groove
+ pack $w.m -side top -fill both -padx 2 -pady 2
+ $w.m configure -font $uifont
+ button $w.ok -text Close -command "destroy $w" -default active
pack $w.ok -side bottom
+ $w.ok configure -font $uifont
+ bind $w <Visibility> "focus $w.ok"
+ bind $w <Key-Escape> "destroy $w"
+ bind $w <Key-Return> "destroy $w"
}
# Procedures for manipulating the file list window at the
toplevel $top
wm title $top $title
label $top.nl -text "Name" -font $uifont
- entry $top.name -width 20 -textvariable newviewname($n)
+ entry $top.name -width 20 -textvariable newviewname($n) -font $uifont
grid $top.nl $top.name -sticky w -pady 5
- checkbutton $top.perm -text "Remember this view" -variable newviewperm($n)
+ checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
+ -font $uifont
grid $top.perm - -pady 5 -sticky w
message $top.al -aspect 1000 -font $uifont \
-text "Commits to include (arguments to git rev-list):"
grid $top.al - -sticky w -pady 5
entry $top.args -width 50 -textvariable newviewargs($n) \
- -background white
+ -background white -font $uifont
grid $top.args - -sticky ew -padx 5
message $top.l -aspect 1000 -font $uifont \
-text "Enter files and directories to include, one per line:"
grid $top.l - -sticky w
- text $top.t -width 40 -height 10 -background white
+ text $top.t -width 40 -height 10 -background white -font $uifont
if {[info exists viewfiles($n)]} {
foreach f $viewfiles($n) {
$top.t insert end $f
}
grid $top.t - -sticky ew -padx 5
frame $top.buts
- button $top.buts.ok -text "OK" -command [list newviewok $top $n]
- button $top.buts.can -text "Cancel" -command [list destroy $top]
+ button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
+ -font $uifont
+ button $top.buts.can -text "Cancel" -command [list destroy $top] \
+ -font $uifont
grid $top.buts.ok $top.buts.can
grid columnconfigure $top.buts 0 -weight 1 -uniform a
grid columnconfigure $top.buts 1 -weight 1 -uniform a
global maxwidth maxgraphpct diffopts
global oldprefs prefstop showneartags
global bgcolor fgcolor ctext diffcolors
+ global uifont
set top .gitkprefs
set prefstop $top
toplevel $top
wm title $top "Gitk preferences"
label $top.ldisp -text "Commit list display options"
+ $top.ldisp configure -font $uifont
grid $top.ldisp - -sticky w -pady 10
label $top.spacer -text " "
label $top.maxwidthl -text "Maximum graph width (lines)" \
grid x $top.maxpctl $top.maxpct -sticky w
label $top.ddisp -text "Diff display options"
+ $top.ddisp configure -font $uifont
grid $top.ddisp - -sticky w -pady 10
label $top.diffoptl -text "Options for diff program" \
-font optionfont
grid x $top.ntag -sticky w
label $top.cdisp -text "Colors: press to choose"
+ $top.cdisp configure -font $uifont
grid $top.cdisp - -sticky w -pady 10
label $top.bg -padx 40 -relief sunk -background $bgcolor
button $top.bgbut -text "Background" -font optionfont \
grid x $top.hunksepbut $top.hunksep -sticky w
frame $top.buts
- button $top.buts.ok -text "OK" -command prefsok
- button $top.buts.can -text "Cancel" -command prefscan
+ button $top.buts.ok -text "OK" -command prefsok -default active
+ $top.buts.ok configure -font $uifont
+ button $top.buts.can -text "Cancel" -command prefscan -default normal
+ $top.buts.can configure -font $uifont
grid $top.buts.ok $top.buts.can
grid columnconfigure $top.buts 0 -weight 1 -uniform a
grid columnconfigure $top.buts 1 -weight 1 -uniform a
grid $top.buts - - -pady 10 -sticky ew
+ bind $top <Visibility> "focus $top.buts.ok"
}
proc choosecolor {v vi w x cmd} {
# source of projects list
our $projects_list = "++GITWEB_LIST++";
+# default order of projects list
+# valid values are none, project, descr, owner, and age
+our $default_projects_order = "project";
+
# show repository only if this file exists
# (only effective if this variable evaluates to true)
our $export_ok = "++GITWEB_EXPORT_OK++";
# projects matching $projname/*.git will not be shown in the main
# projects list, instead a '+' mark will be added to $projname
# there and a 'forks' view will be enabled for the project, listing
- # all the forks. This feature is supported only if project list
- # is taken from a directory, not file.
+ # all the forks. If project list is taken from a file, forks have
+ # to be listed after the main project.
# To enable system wide have in $GITWEB_CONFIG
# $feature{'forks'}{'default'} = [1];
$filter ||= '';
$filter =~ s/\.git$//;
+ my ($check_forks) = gitweb_check_feature('forks');
+
if (-d $projects_list) {
# search in directory
my $dir = $projects_list . ($filter ? "/$filter" : '');
$dir =~ s!/+$!!;
my $pfxlen = length("$dir");
- my ($check_forks) = gitweb_check_feature('forks');
-
File::Find::find({
follow_fast => 1, # follow symbolic links
dangling_symlinks => 0, # ignore dangling symlinks, silently
# 'git%2Fgit.git Linus+Torvalds'
# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
+ my %paths;
open my ($fd), $projects_list or return;
+ PROJECT:
while (my $line = <$fd>) {
chomp $line;
my ($path, $owner) = split ' ', $line;
# looking for forks;
my $pfx = substr($path, 0, length($filter));
if ($pfx ne $filter) {
- next;
+ next PROJECT;
}
my $sfx = substr($path, length($filter));
if ($sfx !~ /^\/.*\.git$/) {
- next;
+ next PROJECT;
+ }
+ } elsif ($check_forks) {
+ PATH:
+ foreach my $filter (keys %paths) {
+ # looking for forks;
+ my $pfx = substr($path, 0, length($filter));
+ if ($pfx ne $filter) {
+ next PATH;
+ }
+ my $sfx = substr($path, length($filter));
+ if ($sfx !~ /^\/.*\.git$/) {
+ next PATH;
+ }
+ # is a fork, don't include it in
+ # the list
+ next PROJECT;
}
}
if (check_export_ok("$projectroot/$path")) {
path => $path,
owner => to_utf8($owner),
};
- push @list, $pr
+ push @list, $pr;
+ (my $forks_path = $path) =~ s/\.git$//;
+ $paths{$forks_path}++;
}
}
close $fd;
}
- @list = sort {$a->{'path'} cmp $b->{'path'}} @list;
return @list;
}
push @projects, $pr;
}
- $order ||= "project";
+ $order ||= $default_projects_order;
$from = 0 unless defined $from;
$to = $#projects if (!defined $to || $#projects < $to);
sub git_project_list {
my $order = $cgi->param('o');
- if (defined $order && $order !~ m/project|descr|owner|age/) {
+ if (defined $order && $order !~ m/none|project|descr|owner|age/) {
die_error(undef, "Unknown order parameter");
}
sub git_forks {
my $order = $cgi->param('o');
- if (defined $order && $order !~ m/project|descr|owner|age/) {
+ if (defined $order && $order !~ m/none|project|descr|owner|age/) {
die_error(undef, "Unknown order parameter");
}
static char git_default_date[50];
-static void copy_gecos(struct passwd *w, char *name, int sz)
+static void copy_gecos(const struct passwd *w, char *name, size_t sz)
{
char *src, *dst;
- int len, nlen;
+ size_t len, nlen;
nlen = strlen(w->pw_name);
}
-static void copy_email(struct passwd *pw)
+static void copy_email(const struct passwd *pw)
{
/*
* Make up a fake email address
* (name + '@' + hostname [+ '.' + domainname])
*/
- int len = strlen(pw->pw_name);
+ size_t len = strlen(pw->pw_name);
if (len > sizeof(git_default_email)/2)
die("Your sysadmin must hate you!");
memcpy(git_default_email, pw->pw_name, len);
datestamp(git_default_date, sizeof(git_default_date));
}
-static int add_raw(char *buf, int size, int offset, const char *str)
+static int add_raw(char *buf, size_t size, int offset, const char *str)
{
- int len = strlen(str);
+ size_t len = strlen(str);
if (offset + len > size)
return size;
memcpy(buf + offset, str, len);
* Copy over a string to the destination, but avoid special
* characters ('\n', '<' and '>') and remove crud at the end
*/
-static int copy(char *buf, int size, int offset, const char *src)
+static int copy(char *buf, size_t size, int offset, const char *src)
{
- int i, len;
+ size_t i, len;
unsigned char c;
/* Remove crud from the beginning.. */
#include "log-tree.h"
#include "reflog-walk.h"
+struct decoration name_decoration = { "object names" };
+
static void show_parents(struct commit *commit, int abbrev)
{
struct commit_list *p;
}
}
+static void show_decorations(struct commit *commit)
+{
+ const char *prefix;
+ struct name_decoration *decoration;
+
+ decoration = lookup_decoration(&name_decoration, &commit->object);
+ if (!decoration)
+ return;
+ prefix = " (";
+ while (decoration) {
+ printf("%s%s", prefix, decoration->name);
+ prefix = ", ";
+ decoration = decoration->next;
+ }
+ putchar(')');
+}
+
/*
* Search for "^[-A-Za-z]+: [^@]+@" pattern. It usually matches
* Signed-off-by: and Acked-by: lines.
fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
if (opt->parents)
show_parents(commit, abbrev_commit);
+ show_decorations(commit);
putchar(opt->diffopt.line_termination);
return;
}
if (opt->total > 0) {
static char buffer[64];
snprintf(buffer, sizeof(buffer),
- "Subject: [PATCH %0*d/%d] ",
+ "Subject: [%s %0*d/%d] ",
+ opt->subject_prefix,
digits_in_number(opt->total),
opt->nr, opt->total);
subject = buffer;
- } else if (opt->total == 0)
- subject = "Subject: [PATCH] ";
- else
+ } else if (opt->total == 0) {
+ static char buffer[256];
+ snprintf(buffer, sizeof(buffer),
+ "Subject: [%s] ",
+ opt->subject_prefix);
+ subject = buffer;
+ } else {
subject = "Subject: ";
+ }
printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
if (opt->message_id)
printf(" (from %s)",
diff_unique_abbrev(parent->object.sha1,
abbrev_commit));
+ show_decorations(commit);
printf("%s",
diff_get_color(opt->diffopt.color_diff, DIFF_RESET));
putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
#include "cache.h"
#include "object.h"
+#include "decorate.h"
int track_object_refs = 0;
-static unsigned int refs_hash_size, nr_object_refs;
-static struct object_refs **refs_hash;
+static struct decoration ref_decorate;
-static unsigned int hash_obj(struct object *obj, unsigned int n)
+struct object_refs *lookup_object_refs(struct object *base)
{
- unsigned int hash = *(unsigned int *)obj->sha1;
- return hash % n;
+ return lookup_decoration(&ref_decorate, base);
}
-static void insert_ref_hash(struct object_refs *ref, struct object_refs **hash, unsigned int size)
+static void add_object_refs(struct object *obj, struct object_refs *refs)
{
- int j = hash_obj(ref->base, size);
-
- while (hash[j]) {
- j++;
- if (j >= size)
- j = 0;
- }
- hash[j] = ref;
-}
-
-static void grow_refs_hash(void)
-{
- int i;
- int new_hash_size = (refs_hash_size + 1000) * 3 / 2;
- struct object_refs **new_hash;
-
- new_hash = xcalloc(new_hash_size, sizeof(struct object_refs *));
- for (i = 0; i < refs_hash_size; i++) {
- struct object_refs *ref = refs_hash[i];
- if (!ref)
- continue;
- insert_ref_hash(ref, new_hash, new_hash_size);
- }
- free(refs_hash);
- refs_hash = new_hash;
- refs_hash_size = new_hash_size;
-}
-
-static void add_object_refs(struct object *obj, struct object_refs *ref)
-{
- int nr = nr_object_refs + 1;
-
- if (nr > refs_hash_size * 2 / 3)
- grow_refs_hash();
- ref->base = obj;
- insert_ref_hash(ref, refs_hash, refs_hash_size);
- nr_object_refs = nr;
-}
-
-struct object_refs *lookup_object_refs(struct object *obj)
-{
- struct object_refs *ref;
- int j;
-
- /* nothing to lookup */
- if (!refs_hash_size)
- return NULL;
- j = hash_obj(obj, refs_hash_size);
- while ((ref = refs_hash[j]) != NULL) {
- if (ref->base == obj)
- break;
- j++;
- if (j >= refs_hash_size)
- j = 0;
- }
- return ref;
+ if (add_decoration(&ref_decorate, obj, refs))
+ die("object %s tried to add refs twice!", sha1_to_hex(obj->sha1));
}
struct object_refs *alloc_object_refs(unsigned count)
struct object_refs {
unsigned count;
- struct object *base;
struct object *ref[FLEX_ARRAY]; /* more */
};
--- /dev/null
+#include "cache.h"
+#include "diff.h"
+#include "commit.h"
+#include "patch-ids.h"
+
+static int commit_patch_id(struct commit *commit, struct diff_options *options,
+ unsigned char *sha1)
+{
+ if (commit->parents)
+ diff_tree_sha1(commit->parents->item->object.sha1,
+ commit->object.sha1, "", options);
+ else
+ diff_root_tree_sha1(commit->object.sha1, "", options);
+ diffcore_std(options);
+ return diff_flush_patch_id(options, sha1);
+}
+
+static uint32_t take2(const unsigned char *id)
+{
+ return ((id[0] << 8) | id[1]);
+}
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ * do {
+ * int mi = (lo + hi) / 2;
+ * int cmp = "entry pointed at by mi" minus "target";
+ * if (!cmp)
+ * return (mi is the wanted one)
+ * if (cmp > 0)
+ * hi = mi; "mi is larger than target"
+ * else
+ * lo = mi+1; "mi is smaller than target"
+ * } while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ * above the target (it could be at the target), hi points at a
+ * slot that is guaranteed to be above the target (it can never
+ * be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ * as lo, but never can be the same as hi), and check if it hits
+ * the target. There are three cases:
+ *
+ * - if it is a hit, we are happy.
+ *
+ * - if it is strictly higher than the target, we update hi with
+ * it.
+ *
+ * - if it is strictly lower than the target, we update lo to be
+ * one slot after it, because we allow lo to be at the target.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied. When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ */
+static int patch_pos(struct patch_id **table, int nr, const unsigned char *id)
+{
+ int hi = nr;
+ int lo = 0;
+ int mi = 0;
+
+ if (!nr)
+ return -1;
+
+ if (nr != 1) {
+ unsigned lov, hiv, miv, ofs;
+
+ for (ofs = 0; ofs < 18; ofs += 2) {
+ lov = take2(table[0]->patch_id + ofs);
+ hiv = take2(table[nr-1]->patch_id + ofs);
+ miv = take2(id + ofs);
+ if (miv < lov)
+ return -1;
+ if (hiv < miv)
+ return -1 - nr;
+ if (lov != hiv) {
+ /*
+ * At this point miv could be equal
+ * to hiv (but id could still be higher);
+ * the invariant of (mi < hi) should be
+ * kept.
+ */
+ mi = (nr-1) * (miv - lov) / (hiv - lov);
+ if (lo <= mi && mi < hi)
+ break;
+ die("oops");
+ }
+ }
+ if (18 <= ofs)
+ die("cannot happen -- lo and hi are identical");
+ }
+
+ do {
+ int cmp;
+ cmp = hashcmp(table[mi]->patch_id, id);
+ if (!cmp)
+ return mi;
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi + 1;
+ mi = (hi + lo) / 2;
+ } while (lo < hi);
+ return -lo-1;
+}
+
+#define BUCKET_SIZE 190 /* 190 * 21 = 3990, with slop close enough to 4K */
+struct patch_id_bucket {
+ struct patch_id_bucket *next;
+ int nr;
+ struct patch_id bucket[BUCKET_SIZE];
+};
+
+int init_patch_ids(struct patch_ids *ids)
+{
+ memset(ids, 0, sizeof(*ids));
+ diff_setup(&ids->diffopts);
+ ids->diffopts.recursive = 1;
+ if (diff_setup_done(&ids->diffopts) < 0)
+ return error("diff_setup_done failed");
+ return 0;
+}
+
+int free_patch_ids(struct patch_ids *ids)
+{
+ struct patch_id_bucket *next, *patches;
+
+ free(ids->table);
+ for (patches = ids->patches; patches; patches = next) {
+ next = patches->next;
+ free(patches);
+ }
+ return 0;
+}
+
+static struct patch_id *add_commit(struct commit *commit,
+ struct patch_ids *ids,
+ int no_add)
+{
+ struct patch_id_bucket *bucket;
+ struct patch_id *ent;
+ unsigned char sha1[20];
+ int pos;
+
+ if (commit_patch_id(commit, &ids->diffopts, sha1))
+ return NULL;
+ pos = patch_pos(ids->table, ids->nr, sha1);
+ if (0 <= pos)
+ return ids->table[pos];
+ if (no_add)
+ return NULL;
+
+ pos = -1 - pos;
+
+ bucket = ids->patches;
+ if (!bucket || (BUCKET_SIZE <= bucket->nr)) {
+ bucket = xcalloc(1, sizeof(*bucket));
+ bucket->next = ids->patches;
+ ids->patches = bucket;
+ }
+ ent = &bucket->bucket[bucket->nr++];
+ hashcpy(ent->patch_id, sha1);
+
+ if (ids->alloc <= ids->nr) {
+ ids->alloc = alloc_nr(ids->nr);
+ ids->table = xrealloc(ids->table, sizeof(ent) * ids->alloc);
+ }
+ if (pos < ids->nr)
+ memmove(ids->table + pos + 1, ids->table + pos,
+ sizeof(ent) * (ids->nr - pos));
+ ids->nr++;
+ ids->table[pos] = ent;
+ return ids->table[pos];
+}
+
+struct patch_id *has_commit_patch_id(struct commit *commit,
+ struct patch_ids *ids)
+{
+ return add_commit(commit, ids, 1);
+}
+
+struct patch_id *add_commit_patch_id(struct commit *commit,
+ struct patch_ids *ids)
+{
+ return add_commit(commit, ids, 0);
+}
--- /dev/null
+#ifndef PATCH_IDS_H
+#define PATCH_IDS_H
+
+struct patch_id {
+ unsigned char patch_id[20];
+ char seen;
+};
+
+struct patch_ids {
+ struct diff_options diffopts;
+ int nr, alloc;
+ struct patch_id **table;
+ struct patch_id_bucket *patches;
+};
+
+int init_patch_ids(struct patch_ids *);
+int free_patch_ids(struct patch_ids *);
+struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
+struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);
+
+#endif /* PATCH_IDS_H */
#include "revision.h"
#include "grep.h"
#include "reflog-walk.h"
+#include "patch-ids.h"
static char *path_name(struct name_path *path, const char *name)
{
}
}
+static void cherry_pick_list(struct commit_list *list)
+{
+ struct commit_list *p;
+ int left_count = 0, right_count = 0;
+ int left_first;
+ struct patch_ids ids;
+
+ /* First count the commits on the left and on the right */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ unsigned flags = commit->object.flags;
+ if (flags & BOUNDARY)
+ ;
+ else if (flags & SYMMETRIC_LEFT)
+ left_count++;
+ else
+ right_count++;
+ }
+
+ left_first = left_count < right_count;
+ init_patch_ids(&ids);
+
+ /* Compute patch-ids for one side */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ unsigned flags = commit->object.flags;
+
+ if (flags & BOUNDARY)
+ continue;
+ /*
+ * If we have fewer left, left_first is set and we omit
+ * commits on the right branch in this loop. If we have
+ * fewer right, we skip the left ones.
+ */
+ if (left_first != !!(flags & SYMMETRIC_LEFT))
+ continue;
+ commit->util = add_commit_patch_id(commit, &ids);
+ }
+
+ /* Check the other side */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ struct patch_id *id;
+ unsigned flags = commit->object.flags;
+
+ if (flags & BOUNDARY)
+ continue;
+ /*
+ * If we have fewer left, left_first is set and we omit
+ * commits on the left branch in this loop.
+ */
+ if (left_first == !!(flags & SYMMETRIC_LEFT))
+ continue;
+
+ /*
+ * Have we seen the same patch id?
+ */
+ id = has_commit_patch_id(commit, &ids);
+ if (!id)
+ continue;
+ id->seen = 1;
+ commit->object.flags |= SHOWN;
+ }
+
+ /* Now check the original side for seen ones */
+ for (p = list; p; p = p->next) {
+ struct commit *commit = p->item;
+ struct patch_id *ent;
+
+ ent = commit->util;
+ if (!ent)
+ continue;
+ if (ent->seen)
+ commit->object.flags |= SHOWN;
+ commit->util = NULL;
+ }
+
+ free_patch_ids(&ids);
+}
+
static void limit_list(struct rev_info *revs)
{
struct commit_list *list = revs->commits;
continue;
p = &commit_list_insert(commit, p)->next;
}
+ if (revs->cherry_pick)
+ cherry_pick_list(newlist);
+
revs->commits = newlist;
}
revs->min_age = -1;
revs->skip_count = -1;
revs->max_count = -1;
+ revs->subject_prefix = "PATCH";
revs->prune_fn = NULL;
revs->prune_data = NULL;
revs->left_right = 1;
continue;
}
+ if (!strcmp(arg, "--cherry-pick")) {
+ revs->cherry_pick = 1;
+ continue;
+ }
if (!strcmp(arg, "--objects")) {
revs->tag_objects = 1;
revs->tree_objects = 1;
left_right:1,
parents:1,
reverse:1,
+ cherry_pick:1,
first_parent_only:1;
/* Diff flags */
const char *add_signoff;
const char *extra_headers;
const char *log_reencode;
+ const char *subject_prefix;
int no_inline;
/* Filter by commit log message */
'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
'git-ls-files --error-unmatch baz'
+test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
+ git rm --ignore-unmatch nonexistent
+'
+
+test_expect_success '"rm" command printed' '
+ echo frotz > test-file &&
+ git add test-file &&
+ git commit -m "add file for rm test" &&
+ git rm test-file > rm-output &&
+ test `egrep "^rm " rm-output | wc -l` = 1 &&
+ rm -f test-file rm-output &&
+ git commit -m "remove file from rm test"
+'
+
+test_expect_success '"rm" command suppressed with --quiet' '
+ echo frotz > test-file &&
+ git add test-file &&
+ git commit -m "add file for rm --quiet test" &&
+ git rm --quiet test-file > rm-output &&
+ test `wc -l < rm-output` = 0 &&
+ rm -f test-file rm-output &&
+ git commit -m "remove file from rm --quiet test"
+'
+
# Now, failure cases.
test_expect_success 'Re-add foo and baz' '
git add foo baz &&
! test -d frotz
'
+test_expect_failure 'Remove nonexistent file returns nonzero exit status' '
+ git rm nonexistent
+'
+
test_done
format-patch --inline --stdout initial..side
format-patch --inline --stdout initial..master^
format-patch --inline --stdout initial..master
+format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
diff --abbrev initial..side
diff -r initial..side
--- /dev/null
+$ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [TESTCASE] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file2 | 3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [TESTCASE] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file1 | 3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [TESTCASE] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub | 2 ++
+ file0 | 3 +++
+ file3 | 4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch; name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline; filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='git-shortlog
+'
+
+. ./test-lib.sh
+
+echo 1 > a1
+git add a1
+tree=$(git write-tree)
+commit=$( (echo "Test"; echo) | git commit-tree $tree )
+git update-ref HEAD $commit
+
+echo 2 > a1
+git commit -m "This is a very, very long first line for the commit message to see if it is wrapped correctly" a1
+
+# test if the wrapping is still valid when replacing all i's by treble clefs.
+echo 3 > a1
+git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\360\235\204\236')" a1
+
+# now fsck up the utf8
+git repo-config i18n.commitencoding non-utf-8
+echo 4 > a1
+git commit -m "$(echo "This is a very, very long first line for the commit message to see if it is wrapped correctly" | sed "s/i/1234/g" | tr 1234 '\370\235\204\236')" a1
+
+echo 5 > a1
+git commit -m "a 12 34 56 78" a1
+
+git shortlog -w HEAD > out
+
+cat > expect << EOF
+A U Thor (5):
+ Test
+ This is a very, very long first line for the commit message to see if
+ it is wrapped correctly
+ Th𝄞s 𝄞s a very, very long f𝄞rst l𝄞ne for the comm𝄞t message to see 𝄞f
+ 𝄞t 𝄞s wrapped correctly
+ Thø\9d\84\9es ø\9d\84\9es a very, very long fø\9d\84\9erst lø\9d\84\9ene for the commø\9d\84\9et
+ message to see ø\9d\84\9ef ø\9d\84\9et ø\9d\84\9es wrapped correctly
+ a 12 34
+ 56 78
+
+EOF
+
+test_expect_success 'shortlog wrapping' 'diff -u expect out'
+
+test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Christian Couder
+#
+test_description='Tests git-bisect functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+ _line=$1
+ _file=$2
+
+ if [ -f "$_file" ]; then
+ echo "$_line" >> $_file || return $?
+ MSG="Add <$_line> into <$_file>."
+ else
+ echo "$_line" > $_file || return $?
+ git add $_file || return $?
+ MSG="Create file <$_file> with <$_line> inside."
+ fi
+
+ git-commit -m "$MSG" $_file
+}
+
+HASH1=
+HASH3=
+HASH4=
+
+test_expect_success \
+ 'set up basic repo with 1 file (hello) and 4 commits' \
+ 'add_line_into_file "1: Hello World" hello &&
+ add_line_into_file "2: A new day for git" hello &&
+ add_line_into_file "3: Another new day for git" hello &&
+ add_line_into_file "4: Ciao for now" hello &&
+ HASH1=$(git rev-list HEAD | tail -1) &&
+ HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
+ HASH4=$(git rev-list HEAD | head -1)'
+
+test_expect_success 'bisect starts with only one bad' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect bad $HASH4 &&
+ git bisect next
+'
+
+test_expect_success 'bisect does not start with only one good' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 || return 1
+
+ if git bisect next
+ then
+ echo Oops, should have failed.
+ false
+ else
+ :
+ fi
+'
+
+test_expect_success 'bisect start with one bad and good' '
+ git bisect reset &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect next
+'
+
+# We want to automatically find the commit that
+# introduced "Another" into hello.
+test_expect_success \
+ '"git bisect run" simple case' \
+ 'echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "grep Another hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start &&
+ git bisect good $HASH1 &&
+ git bisect bad $HASH4 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+ git bisect reset'
+
+# We want to automatically find the commit that
+# introduced "Ciao" into hello.
+test_expect_success \
+ '"git bisect run" with more complex "git bisect start"' \
+ 'echo "#"\!"/bin/sh" > test_script.sh &&
+ echo "grep Ciao hello > /dev/null" >> test_script.sh &&
+ echo "test \$? -ne 0" >> test_script.sh &&
+ chmod +x test_script.sh &&
+ git bisect start $HASH4 $HASH1 &&
+ git bisect run ./test_script.sh > my_bisect_log.txt &&
+ grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+ git bisect reset'
+
+#
+#
+test_done
+
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2007 Christian Couder
-#
-test_description='Tests git-bisect functionality'
-
-exec </dev/null
-
-. ./test-lib.sh
-
-add_line_into_file()
-{
- _line=$1
- _file=$2
-
- if [ -f "$_file" ]; then
- echo "$_line" >> $_file || return $?
- MSG="Add <$_line> into <$_file>."
- else
- echo "$_line" > $_file || return $?
- git add $_file || return $?
- MSG="Create file <$_file> with <$_line> inside."
- fi
-
- git-commit -m "$MSG" $_file
-}
-
-HASH1=
-HASH3=
-HASH4=
-
-test_expect_success \
- 'set up basic repo with 1 file (hello) and 4 commits' \
- 'add_line_into_file "1: Hello World" hello &&
- add_line_into_file "2: A new day for git" hello &&
- add_line_into_file "3: Another new day for git" hello &&
- add_line_into_file "4: Ciao for now" hello &&
- HASH1=$(git rev-list HEAD | tail -1) &&
- HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
- HASH4=$(git rev-list HEAD | head -1)'
-
-test_expect_success 'bisect starts with only one bad' '
- git bisect reset &&
- git bisect start &&
- git bisect bad $HASH4 &&
- git bisect next
-'
-
-test_expect_success 'bisect starts with only one good' '
- git bisect reset &&
- git bisect start &&
- git bisect good $HASH1 || return 1
-
- if git bisect next
- then
- echo Oops, should have failed.
- false
- else
- :
- fi
-'
-
-test_expect_success 'bisect start with one bad and good' '
- git bisect reset &&
- git bisect start &&
- git bisect good $HASH1 &&
- git bisect bad $HASH4 &&
- git bisect next
-'
-
-# We want to automatically find the commit that
-# introduced "Another" into hello.
-test_expect_success \
- '"git bisect run" simple case' \
- 'echo "#"\!"/bin/sh" > test_script.sh &&
- echo "grep Another hello > /dev/null" >> test_script.sh &&
- echo "test \$? -ne 0" >> test_script.sh &&
- chmod +x test_script.sh &&
- git bisect start &&
- git bisect good $HASH1 &&
- git bisect bad $HASH4 &&
- git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH3 is first bad commit" my_bisect_log.txt &&
- git bisect reset'
-
-# We want to automatically find the commit that
-# introduced "Ciao" into hello.
-test_expect_success \
- '"git bisect run" with more complex "git bisect start"' \
- 'echo "#"\!"/bin/sh" > test_script.sh &&
- echo "grep Ciao hello > /dev/null" >> test_script.sh &&
- echo "test \$? -ne 0" >> test_script.sh &&
- chmod +x test_script.sh &&
- git bisect start $HASH4 $HASH1 &&
- git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH4 is first bad commit" my_bisect_log.txt &&
- git bisect reset'
-
-#
-#
-test_done
-
allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
# check for no description
+projectdesc=$(sed -e '1p' "$GIT_DIR/description")
if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
echo "*** Project description file hasn't been set" >&2
exit 1
fi
# --- Check types
-newrev_type=$(git-cat-file -t $newrev)
+# if $newrev is 0000...0000, it's a commit to delete a branch
+if [ -z "${newrev##0*}" ]; then
+ newrev_type=commit
+else
+ newrev_type=$(git-cat-file -t $newrev)
+fi
case "$refname","$newrev_type" in
refs/tags/*,commit)