/git-remote-https
/git-remote-ftp
/git-remote-ftps
+/git-remote-testgit
/git-repack
/git-replace
/git-repo-config
/git-write-tree
/git-core-*/?*
/gitk-git/gitk-wish
+/gitweb/GITWEB-BUILD-OPTIONS
/gitweb/gitweb.cgi
+/gitweb/gitweb.min.*
/test-chmtime
/test-ctype
/test-date
*.exe
*.[aos]
*.py[co]
-*.o.d
+.depend/
*+
/config.mak
/autom4te.cache
# same person appearing not to be so.
#
+Alex Bennée <kernel-hacker@bennee.com>
Alexander Gavrilov <angavrilov@gmail.com>
Aneesh Kumar K.V <aneesh.kumar@gmail.com>
Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
David D. Kilzer <ddkilzer@kilzer.net>
David Kågedal <davidk@lysator.liu.se>
David S. Miller <davem@davemloft.net>
+Deskin Miller <deskinm@umich.edu>
Dirk Süsserott <newsletter@dirk.my1.cc>
Fredrik Kuivinen <freku045@student.liu.se>
H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
Uwe Kleine-König <uzeisberger@io.fsforth.de>
Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de>
Ville Skyttä <scop@xemacs.org>
+Vitaly "_Vi" Shukela <public_vi@tut.by>
William Pursell <bill.pursell@gmail.com>
YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
anonymous <linux@horizon.com>
mv $@+ $@
user-manual.xml: user-manual.txt user-manual.conf
- $(QUIET_ASCIIDOC)$(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book $<
+ $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
+ $(ASCIIDOC) $(ASCIIDOC_EXTRA) -b docbook -d book -o $@+ $< && \
+ mv $@+ $@
technical/api-index.txt: technical/api-index-skel.txt \
technical/api-index.sh $(patsubst %,%.txt,$(API_DOCS))
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
- $(QUIET_XSLTPROC)xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
+ $(QUIET_XSLTPROC)$(RM) $@+ $@ && \
+ xsltproc $(XSLTOPTS) -o $@+ $(XSLT) $< && \
+ mv $@+ $@
git.info: user-manual.texi
$(QUIET_MAKEINFO)$(MAKEINFO) --no-split -o $@ user-manual.texi
--- /dev/null
+Git v1.7.0.5 Release Notes
+==========================
+
+Fixes since v1.7.0.4
+--------------------
+
+ * "git daemon" failed to compile on platforms without sockaddr_storage type.
+
+ * Output from "git rev-list --pretty=oneline" was unparsable when a
+ commit did not have any message, which is abnormal but possible in a
+ repository converted from foreign scm.
+
+ * "git stash show <commit-that-is-not-a-stash>" gave an error message
+ that was not so useful. Reworded the message to "<it> is not a
+ stash".
+
+ * Python scripts in contrib/ area now start with "#!/usr/bin/env python"
+ to honor user's PATH.
+
+ * "git imap-send" used to mistake any line that begins with "From " as a
+ message separator in format-patch output.
+
+ * Smart http server backend failed to report an internal server error and
+ infinitely looped instead after output pipe was closed.
+
+And other minor fixes and documentation updates.
--- /dev/null
+Git v1.7.0.6 Release Notes
+==========================
+
+Fixes since v1.7.0.5
+--------------------
+
+ * "git diff --stat" used "int" to count the size of differences,
+ which could result in overflowing.
+
+ * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
+ newer tools in the git toolset.
+
+And other minor fixes and documentation updates.
-Git v1.7.1 Release Notes (draft)
-================================
+Git v1.7.1 Release Notes
+========================
Updates since v1.7.0
--------------------
+ * Eric Raymond is the maintainer of updated CIAbot scripts, in contrib/.
+
+ * gitk updates.
+
* Some commands (e.g. svn and http interfaces) that interactively ask
- password can be told to use an external program given via GIT_ASKPASS.
+ for a password can be told to use an external program given via
+ GIT_ASKPASS.
+
+ * Conflict markers that lead the common ancestor in diff3-style output
+ now have a label, which hopefully would help third-party tools that
+ expect one.
+
+ * Comes with an updated bash-completion script.
* "git am" learned "--keep-cr" option to handle inputs that are
- mixture of changes to files with and without CRLF line endings.
+ a mixture of changes to files with and without CRLF line endings.
* "git cvsimport" learned -R option to leave revision mapping between
CVS revisions and resulting git commits.
- * "git diff --submodule" notices and descries dirty submodules.
+ * "git diff --submodule" notices and describes dirty submodules.
* "git for-each-ref" learned %(symref), %(symref:short) and %(flag)
tokens.
* "git log -p --first-parent -m" shows one-parent diff for merge
commits, instead of showing combined diff.
- * "git merge-file" learned to use custom conflict marker size and also use
- the "union merge" behaviour.
+ * "git merge-file" learned to use custom conflict marker size and also
+ to use the "union merge" behaviour.
- * "git notes" command has been rewritten in C and learned quite a
- many commands and features to help you carry notes forward across
- rebases and amends.
+ * "git notes" command has been rewritten in C and learned many commands
+ and features to help you carry notes forward across rebases and amends.
* "git request-pull" identifies the commit the request is relative to in
a more readable way.
near the tip while preserving your local changes in a way similar
to how "git checkout branch" does.
- * "git status" notices and descries dirty submodules.
+ * "git status" notices and describes dirty submodules.
* "git svn" should work better when interacting with repositories
with CRLF line endings.
* "git imap-send" learned to support CRAM-MD5 authentication.
+ * "gitweb" installation procedure can use "minified" js/css files
+ better.
+
+ * Various documentation updates.
+
Fixes since v1.7.0
------------------
* "git add frotz/nitfol" did not complain when the entire frotz/ directory
was ignored.
----
-exec >/var/tmp/1
-echo O=$(git describe)
-O=v1.7.0.3-310-g99f5b08
-git shortlog --no-merges ^maint $O..
+ * "git diff --stat" used "int" to count the size of differences,
+ which could result in overflowing.
+
+ * "git rev-list --pretty=oneline" didn't terminate a record with LF for
+ commits without any message.
+
+ * "git rev-list --abbrev-commit" defaulted to 40-byte abbreviations, unlike
+ newer tools in the git toolset.
--- /dev/null
+Git v1.7.2 Release Notes (draft)
+================================
+
+Updates since v1.7.1
+--------------------
+
+ * After "git apply --whitespace=fix" removed trailing blank lines in an
+ patch in a patch series, it failed to apply later patches that depend
+ on the presense of such blank lines.
+
+ * The output from the textconv filter used by "git diff" can be cached to
+ speed up their reuse.
+
+ * "git send-email" learned --smtp-domain option to specify the domainname
+ used in the EHLO/HELO exchange.
+
+ * "git revert" learned --strategy option to specify the merge strategy.
+
+ * The whitespace rules used in "git apply --whitespace" and "git diff"
+ gained a new member in the family (tab-in-indent) to help projects with
+ policy to indent only with spaces.
+
+ * Authentication over http transport can now be made lazily, in that the
+ request can first go to a URL without username, get a 401 response and
+ then the client will ask for the username to use.
+
+
+Fixes since v1.7.1
+------------------
+
+ * In 1.7.1, "git status" stopped refreshing the index by mistake.
+
+All of the fixes in v1.7.1.X maintenance series are included in this
+release, unless otherwise noted.
+
+--
+exec >/var/tmp/1
+O=v1.7.1-77-gb751157
+echo O=$(git describe master)
+git shortlog --no-merges master ^maint ^$O
GMail does not appear to have any way to turn off line wrapping in the web
interface, so this will mangle any emails that you send. You can however
use any IMAP email client to connect to the google imap server, and forward
-the emails through that. Just make sure to disable line wrapping in that
-email client. Alternatively, use "git send-email" instead.
+the emails through that.
-Submitting properly formatted patches via Gmail is simple now that
-IMAP support is available. First, edit your ~/.gitconfig to specify your
+To submit using the IMAP interface, first, edit your ~/.gitconfig to specify your
account settings:
[imap]
You might need to instead use: folder = "[Google Mail]/Drafts" if you get an error
that the "Folder doesn't exist".
-Next, ensure that your Gmail settings are correct. In "Settings" the
-"Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
+Once your commits are ready to be sent to the mailing list, run the
+following command to send the patch emails to your Gmail Drafts
+folder.
-Once your commits are ready to send to the mailing list, run the following
-command to send the patch emails to your Gmail Drafts folder.
+ $ git format-patch --cover-letter -M --stdout origin/master | git imap-send
- $ git format-patch -M --stdout origin/master | git imap-send
+Just make sure to disable line wrapping in the email client (GMail web
+interface will line wrap no matter what, so you need to use a real
+IMAP client).
-Go to your Gmail account, open the Drafts folder, find the patch email, fill
-in the To: and CC: fields and send away!
+Alternatively, you can use "git send-email" and send your patches
+through the GMail SMTP server. edit ~/.gitconfig to specify your
+account settings:
+
+[sendemail]
+ smtpencryption = tls
+ smtpserver = smtp.gmail.com
+ smtpuser = user@gmail.com
+ smtppass = p4ssw0rd
+ smtpserverport = 587
+
+Once your commits are ready to be sent to the mailing list, run the
+following commands:
+ $ git format-patch --cover-letter -M origin/master -o outgoing/
+ $ git send-email outgoing/*
of the --date option at linkgit:git-log[1].
-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), the 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.
+ Detect moved or copied lines within a file. When a commit
+ moves or copies a block of lines (e.g. the original file
+ has A and then B, and the commit changes it to B and then
+ A), the traditional 'blame' algorithm notices only half of
+ the movement and 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 by
+ running extra passes of inspection.
+
<num> is optional but it is the lower bound on the number of
alphanumeric characters that git must detect as moving
commit.
-C|<num>|::
- In addition to `-M`, detect lines copied from other
+ In addition to `-M`, detect lines moved or 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,
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
+ `LF` when reading from the work tree, and convert in reverse when
+ writing to the work tree. 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. A file is considered
+ reading from the work tree but files are written out to the work
+ tree with `LF` at the end of lines. A file is considered
"text" (i.e. be subjected to the autocrlf mechanism) based on
the file's `crlf` attribute, or if `crlf` is unspecified,
based on the file's contents. See linkgit:gitattributes[5].
error (enabled by default).
* `indent-with-non-tab` treats a line that is indented with 8 or more
space characters as an error (not enabled by default).
+* `tab-in-indent` treats a tab character in the initial indent part of
+ the line as an error (not enabled by default).
* `blank-at-eof` treats blank lines added at the end of file as an error
(enabled by default).
* `trailing-space` is a short-hand to cover both `blank-at-eol` and
gc.aggressiveWindow::
The window size parameter used in the delta compression
algorithm used by 'git gc --aggressive'. This defaults
- to 10.
+ to 250.
gc.auto::
When there are approximately more than this many loose
following alternatives: {relative,local,default,iso,rfc,short}.
See linkgit:git-log[1].
+log.decorate::
+ Print out the ref names of any commits that are shown by the log
+ command. If 'short' is specified, the ref name prefixes 'refs/heads/',
+ 'refs/tags/' and 'refs/remotes/' will not be printed. If 'full' is
+ specified, the full ref name (including prefix) will be printed.
+ This is the same as the log commands '--decorate' option.
+
log.showroot::
If true, the initial commit will be shown as a big creation event.
This is equivalent to a diff against an empty tree.
automatically copies your notes from the original to the
rewritten commit. Defaults to `true`, but see
"notes.rewriteRef" below.
-+
-This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
-environment variable, which must be a colon separated list of refs or
-globs.
notes.rewriteMode::
When copying notes during a rewrite (see the
+
Does not have a default value; you must configure this variable to
enable note rewriting.
++
+This setting can be overridden with the `GIT_NOTES_REWRITE_REF`
+environment variable, which must be a colon separated list of refs or
+globs.
pack.window::
The size of the window used by linkgit:git-pack-objects[1] when no
the ref. Use this to prevent such a ref deletion via a push.
receive.denyCurrentBranch::
- If set to true or "refuse", receive-pack will deny a ref update
+ If set to true or "refuse", git-receive-pack will deny a ref update
to the currently checked out branch of a non-bare repository.
Such a push is potentially dangerous because it brings the HEAD
out of sync with the index and working tree. If set to "warn",
sendemail.suppresscc::
sendemail.suppressfrom::
sendemail.to::
+sendemail.smtpdomain::
sendemail.smtpserver::
sendemail.smtpserverport::
sendemail.smtpuser::
ifndef::git-format-patch[]
-p::
-u::
+--patch::
Generate patch (see section on generating patches).
{git-diff? This is the default.}
endif::git-format-patch[]
pathnames and use NULs as output field terminators.
endif::git-log[]
ifndef::git-log[]
- When `--raw` or `--numstat` has been given, do not munge
- pathnames and use NULs as output field terminators.
+ When `--raw`, `--numstat`, `--name-only` or `--name-status` has been
+ given, do not munge pathnames and use NULs as output field terminators.
endif::git-log[]
+
Without this option, each pathname output will have TAB, LF, double quotes,
Everyday GIT With 20 Commands Or So
===================================
-<<Basic Repository>> commands are needed by people who have a
-repository --- that is everybody, because every working tree of
-git is a repository.
-
-In addition, <<Individual Developer (Standalone)>> commands are
-essential for anybody who makes a commit, even for somebody who
-works alone.
+<<Individual Developer (Standalone)>> commands are essential for
+anybody who makes a commit, even for somebody who works alone.
If you work with other people, you will need commands listed in
the <<Individual Developer (Participant)>> section as well.
of git repositories.
-Basic Repository[[Basic Repository]]
-------------------------------------
-
-Everybody uses these commands to maintain git repositories.
-
- * linkgit:git-init[1] or linkgit:git-clone[1] to create a
- new repository.
-
- * linkgit:git-fsck[1] to check the repository for errors.
-
- * linkgit:git-gc[1] to do common housekeeping tasks such as
- repack and prune.
-
-Examples
-~~~~~~~~
-
-Check health and remove cruft.::
-+
-------------
-$ git fsck <1>
-$ git count-objects <2>
-$ git gc <3>
-------------
-+
-<1> running without `\--full` is usually cheap and assures the
-repository health reasonably well.
-<2> check how many loose objects there are and how much
-disk space is wasted by not repacking.
-<3> repacks the local repository and performs other housekeeping tasks.
-
-Repack a small project into single pack.::
-+
-------------
-$ git gc <1>
-------------
-+
-<1> pack all the objects reachable from the refs into one pack,
-then remove the other packs.
-
-
Individual Developer (Standalone)[[Individual Developer (Standalone)]]
----------------------------------------------------------------------
other people, and works alone in a single repository, using the
following commands.
+ * linkgit:git-init[1] to create a new repository.
+
* linkgit:git-show-branch[1] to see where you are.
* linkgit:git-log[1] to see what happened.
OPTIONS
-------
-d::
- Delete a branch. The branch must be fully merged in HEAD.
+ Delete a branch. The branch must be fully merged in its
+ upstream branch, or in `HEAD` if no upstream was set with
+ `--track` or `--set-upstream`.
-D::
Delete a branch irrespective of its merged status.
Create the branch's reflog. This activates recording of
all changes made to the branch ref, enabling use of date
based sha1 expressions such as "<branchname>@\{yesterday}".
+ Note that in non-bare repositories, reflogs are usually
+ enabled by default by the `core.logallrefupdates` config option.
-f::
--force::
--------
[verse]
'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
'git checkout' --patch [<tree-ish>] [--] [<paths>...]
Create the new branch's reflog; see linkgit:git-branch[1] for
details.
+--orphan::
+ Create a new branch named <new_branch>, unparented to any other
+ branch. The new branch you switch to does not have any commit
+ and after the first one it will become the root of a new history
+ completely unconnected from all the other branches.
++
+When you use "--orphan", the index and the working tree are kept intact.
+This allows you to start a new history that records set of paths similar
+to that of the start-point commit, which is useful when you want to keep
+different branches for different audiences you are working to like when
+you have an open source and commercial versions of a software, for example.
++
+If you want to start a disconnected history that records set of paths
+totally different from the original branch, you may want to first clear
+the index and the working tree, by running "git rm -rf ." from the
+top-level of the working tree, before preparing your files (by copying
+from elsewhere, extracting a tarball, etc.) in the working tree.
+
-m::
--merge::
When switching branches,
[verse]
'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
[(-c | -C) <commit>] [-F <file> | -m <msg>] [--reset-author]
- [--allow-empty] [--no-verify] [-e] [--author=<author>]
+ [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
[--date=<date>] [--cleanup=<mode>] [--status | --no-status] [--]
[[-i | -o ]<file>...]
from making such a commit. This option bypasses the safety, and
is primarily for use by foreign scm interface scripts.
+--allow-empty-message::
+ Like --allow-empty this command is primarily for use by foreign
+ scm interface scripts. It allows you to create a commit with an
+ empty commit message without using plumbing commands like
+ linkgit:git-commit-tree[1].
+
--cleanup=<mode>::
This option sets how the commit message is cleaned up.
The '<mode>' can be one of 'verbatim', 'whitespace', 'strip',
SYNOPSIS
--------
-'git fetch' <options> <repository> <refspec>...
+'git fetch' [<options>] [<repository> [<refspec>...]]
-'git fetch' <options> <group>
+'git fetch' [<options>] <group>
-'git fetch' --multiple <options> [<repository> | <group>]...
+'git fetch' --multiple [<options>] [<repository> | <group>]...
-'git fetch' --all <options>
+'git fetch' --all [<options>]
DESCRIPTION
objectname::
The object name (aka SHA-1).
+ For a non-ambiguous abbreviation of the object name append `:short`.
upstream::
The name of a local ref which can be considered ``upstream''
This command uploads a mailbox generated with 'git format-patch'
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
-files directly.
+files directly. The command also works with any general mailbox
+in which emails have the fields "From", "Date", and "Subject" in
+that order.
Typical usage is something like:
users may wish to visit this web page for more information:
http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
-
-BUGS
-----
-Doesn't handle lines starting with "From " in the message body.
-
-
Author
------
Derived from isync 1.0.1 by Mike McCormack.
and <until>, see "SPECIFYING REVISIONS" section in
linkgit:git-rev-parse[1].
---decorate[=short|full]::
+--no-decorate::
+--decorate[=short|full|no]::
Print out the ref names of any commits that are shown. If 'short' is
specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
'refs/remotes/' will not be printed. If 'full' is specified, the
SYNOPSIS
--------
[verse]
-'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
+'git merge' [-n] [--stat] [--no-commit] [--squash]
+ [-s <strategy>] [-X <strategy-option>]
[--[no-]rerere-autoupdate] [-m <msg>] <commit>...
'git merge' <msg> HEAD <commit>...
[verse]
'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-v | --verbose] [-u | --set-upstream]
- [<repository> <refspec>...]
+ [<repository> [<refspec>...]]
DESCRIPTION
-----------
-f::
--force-rebase::
Force the rebase even if the current branch is a descendant
- of the commit you are rebasing onto. Normally the command will
+ of the commit you are rebasing onto. Normally non-interactive rebase will
exit with the message "Current branch is up to date" in such a
situation.
+ Incompatible with the --interactive option.
++
+You may find this (or --no-ff with an interactive rebase) helpful after
+reverting a topic branch merge, as this option recreates the topic branch with
+fresh commits so it can be remerged successfully without needing to "revert
+the reversion" (see the
+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).
--ignore-whitespace::
--whitespace=<option>::
--ignore-date::
These flags are passed to 'git am' to easily change the dates
of the rebased commits (see linkgit:git-am[1]).
+ Incompatible with the --interactive option.
-i::
--interactive::
commit to be modified, and change the action of the moved
commit from `pick` to `squash` (or `fixup`).
+
-This option is only valid when '--interactive' option is used.
+This option is only valid when the '--interactive' option is used.
+
+--no-ff::
+ With --interactive, cherry-pick all rebased commits instead of
+ fast-forwarding over the unchanged ones. This ensures that the
+ entire history of the rebased branch is composed of new commits.
++
+Without --interactive, this is a synonym for --force-rebase.
++
+You may find this helpful after reverting a topic branch merge, as this option
+recreates the topic branch with fresh commits so it can be remerged
+successfully without needing to "revert the reversion" (see the
+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).
include::merge-strategies.txt[]
NAME
----
-git-remote-helpers - Helper programs for interoperation with remote git
+git-remote-helpers - Helper programs to interact with remote repositories
SYNOPSIS
--------
-'git remote-<transport>' <remote>
+'git remote-<transport>' <repository> [<URL>]
DESCRIPTION
-----------
-These programs are normally not used directly by end users, but are
-invoked by various git programs that interact with remote repositories
-when the repository they would operate on will be accessed using
-transport code not linked into the main git binary. Various particular
-helper programs will behave as documented here.
+Remote helper programs are normally not used directly by end users,
+but they are invoked by git when it needs to interact with remote
+repositories git does not support natively. A given helper will
+implement a subset of the capabilities documented here. When git
+needs to interact with a repository using a remote helper, it spawns
+the helper as an independent process, sends commands to the helper's
+standard input, and expects results from the helper's standard
+output. Because a remote helper runs as an independent process from
+git, there is no need to re-link git to add a new helper, nor any
+need to link the helper with the implementation of git.
+
+Every helper must support the "capabilities" command, which git will
+use to determine what other commands the helper will accept. Other
+commands generally concern facilities like discovering and updating
+remote refs, transporting objects between the object database and
+the remote repository, and updating the local object store.
+
+Helpers supporting the 'fetch' capability can discover refs from the
+remote repository and transfer objects reachable from those refs to
+the local object store. Helpers supporting the 'push' capability can
+transfer local objects to the remote repository and update remote refs.
+
+Git comes with a "curl" family of remote helpers, that handle various
+transport protocols, such as 'git-remote-http', 'git-remote-https',
+'git-remote-ftp' and 'git-remote-ftps'. They implement the capabilities
+'fetch', 'option', and 'push'.
+
+INVOCATION
+----------
+
+Remote helper programs are invoked with one or (optionally) two
+arguments. The first argument specifies a remote repository as in git;
+it is either the name of a configured remote or a URL. The second
+argument specifies a URL; it is usually of the form
+'<transport>://<address>', but any arbitrary string is possible.
+
+When git encounters a URL of the form '<transport>://<address>', where
+'<transport>' is a protocol that it cannot handle natively, it
+automatically invokes 'git remote-<transport>' with the full URL as
+the second argument. If such a URL is encountered directly on the
+command line, the first argument is the same as the second, and if it
+is encountered in a configured remote, the first argument is the name
+of that remote.
+
+A URL of the form '<transport>::<address>' explicitly instructs git to
+invoke 'git remote-<transport>' with '<address>' as the second
+argument. If such a URL is encountered directly on the command line,
+the first argument is '<address>', and if it is encountered in a
+configured remote, the first argument is the name of that remote.
+
+Additionally, when a configured remote has 'remote.<name>.vcs' set to
+'<transport>', git explicitly invokes 'git remote-<transport>' with
+'<name>' as the first argument. If set, the second argument is
+'remote.<name>.url'; otherwise, the second argument is omitted.
COMMANDS
--------
'capabilities'::
Lists the capabilities of the helper, one per line, ending
- with a blank line. Each capability may be preceded with '*'.
- This marks them mandatory for git version using the remote
+ with a blank line. Each capability may be preceded with '*',
+ which marks them mandatory for git version using the remote
helper to understand (unknown mandatory capability is fatal
error).
[<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
a symref, or "?" to indicate that the helper could not get the
value of the ref. A space-separated list of attributes follows
- the name; unrecognized attributes are ignored. After the
- complete list, outputs a blank line.
+ the name; unrecognized attributes are ignored. The list ends
+ with a blank line.
+
If 'push' is supported this may be called as 'list for-push'
to obtain the current refs prior to sending one or more 'push'
commands to the helper.
'option' <name> <value>::
- Set the transport helper option <name> to <value>. Outputs a
+ Sets the transport helper option <name> to <value>. Outputs a
single line containing one of 'ok' (option successfully set),
'unsupported' (option not recognized) or 'error <msg>'
- (option <name> is supported but <value> is not correct
+ (option <name> is supported but <value> is not valid
for it). Options should be set before other commands,
- and may how those commands behave.
+ and may influence the behavior of those commands.
+
Supported if the helper has the "option" capability.
'fetch' <sha1> <name>::
Fetches the given object, writing the necessary objects
to the database. Fetch commands are sent in a batch, one
- per line, and the batch is terminated with a blank line.
+ per line, terminated with a blank line.
Outputs a single blank line when all fetch commands in the
same batch are complete. Only objects which were reported
in the ref list with a sha1 may be fetched this way.
Supported if the helper has the "fetch" capability.
'push' +<src>:<dst>::
- Pushes the given <src> commit or branch locally to the
+ Pushes the given local <src> commit or branch to the
remote branch described by <dst>. A batch sequence of
one or more push commands is terminated with a blank line.
+
by applying the refspecs from the "refspec" capability to the
name of the ref.
+
+Especially useful for interoperability with a foreign versioning
+system.
++
Supported if the helper has the "import" capability.
'connect' <service>::
------------
'fetch'::
- This helper supports the 'fetch' command.
-
'option'::
- This helper supports the option command.
-
'push'::
- This helper supports the 'push' command.
-
'import'::
- This helper supports the 'import' command.
+'connect'::
+ This helper supports the corresponding command with the same name.
'refspec' 'spec'::
When using the import command, expect the source ref to have
all, it must cover all refs reported by the list command; if
it is not used, it is effectively "*:*"
-'connect'::
- This helper supports the 'connect' command.
-
REF LIST ATTRIBUTES
-------------------
OPTIONS
-------
'option verbosity' <N>::
- Change the level of messages displayed by the helper.
- When N is 0 the end-user has asked the process to be
- quiet, and the helper should produce only error output.
- N of 1 is the default level of verbosity, higher values
+ Changes the verbosity of messages displayed by the helper.
+ A value of 0 for N means that processes operate
+ quietly, and the helper produces only error output.
+ 1 is the default level of verbosity, and higher values
of N correspond to the number of -v flags passed on the
command line.
'option progress' \{'true'|'false'\}::
- Enable (or disable) progress messages displayed by the
+ Enables (or disables) progress messages displayed by the
transport helper during a command.
'option depth' <depth>::
- Deepen the history of a shallow repository.
+ Deepens the history of a shallow repository.
'option followtags' \{'true'|'false'\}::
If enabled the helper should automatically fetch annotated
helpers this only applies to the 'push', if supported.
'option servpath <c-style-quoted-path>'::
- Set service path (--upload-pack, --receive-pack etc.) for
- next connect. Remote helper MAY support this option. Remote
- helper MUST NOT rely on this option being set before
+ Sets service path (--upload-pack, --receive-pack etc.) for
+ next connect. Remote helper may support this option, but
+ must not rely on this option being set before
connect request occurs.
+SEE ALSO
+--------
+linkgit:git-remote[1]
+
Documentation
-------------
Documentation by Daniel Barkalow and Ilari Liusvaara
value reverts to plain SMTP. Default is the value of
'sendemail.smtpencryption'.
+--smtp-domain=<FQDN>::
+ Specifies the Fully Qualified Domain Name (FQDN) used in the
+ HELO/EHLO command to the SMTP server. Some servers require the
+ FQDN to match your IP address. If not set, git send-email attempts
+ to determine your FQDN automatically. Default is the value of
+ 'sendemail.smtpdomain'.
+
--smtp-pass[=<password>]::
Password for SMTP-AUTH. The argument is optional: If no
argument is specified, then the empty string is used as
in the previous section for the meaning of these values.
+Use gmail as the smtp server
+----------------------------
+
+Add the following section to the config file:
+
+ [sendemail]
+ smtpencryption = tls
+ smtpserver = smtp.gmail.com
+ smtpuser = yourname@gmail.com
+ smtpserverport = 587
+
+Note: the following perl modules are required
+ Net::SMTP::SSL, MIME::Base64 and Authen::SASL
+
+
Author
------
Written by Ryan Anderson <ryan@michonline.com>
where `PATH1` is the path in the `HEAD`, and ` -> PATH2` part is
shown only when `PATH1` corresponds to a different path in the
-index/worktree (i.e. renamed).
-
-For unmerged entries, `X` shows the status of stage #2 (i.e. ours) and `Y`
-shows the status of stage #3 (i.e. theirs).
-
-For entries that do not have conflicts, `X` shows the status of the index,
-and `Y` shows the status of the work tree. For untracked paths, `XY` are
-`??`.
+index/worktree (i.e. the file is renamed). The 'XY' is a two-letter
+status code.
+
+The fields (including the `->`) are separated from each other by a
+single space. If a filename contains whitespace or other nonprintable
+characters, that field will be quoted in the manner of a C string
+literal: surrounded by ASCII double quote (34) characters, and with
+interior special characters backslash-escaped.
+
+For paths with merge conflicts, `X` and 'Y' show the modification
+states of each side of the merge. For paths that do not have merge
+conflicts, `X` shows the status of the index, and `Y` shows the status
+of the work tree. For untracked paths, `XY` are `??`. Other status
+codes can be interpreted as follows:
+
+* ' ' = unmodified
+* 'M' = modified
+* 'A' = added
+* 'D' = deleted
+* 'R' = renamed
+* 'C' = copied
+* 'U' = updated but unmerged
+
+Ignored files are not listed.
X Y Meaning
-------------------------------------------------
[MD] not updated
M [ MD] updated in index
A [ MD] added to index
- D [ MD] deleted from index
+ D [ M] deleted from index
R [ MD] renamed in index
C [ MD] copied in index
[MARC] index and work tree matches
? ? untracked
-------------------------------------------------
+There is an alternate -z format recommended for machine parsing. In
+that format, the status field is the same, but some other things
+change. First, the '->' is omitted from rename entries and the field
+order is reversed (e.g 'from -> to' becomes 'to from'). Second, a NUL
+(ASCII 0) follows each filename, replacing space as a field separator
+and the terminating newline (but a space still separates the status
+field from the first filename). Third, filenames containing special
+characters are not specially formatted; no quoting or
+backslash-escaping is performed.
CONFIGURATION
-------------
--username;;
Specify the SVN username to perform the commit as. This option overrides
- configuration property 'username'.
+ the 'username' configuration property.
--commit-url;;
Use the specified URL to connect to the destination Subversion
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
[-p|--paginate|--no-pager] [--no-replace-objects]
[--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
+ [-c name=value]
[--help] COMMAND [ARGS]
DESCRIPTION
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.7.0.4/git.html[documentation for release 1.7.0.4]
+* link:v1.7.1/git.html[documentation for release 1.7.1]
* release notes for
+ link:RelNotes-1.7.1.txt[1.7.1].
+
+* link:v1.7.0.6/git.html[documentation for release 1.7.0.6]
+
+* release notes for
+ link:RelNotes-1.7.0.6.txt[1.7.0.6],
+ link:RelNotes-1.7.0.5.txt[1.7.0.5],
link:RelNotes-1.7.0.4.txt[1.7.0.4],
link:RelNotes-1.7.0.3.txt[1.7.0.3],
link:RelNotes-1.7.0.2.txt[1.7.0.2],
because `git --help ...` is converted internally into `git
help ...`.
+-c <name>=<value>::
+ Pass a configuration parameter to the command. The value
+ given will override values from configuration files.
+ The <name> is expected in the same format as listed by
+ 'git config' (subkeys separated by dots).
+
--exec-path::
Path to wherever your core git programs are installed.
This can also be controlled by setting the GIT_EXEC_PATH
a GIT_DIR set on the command line or in the environment.
(Useful for excluding slow-loading network directories.)
+'GIT_DISCOVERY_ACROSS_FILESYSTEM'::
+ When run in a directory that does not have ".git" repository
+ directory, git tries to find such a directory in the parent
+ directories to find the top of the working tree, but by default it
+ does not cross filesystem boundaries. This environment variable
+ can be set to true to tell git not to stop at filesystem
+ boundaries. Like 'GIT_CEILING_DIRECTORIES', this will not affect
+ an explicit repository directory set via 'GIT_DIR' or on the
+ command line.
+
git Commits
~~~~~~~~~~~
'GIT_AUTHOR_NAME'::
should generate it separately and send it as a comment _in
addition to_ the usual binary diff that you might send.
+Because text conversion can be slow, especially when doing a
+large number of them with `git log -p`, git provides a mechanism
+to cache the output and use it in future diffs. To enable
+caching, set the "cachetextconv" variable in your diff driver's
+config. For example:
+
+------------------------
+[diff "jpg"]
+ textconv = exif
+ cachetextconv = true
+------------------------
+
+This will cache the result of running "exif" on each blob
+indefinitely. If you change the textconv config variable for a
+diff driver, git will automatically invalidate the cache entries
+and re-run the textconv filter. If you want to invalidate the
+cache manually (e.g., because your version of "exif" was updated
+and now produces better output), you can remove the cache
+manually with `git update-ref -d refs/notes/textconv/jpg` (where
+"jpg" is the name of the diff driver, as in the example above).
Performing a three-way merge
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
commands.
When diffcore-pickaxe is in use, it checks if there are
-filepairs whose "original" side has the specified string and
-whose "result" side does not. Such a filepair represents "the
+filepairs whose "result" side has the specified string and
+whose "origin" side does not. Such a filepair represents "the
string appeared in this changeset". It also checks for the
opposite case that loses the specified string.
revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,
as you seem to have interpreted), then re-merging the result without
doing anything else fancy would be the right thing to do.
+ (See the ADDENDUM below for how to rebuild a branch from scratch
+ without changing its original branching-off point.)
However, there are things to keep in mind when reverting a merge (and
reverting such a revert).
ready yet, and I really need to undo _all_ of the merge"). So then you
really should revert the merge, but when you want to re-do the merge, you
now need to do it by reverting the revert.
+
+ADDENDUM
+
+Sometimes you have to rewrite one of a topic branch's commits *and* you can't
+change the topic's branching-off point. Consider the following situation:
+
+ P---o---o---M---x---x---W---x
+ \ /
+ A---B---C
+
+where commit W reverted commit M because it turned out that commit B was wrong
+and needs to be rewritten, but you need the rewritten topic to still branch
+from commit P (perhaps P is a branching-off point for yet another branch, and
+you want be able to merge the topic into both branches).
+
+The natural thing to do in this case is to checkout the A-B-C branch and use
+"rebase -i P" to change commit B. However this does not rewrite commit A,
+because "rebase -i" by default fast-forwards over any initial commits selected
+with the "pick" command. So you end up with this:
+
+ P---o---o---M---x---x---W---x
+ \ /
+ A---B---C <-- old branch
+ \
+ B'---C' <-- naively rewritten branch
+
+To merge A-B'-C' into the mainline branch you would still have to first revert
+commit W in order to pick up the changes in A, but then it's likely that the
+changes in B' will conflict with the original B changes re-introduced by the
+reversion of W.
+
+However, you can avoid these problems if you recreate the entire branch,
+including commit A:
+
+ A'---B'---C' <-- completely rewritten branch
+ /
+ P---o---o---M---x---x---W---x
+ \ /
+ A---B---C
+
+You can merge A'-B'-C' into the mainline branch without worrying about first
+reverting W. Mainline's history would look like this:
+
+ A'---B'---C'------------------
+ / \
+ P---o---o---M---x---x---W---x---M2
+ \ /
+ A---B---C
+
+But if you don't actually need to change commit A, then you need some way to
+recreate it as a new commit with the same changes in it. The rebase commmand's
+--no-ff option provides a way to do this:
+
+ $ git rebase [-i] --no-ff P
+
+The --no-ff option creates a new branch A'-B'-C' with all-new commits (all the
+SHA IDs will be different) even if in the interactive case you only actually
+modify commit B. You can then merge this new branch directly into the mainline
+branch and be sure you'll get all of the branch's changes.
+
+You can also use --no-ff in cases where you just add extra commits to the topic
+to fix it up. Let's revisit the situation discussed at the start of this howto:
+
+ P---o---o---M---x---x---W---x
+ \ /
+ A---B---C----------------D---E <-- fixed-up topic branch
+
+At this point, you can use --no-ff to recreate the topic branch:
+
+ $ git checkout E
+ $ git rebase --no-ff P
+
+yielding
+
+ A'---B'---C'------------D'---E' <-- recreated topic branch
+ /
+ P---o---o---M---x---x---W---x
+ \ /
+ A---B---C----------------D---E
+
+You can merge the recreated branch into the mainline without reverting commit W,
+and mainline's history will look like this:
+
+ A'---B'---C'------------D'---E'
+ / \
+ P---o---o---M---x---x---W---x---M2
+ \ /
+ A---B---C
is used instead ('git merge-recursive' when merging a single
head, 'git merge-octopus' otherwise).
+-X <option>::
+--strategy-option=<option>::
+ Pass merge strategy specific option through to the merge
+ strategy.
+
--summary::
--no-summary::
Synonyms to --stat and --no-stat; these are deprecated and will be
--verbose::
Be verbose.
endif::git-pull[]
-
--X <option>::
---strategy-option=<option>::
- Pass merge strategy specific option through to the merge
- strategy.
- '%s': subject
- '%f': sanitized subject line, suitable for a filename
- '%b': body
+- '%B': raw body (unwrapped subject and body)
- '%N': commit notes
- '%gD': reflog selector, e.g., `refs/stash@\{1\}`
- '%gd': shortened reflog selector, e.g., `stash@\{1\}`
`unsorted_string_list_has_string`::
It's like `string_list_has_string()` but for unsorted lists.
+
+`unsorted_string_list_lookup`::
+
+ It's like `string_list_lookup()` but for unsorted lists.
+
-This function needs to look through all items, as opposed to its
+The above two functions need to look through all items, as opposed to their
counterpart for sorted lists, which performs a binary search.
Data structures
The Git transport starts off by sending the command and repository
on the wire using the pkt-line format, followed by a NUL byte and a
-hostname paramater, terminated by a NUL byte.
+hostname parameter, terminated by a NUL byte.
0032git-upload-pack /project.git\0host=myserver.com\0
C: 0009done\n
- S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d\n
+ S: 0031ACK 74730d410fcb6603ace96f1dc55ea6196122532d\n
S: [PACKFILE]
----
C: 0000
C: [PACKDATA]
- S: 000aunpack ok\n
- S: 0014ok refs/heads/debug\n
- S: 0026ng refs/heads/master non-fast-forward\n
+ S: 000eunpack ok\n
+ S: 0018ok refs/heads/debug\n
+ S: 002ang refs/heads/master non-fast-forward\n
----
GIT URLS[[URLS]]
----------------
-One of the following notations can be used
-to name the remote repository:
+In general, URLs contain information about the transport protocol, the
+address of the remote server, and the path to the repository.
+Depending on the transport protocol, some of this information may be
+absent.
+
+Git natively supports ssh, git, http, https, ftp, ftps, and rsync
+protocols. The following syntaxes may be used with them:
-- rsync://host.xz/path/to/repo.git/
-- http://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- https://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
-- git://host.xz{startsb}:port{endsb}/~user/path/to/repo.git/
- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
-- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
-- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
-- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
+- git://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- http{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- ftp{startsb}s{endsb}://host.xz{startsb}:port{endsb}/path/to/repo.git/
+- rsync://host.xz/path/to/repo.git/
-SSH is the default transport protocol over the network. You can
-optionally specify which user to log-in as, and an alternate,
-scp-like syntax is also supported. Both syntaxes support
-username expansion, as does the native git protocol, but
-only the former supports port specification. The following
-three are identical to the last three above, respectively:
+An alternative scp-like syntax may also be used with the ssh protocol:
-- {startsb}user@{endsb}host.xz:/path/to/repo.git/
-- {startsb}user@{endsb}host.xz:~user/path/to/repo.git/
-- {startsb}user@{endsb}host.xz:path/to/repo.git
+- {startsb}user@{endsb}host.xz:path/to/repo.git/
-To sync with a local directory, you can use:
+The ssh and git protocols additionally support ~username expansion:
+
+- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
+- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
+
+For local respositories, also supported by git natively, the following
+syntaxes may be used:
- /path/to/repo.git/
- file:///path/to/repo.git/
ifndef::git-clone[]
-They are mostly equivalent, except when cloning. See
-linkgit:git-clone[1] for details.
+These two syntaxes are mostly equivalent, except when cloning, when
+the former implies --local option. See linkgit:git-clone[1] for
+details.
endif::git-clone[]
ifdef::git-clone[]
-They are equivalent, except the former implies --local option.
+These two syntaxes are mostly equivalent, except the former implies
+--local option.
endif::git-clone[]
+When git doesn't know how to handle a certain transport protocol, it
+attempts to use the 'remote-<transport>' remote helper, if one
+exists. To explicitly request a remote helper, the following syntax
+may be used:
+
+- <transport>::<address>
+
+where <address> may be a path, a server and path, or an arbitrary
+URL-like string recognized by the specific remote helper being
+invoked. See linkgit:git-remote-helpers[1] for details.
If there are a large number of similarly-named remote repositories and
you want to use a different format for them (such that the URLs you
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.0.4
+DEF_VER=v1.7.1.GIT
LF='
'
then
VN=$(cat version) || VN="$DEF_VER"
elif test -d .git -o -f .git &&
- VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
+ VN=$(git describe --match "v[0-9]*" --abbrev=4 HEAD 2>/dev/null) &&
case "$VN" in
*$LF*) (exit 1) ;;
v[0-9]*)
# Define JSMIN to point to JavaScript minifier that functions as
# a filter to have gitweb.js minified.
#
+# Define CSSMIN to point to a CSS minifier in order to generate a minified
+# version of gitweb.css
+#
# Define DEFAULT_PAGER to a sensible pager command (defaults to "less") if
# you want to use something different. The value will be interpreted by the
# shell at runtime when it is used.
# DESTDIR=
pathsep = :
-# JavaScript minifier invocation that can function as filter
-JSMIN =
-
export prefix bindir sharedir sysconfdir
CC = gcc
SCRIPT_PERL += git-send-email.perl
SCRIPT_PERL += git-svn.perl
+SCRIPT_PYTHON += git-remote-testgit.py
+
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
$(patsubst %.perl,%,$(SCRIPT_PERL)) \
$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
LIB_H += mailmap.h
LIB_H += merge-recursive.h
LIB_H += notes.h
+LIB_H += notes-cache.h
LIB_H += object.h
LIB_H += pack.h
LIB_H += pack-refs.h
LIB_OBJS += merge-recursive.o
LIB_OBJS += name-hash.o
LIB_OBJS += notes.o
+LIB_OBJS += notes-cache.o
LIB_OBJS += object.o
LIB_OBJS += pack-check.o
LIB_OBJS += pack-refs.o
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
endif
ifeq ($(uname_O),Cygwin)
- ifneq ($(wordlist 1, 2, $(subst ., ,$(uname_R))),1 7)
+ ifeq ($(shell expr "$(uname_R)" : '1\.[1-6]\.'),4)
NO_D_TYPE_IN_DIRENT = YesPlease
NO_D_INO_IN_DIRENT = YesPlease
NO_STRCASESTR = YesPlease
NO_UINTMAX_T = YesPlease
NO_STRTOUMAX = YesPlease
endif
+ PYTHON_PATH = /usr/local/bin/python
endif
ifeq ($(uname_S),OpenBSD)
NO_STRCASESTR = YesPlease
SNPRINTF_RETURNS_BOGUS = YesPlease
SHELL_PATH = /usr/gnu/bin/bash
NEEDS_LIBGEN = YesPlease
- NEEDS_LIBICONV = YesPlease
endif
ifeq ($(uname_S),IRIX64)
NO_SETENV=YesPlease
SNPRINTF_RETURNS_BOGUS = YesPlease
SHELL_PATH=/usr/gnu/bin/bash
NEEDS_LIBGEN = YesPlease
- NEEDS_LIBICONV = YesPlease
endif
ifeq ($(uname_S),HP-UX)
NO_IPV6=YesPlease
ifndef NO_PYTHON
$(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
endif
- $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
+ $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1) SHELL_PATH='$(SHELL_PATH_SQ)' PERL_PATH='$(PERL_PATH_SQ)'
please_set_SHELL_PATH_to_a_more_modern_shell:
@$$(:)
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
ifdef JSMIN
-OTHER_PROGRAMS += gitweb/gitweb.cgi gitweb/gitweb.min.js
-gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.min.js
+GITWEB_PROGRAMS += gitweb/gitweb.min.js
+GITWEB_JS = gitweb/gitweb.min.js
else
-OTHER_PROGRAMS += gitweb/gitweb.cgi
-gitweb/gitweb.cgi: gitweb/gitweb.perl
+GITWEB_JS = gitweb/gitweb.js
endif
+ifdef CSSMIN
+GITWEB_PROGRAMS += gitweb/gitweb.min.css
+GITWEB_CSS = gitweb/gitweb.min.css
+else
+GITWEB_CSS = gitweb/gitweb.css
+endif
+OTHER_PROGRAMS += gitweb/gitweb.cgi $(GITWEB_PROGRAMS)
+gitweb/gitweb.cgi: gitweb/gitweb.perl $(GITWEB_PROGRAMS)
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
ifdef JSMIN
gitweb/gitweb.min.js: gitweb/gitweb.js
$(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
endif # JSMIN
+ifdef CSSMIN
+gitweb/gitweb.min.css: gitweb/gitweb.css
+ $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) $(patsubst gitweb/%,%,$@)
+endif # CSSMIN
git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
-e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
-e '/@@GITWEB_CGI@@/d' \
- -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
+ -e '/@@GITWEB_CSS@@/r $(GITWEB_CSS)' \
-e '/@@GITWEB_CSS@@/d' \
- -e '/@@GITWEB_JS@@/r gitweb/gitweb.js' \
+ -e '/@@GITWEB_JS@@/r $(GITWEB_JS)' \
-e '/@@GITWEB_JS@@/d' \
-e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
+ -e 's|@@GITWEB_CSS_NAME@@|$(GITWEB_CSS)|' \
+ -e 's|@@GITWEB_JS_NAME@@|$(GITWEB_JS)|' \
$@.sh > $@+ && \
chmod +x $@+ && \
mv $@+ $@
INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
--no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
instlibdir` && \
- sed -e '1{' \
- -e ' s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
- -e '}' \
- -e 's|^import sys.*|&; \\\
- import os; \\\
- sys.path[0] = os.environ.has_key("GITPYTHONLIB") and \\\
- os.environ["GITPYTHONLIB"] or \\\
- "@@INSTLIBDIR@@"|' \
+ sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
+ -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
-e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
$@.py >$@+ && \
chmod +x $@+ && \
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
$(MAKE) -C Documentation/ clean
ifndef NO_PERL
- $(RM) gitweb/gitweb.cgi
+ $(MAKE) -C gitweb clean
$(MAKE) -C perl clean
endif
ifndef NO_PYTHON
-Documentation/RelNotes-1.7.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.7.2.txt
\ No newline at end of file
return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
}
+static int macroexpand_one(int attr_nr, int rem);
+
static int fill_one(const char *what, struct match_attr *a, int rem)
{
struct git_attr_check *check = check_all_attr;
int i;
- for (i = 0; 0 < rem && i < a->num_attr; i++) {
+ for (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) {
struct git_attr *attr = a->state[i].attr;
const char **n = &(check[attr->attr_nr].value);
const char *v = a->state[i].setto;
if (*n == ATTR__UNKNOWN) {
- debug_set(what, a->u.pattern, attr, v);
+ debug_set(what,
+ a->is_macro ? a->u.attr->name : a->u.pattern,
+ attr, v);
*n = v;
rem--;
+ rem = macroexpand_one(attr->attr_nr, rem);
}
}
return rem;
return rem;
}
-static int macroexpand(struct attr_stack *stk, int rem)
+static int macroexpand_one(int attr_nr, int rem)
{
+ struct attr_stack *stk;
+ struct match_attr *a = NULL;
int i;
- struct git_attr_check *check = check_all_attr;
- for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
- struct match_attr *a = stk->attrs[i];
- if (!a->is_macro)
- continue;
- if (check[a->u.attr->attr_nr].value != ATTR__TRUE)
- continue;
+ if (check_all_attr[attr_nr].value != ATTR__TRUE)
+ return rem;
+
+ for (stk = attr_stack; !a && stk; stk = stk->prev)
+ for (i = stk->num_matches - 1; !a && 0 <= i; i--) {
+ struct match_attr *ma = stk->attrs[i];
+ if (!ma->is_macro)
+ continue;
+ if (ma->u.attr->attr_nr == attr_nr)
+ a = ma;
+ }
+
+ if (a)
rem = fill_one("expand", a, rem);
- }
+
return rem;
}
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
rem = fill(path, pathlen, stk, rem);
- for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
- rem = macroexpand(stk, rem);
-
for (i = 0; i < num; i++) {
const char *value = check_all_attr[check[i].attr->attr_nr].value;
if (value == ATTR__UNKNOWN)
log_all_ref_updates = 1;
if (forcing)
- snprintf(msg, sizeof msg, "branch: Reset from %s",
+ snprintf(msg, sizeof msg, "branch: Reset to %s",
start_name);
else if (!dont_change_ref)
snprintf(msg, sizeof msg, "branch: Created from %s",
extern void prune_packed_objects(int);
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
struct strbuf *out);
-extern int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author);
extern int commit_notes(struct notes_tree *t, const char *msg);
struct notes_rewrite_cfg {
{
int i;
char *fixed_buf, *buf, *orig, *target;
+ struct strbuf fixed;
+ size_t fixed_len;
int preimage_limit;
if (preimage->nr + try_lno <= img->nr) {
if (match_end && (preimage->nr + try_lno != img->nr))
return 0;
} else if (ws_error_action == correct_ws_error &&
- (ws_rule & WS_BLANK_AT_EOF) && match_end) {
+ (ws_rule & WS_BLANK_AT_EOF)) {
/*
- * This hunk that matches at the end extends beyond
- * the end of img, and we are removing blank lines
- * at the end of the file. This many lines from the
- * beginning of the preimage must match with img, and
- * the remainder of the preimage must be blank.
+ * This hunk extends beyond the end of img, and we are
+ * removing blank lines at the end of the file. This
+ * many lines from the beginning of the preimage must
+ * match with img, and the remainder of the preimage
+ * must be blank.
*/
preimage_limit = img->nr - try_lno;
} else {
* use the whitespace from the preimage.
*/
extra_chars = preimage_end - preimage_eof;
- fixed_buf = xmalloc(imgoff + extra_chars);
- memcpy(fixed_buf, img->buf + try, imgoff);
- memcpy(fixed_buf + imgoff, preimage_eof, extra_chars);
- imgoff += extra_chars;
+ strbuf_init(&fixed, imgoff + extra_chars);
+ strbuf_add(&fixed, img->buf + try, imgoff);
+ strbuf_add(&fixed, preimage_eof, extra_chars);
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, imgoff, postlen);
+ fixed_buf, fixed_len, postlen);
return 1;
}
* but in this loop we will only handle the part of the
* preimage that falls within the file.
*/
- fixed_buf = xmalloc(preimage->len + 1);
- buf = fixed_buf;
+ strbuf_init(&fixed, preimage->len + 1);
orig = preimage->buf;
target = img->buf + try;
for (i = 0; i < preimage_limit; i++) {
- size_t fixlen; /* length after fixing the preimage */
size_t oldlen = preimage->line[i].len;
size_t tgtlen = img->line[try_lno + i].len;
- size_t tgtfixlen; /* length after fixing the target line */
- char tgtfixbuf[1024], *tgtfix;
+ size_t fixstart = fixed.len;
+ struct strbuf tgtfix;
int match;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
/* Try fixing the line in the target */
- if (sizeof(tgtfixbuf) > tgtlen)
- tgtfix = tgtfixbuf;
- else
- tgtfix = xmalloc(tgtlen);
- tgtfixlen = ws_fix_copy(tgtfix, target, tgtlen, ws_rule, NULL);
+ strbuf_init(&tgtfix, tgtlen);
+ ws_fix_copy(&tgtfix, target, tgtlen, ws_rule, NULL);
/*
* If they match, either the preimage was based on
* so we might as well take the fix together with their
* real change.
*/
- match = (tgtfixlen == fixlen && !memcmp(tgtfix, buf, fixlen));
+ match = (tgtfix.len == fixed.len - fixstart &&
+ !memcmp(tgtfix.buf, fixed.buf + fixstart,
+ fixed.len - fixstart));
- if (tgtfix != tgtfixbuf)
- free(tgtfix);
+ strbuf_release(&tgtfix);
if (!match)
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
target += tgtlen;
}
* false).
*/
for ( ; i < preimage->nr; i++) {
- size_t fixlen; /* length after fixing the preimage */
+ size_t fixstart = fixed.len; /* start of the fixed preimage */
size_t oldlen = preimage->line[i].len;
int j;
/* Try fixing the line in the preimage */
- fixlen = ws_fix_copy(buf, orig, oldlen, ws_rule, NULL);
+ ws_fix_copy(&fixed, orig, oldlen, ws_rule, NULL);
- for (j = 0; j < fixlen; j++)
- if (!isspace(buf[j]))
+ for (j = fixstart; j < fixed.len; j++)
+ if (!isspace(fixed.buf[j]))
goto unmatch_exit;
orig += oldlen;
- buf += fixlen;
}
/*
* has whitespace breakages unfixed, and fixing them makes the
* hunk match. Update the context lines in the postimage.
*/
+ fixed_buf = strbuf_detach(&fixed, &fixed_len);
update_pre_post_images(preimage, postimage,
- fixed_buf, buf - fixed_buf, 0);
+ fixed_buf, fixed_len, 0);
return 1;
unmatch_exit:
- free(fixed_buf);
+ strbuf_release(&fixed);
return 0;
}
int match_beginning, match_end;
const char *patch = frag->patch;
int size = frag->size;
- char *old, *new, *oldlines, *newlines;
+ char *old, *oldlines;
+ struct strbuf newlines;
int new_blank_lines_at_end = 0;
unsigned long leading, trailing;
int pos, applied_pos;
memset(&preimage, 0, sizeof(preimage));
memset(&postimage, 0, sizeof(postimage));
oldlines = xmalloc(size);
- newlines = xmalloc(size);
+ strbuf_init(&newlines, size);
old = oldlines;
- new = newlines;
while (size > 0) {
char first;
int len = linelen(patch, size);
- int plen, added;
+ int plen;
int added_blank_line = 0;
int is_blank_context = 0;
+ size_t start;
if (!len)
break;
/* ... followed by '\No newline'; nothing */
break;
*old++ = '\n';
- *new++ = '\n';
+ strbuf_addch(&newlines, '\n');
add_line_info(&preimage, "\n", 1, LINE_COMMON);
add_line_info(&postimage, "\n", 1, LINE_COMMON);
is_blank_context = 1;
if (first == '+' && no_add)
break;
+ start = newlines.len;
if (first != '+' ||
!whitespace_error ||
ws_error_action != correct_ws_error) {
- memcpy(new, patch + 1, plen);
- added = plen;
+ strbuf_add(&newlines, patch + 1, plen);
}
else {
- added = ws_fix_copy(new, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
+ ws_fix_copy(&newlines, patch + 1, plen, ws_rule, &applied_after_fixing_ws);
}
- add_line_info(&postimage, new, added,
+ add_line_info(&postimage, newlines.buf + start, newlines.len - start,
(first == '+' ? 0 : LINE_COMMON));
- new += added;
if (first == '+' &&
(ws_rule & WS_BLANK_AT_EOF) &&
ws_blank_line(patch + 1, plen, ws_rule))
}
if (inaccurate_eof &&
old > oldlines && old[-1] == '\n' &&
- new > newlines && new[-1] == '\n') {
+ newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
old--;
- new--;
+ strbuf_setlen(&newlines, newlines.len - 1);
}
leading = frag->leading;
pos = frag->newpos ? (frag->newpos - 1) : 0;
preimage.buf = oldlines;
preimage.len = old - oldlines;
- postimage.buf = newlines;
- postimage.len = new - newlines;
+ postimage.buf = newlines.buf;
+ postimage.len = newlines.len;
preimage.line = preimage.line_allocated;
postimage.line = postimage.line_allocated;
}
free(oldlines);
- free(newlines);
+ strbuf_release(&newlines);
free(preimage.line_allocated);
free(postimage.line_allocated);
if (stat_ret < 0) {
struct checkout costate;
/* checkout */
+ memset(&costate, 0, sizeof(costate));
costate.base_dir = "";
- costate.base_dir_len = 0;
- costate.force = 0;
- costate.quiet = 0;
- costate.not_new = 0;
costate.refresh_cache = 1;
if (checkout_entry(*ce, &costate, NULL) ||
lstat(old_name, st))
die("unable to remove %s from index", patch->old_name);
}
if (!cached) {
- if (S_ISGITLINK(patch->old_mode)) {
- if (rmdir(patch->old_name))
- warning("unable to remove submodule %s",
- patch->old_name);
- } else if (!unlink_or_warn(patch->old_name) && rmdir_empty) {
+ if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
remove_path(patch->old_name);
}
}
int writeout_error;
const char *new_branch;
+ const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
};
read_mmblob(&ours, active_cache[pos+1]->sha1);
read_mmblob(&theirs, active_cache[pos+2]->sha1);
- status = ll_merge(&result_buf, path, &ancestor,
+ status = ll_merge(&result_buf, path, &ancestor, "base",
&ours, "ours", &theirs, "theirs", 0);
free(ancestor.ptr);
free(ours.ptr);
ret = reset_tree(new->commit->tree, opts, 1);
if (ret)
return ret;
+ o.ancestor = old->name;
o.branch1 = new->name;
o.branch2 = "local";
merge_trees(&o, new->commit->tree, work,
struct strbuf msg = STRBUF_INIT;
const char *old_desc;
if (opts->new_branch) {
- create_branch(old->name, opts->new_branch, new->name, 0,
- opts->new_branch_log, opts->track);
+ if (!opts->new_orphan_branch)
+ create_branch(old->name, opts->new_branch, new->name, 0,
+ opts->new_branch_log, opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
}
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
+ OPT_STRING(0, "orphan", &opts.new_orphan_branch, "new branch", "new unparented branch"),
OPT_SET_INT('2', "ours", &opts.writeout_stage, "stage",
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
opts.new_branch = argv0 + 1;
}
+ if (opts.new_orphan_branch) {
+ if (opts.new_branch)
+ die("--orphan and -b are mutually exclusive");
+ if (opts.track > 0 || opts.new_branch_log)
+ die("--orphan cannot be used with -t or -l");
+ opts.new_branch = opts.new_orphan_branch;
+ }
+
if (conflict_style) {
opts.merge = 1; /* implied */
git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
transport = transport_get(remote, src_repo);
ret = transport_get_remote_refs(transport);
transport_disconnect(transport);
+ if (0 <= option_verbosity)
+ printf("done.\n");
return ret;
}
die("could not create leading directories of '%s'", git_dir);
set_git_dir(make_absolute_path(git_dir));
- init_db(option_template, (option_verbosity < 0) ? INIT_DB_QUIET : 0);
+ if (0 <= option_verbosity)
+ printf("Cloning into %s...\n", get_git_dir());
+ init_db(option_template, INIT_DB_QUIET);
/*
* At this point, the config exists, so we do not need the
*/
unsetenv(CONFIG_ENVIRONMENT);
- if (option_reference)
- setup_reference(git_dir);
-
git_config(git_default_config, NULL);
if (option_bare) {
git_config_set(key.buf, "true");
strbuf_reset(&key);
}
-
- strbuf_addf(&key, "remote.%s.url", option_origin);
- git_config_set(key.buf, repo);
- strbuf_reset(&key);
}
+ strbuf_addf(&key, "remote.%s.url", option_origin);
+ git_config_set(key.buf, repo);
+ strbuf_reset(&key);
+
+ if (option_reference)
+ setup_reference(git_dir);
+
fetch_pattern = value.buf;
refspec = parse_fetch_refspec(1, &fetch_pattern);
refs = clone_local(path, git_dir);
mapped_refs = wanted_peer_refs(refs, refspec);
} else {
- struct remote *remote = remote_get(argv[0]);
+ struct remote *remote = remote_get(option_origin);
transport = transport_get(remote, remote->url[0]);
if (!transport->get_refs_list || !transport->fetch)
#include "builtin.h"
#include "utf8.h"
-/*
- * FIXME! Share the code with "write-tree.c"
- */
-static void check_valid(unsigned char *sha1, enum object_type expect)
-{
- enum object_type type = sha1_object_info(sha1, NULL);
- if (type < 0)
- die("%s is not a valid object", sha1_to_hex(sha1));
- if (type != expect)
- die("%s is not a valid '%s' object", sha1_to_hex(sha1),
- typename(expect));
-}
-
static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
static void new_parent(struct commit *parent, struct commit_list **parents_p)
commit_list_insert(parent, parents_p);
}
-static const char commit_utf8_warn[] =
-"Warning: commit message does not conform to UTF-8.\n"
-"You may want to amend it after fixing the message, or set the config\n"
-"variable i18n.commitencoding to the encoding your project uses.\n";
-
-int commit_tree(const char *msg, unsigned char *tree,
- struct commit_list *parents, unsigned char *ret,
- const char *author)
-{
- int result;
- int encoding_is_utf8;
- struct strbuf buffer;
-
- check_valid(tree, OBJ_TREE);
-
- /* Not having i18n.commitencoding is the same as having utf-8 */
- encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
-
- strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
- strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
-
- /*
- * NOTE! This ordering means that the same exact tree merged with a
- * different order of parents will be a _different_ changeset even
- * if everything else stays the same.
- */
- while (parents) {
- struct commit_list *next = parents->next;
- strbuf_addf(&buffer, "parent %s\n",
- sha1_to_hex(parents->item->object.sha1));
- free(parents);
- parents = next;
- }
-
- /* Person/date information */
- if (!author)
- author = git_author_info(IDENT_ERROR_ON_NO_NAME);
- strbuf_addf(&buffer, "author %s\n", author);
- strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
- if (!encoding_is_utf8)
- strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
- strbuf_addch(&buffer, '\n');
-
- /* And add the comment */
- strbuf_addstr(&buffer, msg);
-
- /* And check the encoding */
- if (encoding_is_utf8 && !is_utf8(buffer.buf))
- fprintf(stderr, commit_utf8_warn);
-
- result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
- strbuf_release(&buffer);
- return result;
-}
-
int cmd_commit_tree(int argc, const char **argv, const char *prefix)
{
int i;
if (get_sha1(b, sha1))
die("Not a valid object name %s", b);
- check_valid(sha1, OBJ_COMMIT);
+ assert_sha1_type(sha1, OBJ_COMMIT);
new_parent(lookup_commit(sha1), &parents);
}
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
-static int no_post_rewrite;
+static int no_post_rewrite, allow_empty_message;
static char *untracked_files_arg, *force_date;
/*
* The default commit message cleanup mode will remove the lines
static char *cleanup_arg;
static int use_editor = 1, initial_commit, in_merge, include_status = 1;
+static int show_ignored_in_status;
static const char *only_include_assumed;
static struct strbuf message;
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
- OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
/* end commit contents options */
+ { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
+ "ok to record an empty change",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+ { OPTION_BOOLEAN, 0, "allow-empty-message", &allow_empty_message, NULL,
+ "ok to record a change with an empty message",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
+
OPT_END()
};
int cmd_status(int argc, const char **argv, const char *prefix)
{
struct wt_status s;
+ int fd;
unsigned char sha1[20];
static struct option builtin_status_options[] = {
OPT__VERBOSE(&verbose),
"mode",
"show untracked files, optional modes: all, normal, no. (Default: all)",
PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+ OPT_BOOLEAN(0, "ignored", &show_ignored_in_status,
+ "show ignored files"),
OPT_END(),
};
builtin_status_options,
builtin_status_usage, 0);
handle_untracked_files_arg(&s);
-
+ if (show_ignored_in_status)
+ s.show_ignored_files = 1;
if (*argv)
s.pathspec = get_pathspec(prefix, argv);
read_cache_preload(s.pathspec);
refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, s.pathspec, NULL, NULL);
+
+ fd = hold_locked_index(&index_lock, 0);
+ if (0 <= fd) {
+ if (!write_cache(fd, active_cache, active_nr))
+ commit_locked_index(&index_lock);
+ rollback_lock_file(&index_lock);
+ }
+
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
s.in_merge = in_merge;
wt_status_collect(&s);
if (cleanup_mode != CLEANUP_NONE)
stripspace(&sb, cleanup_mode == CLEANUP_ALL);
- if (message_is_empty(&sb)) {
+ if (message_is_empty(&sb) && !allow_empty_message) {
rollback_index_files();
fprintf(stderr, "Aborting commit due to empty commit message.\n");
exit(1);
git_config_from_file(show_config, system_wide, NULL);
if (do_all && global)
git_config_from_file(show_config, global, NULL);
- git_config_from_file(show_config, local, NULL);
+ if (do_all)
+ git_config_from_file(show_config, local, NULL);
+ git_config_from_parameters(show_config, NULL);
+ if (!do_all && !seen)
+ git_config_from_file(show_config, local, NULL);
if (!do_all && !seen && global)
git_config_from_file(show_config, global, NULL);
if (!do_all && !seen && system_wide)
struct commit_name {
struct tag *tag;
- int prio; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned name_checked:1;
unsigned char sha1[20];
char path[FLEX_ARRAY]; /* more */
};
"head", "lightweight", "annotated",
};
+static int replace_name(struct commit_name *e,
+ int prio,
+ const unsigned char *sha1,
+ struct tag **tag)
+{
+ if (!e || e->prio < prio)
+ return 1;
+
+ if (e->prio == 2 && prio == 2) {
+ /* Multiple annotated tags point to the same commit.
+ * Select one to keep based upon their tagger date.
+ */
+ struct tag *t;
+
+ if (!e->tag) {
+ t = lookup_tag(e->sha1);
+ if (!t || parse_tag(t))
+ return 1;
+ e->tag = t;
+ }
+
+ t = lookup_tag(sha1);
+ if (!t || parse_tag(t))
+ return 0;
+ *tag = t;
+
+ if (e->tag->date < t->date)
+ return 1;
+ }
+
+ return 0;
+}
+
static void add_to_known_names(const char *path,
struct commit *commit,
int prio,
const unsigned char *sha1)
{
struct commit_name *e = commit->util;
- if (!e || e->prio < prio) {
+ struct tag *tag = NULL;
+ if (replace_name(e, prio, sha1, &tag)) {
size_t len = strlen(path)+1;
free(e);
e = xmalloc(sizeof(struct commit_name) + len);
- e->tag = NULL;
+ e->tag = tag;
e->prio = prio;
+ e->name_checked = 0;
hashcpy(e->sha1, sha1);
memcpy(e->path, path, len);
commit->util = e;
{
if (n->prio == 2 && !n->tag) {
n->tag = lookup_tag(n->sha1);
- if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
+ if (!n->tag || parse_tag(n->tag))
die("annotated tag %s not available", n->path);
+ }
+ if (n->tag && !n->name_checked) {
+ if (!n->tag->tag)
+ die("annotated tag %s has no embedded name", n->path);
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
+ n->name_checked = 1;
}
if (n->tag)
#include "transport.h"
static const char * const builtin_fetch_usage[] = {
- "git fetch [options] [<repository> <refspec>...]",
- "git fetch [options] <group>",
- "git fetch --multiple [options] [<repository> | <group>]...",
- "git fetch --all [options]",
+ "git fetch [<options>] [<repository> [<refspec>...]]",
+ "git fetch [<options>] <group>",
+ "git fetch --multiple [<options>] [<repository> | <group>]...",
+ "git fetch --all [<options>]",
NULL
};
#include "diff.h"
#include "revision.h"
#include "tag.h"
+#include "string-list.h"
static const char * const fmt_merge_msg_usage[] = {
"git fmt-merge-msg [--log|--no-log] [--file <file>]",
return 0;
}
-struct list {
- char **list;
- void **payload;
- unsigned nr, alloc;
+struct src_data {
+ struct string_list branch, tag, r_branch, generic;
+ int head_status;
};
-static void append_to_list(struct list *list, char *value, void *payload)
-{
- if (list->nr == list->alloc) {
- list->alloc += 32;
- list->list = xrealloc(list->list, sizeof(char *) * list->alloc);
- list->payload = xrealloc(list->payload,
- sizeof(char *) * list->alloc);
- }
- list->payload[list->nr] = payload;
- list->list[list->nr++] = value;
-}
-
-static int find_in_list(struct list *list, char *value)
-{
- int i;
-
- for (i = 0; i < list->nr; i++)
- if (!strcmp(list->list[i], value))
- return i;
-
- return -1;
-}
-
-static void free_list(struct list *list)
+void init_src_data(struct src_data *data)
{
- int i;
-
- if (list->alloc == 0)
- return;
-
- for (i = 0; i < list->nr; i++) {
- free(list->list[i]);
- free(list->payload[i]);
- }
- free(list->list);
- free(list->payload);
- list->nr = list->alloc = 0;
+ data->branch.strdup_strings = 1;
+ data->tag.strdup_strings = 1;
+ data->r_branch.strdup_strings = 1;
+ data->generic.strdup_strings = 1;
}
-struct src_data {
- struct list branch, tag, r_branch, generic;
- int head_status;
-};
-
-static struct list srcs = { NULL, NULL, 0, 0};
-static struct list origins = { NULL, NULL, 0, 0};
+static struct string_list srcs = { NULL, 0, 0, 1 };
+static struct string_list origins = { NULL, 0, 0, 1 };
static int handle_line(char *line)
{
unsigned char *sha1;
char *src, *origin;
struct src_data *src_data;
+ struct string_list_item *item;
int pulling_head = 0;
if (len < 43 || line[40] != '\t')
pulling_head = 1;
}
- i = find_in_list(&srcs, src);
- if (i < 0) {
- i = srcs.nr;
- append_to_list(&srcs, xstrdup(src),
- xcalloc(1, sizeof(struct src_data)));
+ item = unsorted_string_list_lookup(&srcs, src);
+ if (!item) {
+ item = string_list_append(src, &srcs);
+ item->util = xcalloc(1, sizeof(struct src_data));
+ init_src_data(item->util);
}
- src_data = srcs.payload[i];
+ src_data = item->util;
if (pulling_head) {
- origin = xstrdup(src);
+ origin = src;
src_data->head_status |= 1;
} else if (!prefixcmp(line, "branch ")) {
- origin = xstrdup(line + 7);
- append_to_list(&src_data->branch, origin, NULL);
+ origin = line + 7;
+ string_list_append(origin, &src_data->branch);
src_data->head_status |= 2;
} else if (!prefixcmp(line, "tag ")) {
origin = line;
- append_to_list(&src_data->tag, xstrdup(origin + 4), NULL);
+ string_list_append(origin + 4, &src_data->tag);
src_data->head_status |= 2;
} else if (!prefixcmp(line, "remote branch ")) {
- origin = xstrdup(line + 14);
- append_to_list(&src_data->r_branch, origin, NULL);
+ origin = line + 14;
+ string_list_append(origin, &src_data->r_branch);
src_data->head_status |= 2;
} else {
- origin = xstrdup(src);
- append_to_list(&src_data->generic, xstrdup(line), NULL);
+ origin = src;
+ string_list_append(line, &src_data->generic);
src_data->head_status |= 2;
}
if (!strcmp(".", src) || !strcmp(src, origin)) {
int len = strlen(origin);
- if (origin[0] == '\'' && origin[len - 1] == '\'') {
+ if (origin[0] == '\'' && origin[len - 1] == '\'')
origin = xmemdupz(origin + 1, len - 2);
- } else {
- origin = xstrdup(origin);
- }
} else {
char *new_origin = xmalloc(strlen(origin) + strlen(src) + 5);
sprintf(new_origin, "%s of %s", origin, src);
origin = new_origin;
}
- append_to_list(&origins, origin, sha1);
+ string_list_append(origin, &origins)->util = sha1;
return 0;
}
static void print_joined(const char *singular, const char *plural,
- struct list *list, struct strbuf *out)
+ struct string_list *list, struct strbuf *out)
{
if (list->nr == 0)
return;
if (list->nr == 1) {
- strbuf_addf(out, "%s%s", singular, list->list[0]);
+ strbuf_addf(out, "%s%s", singular, list->items[0].string);
} else {
int i;
strbuf_addstr(out, plural);
for (i = 0; i < list->nr - 1; i++)
- strbuf_addf(out, "%s%s", i > 0 ? ", " : "", list->list[i]);
- strbuf_addf(out, " and %s", list->list[list->nr - 1]);
+ strbuf_addf(out, "%s%s", i > 0 ? ", " : "",
+ list->items[i].string);
+ strbuf_addf(out, " and %s", list->items[list->nr - 1].string);
}
}
int i, count = 0;
struct commit *commit;
struct object *branch;
- struct list subjects = { NULL, NULL, 0, 0 };
+ struct string_list subjects = { NULL, 0, 0, 1 };
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
+ struct strbuf sb = STRBUF_INIT;
branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
if (!branch || branch->type != OBJ_COMMIT)
if (prepare_revision_walk(rev))
die("revision walk setup failed");
while ((commit = get_revision(rev)) != NULL) {
- char *oneline, *bol, *eol;
+ struct pretty_print_context ctx = {0};
/* ignore merges */
if (commit->parents && commit->parents->next)
if (subjects.nr > limit)
continue;
- bol = strstr(commit->buffer, "\n\n");
- if (bol) {
- unsigned char c;
- do {
- c = *++bol;
- } while (isspace(c));
- if (!c)
- bol = NULL;
- }
-
- if (!bol) {
- append_to_list(&subjects, xstrdup(sha1_to_hex(
- commit->object.sha1)),
- NULL);
- continue;
- }
+ format_commit_message(commit, "%s", &sb, &ctx);
+ strbuf_ltrim(&sb);
- eol = strchr(bol, '\n');
- if (eol) {
- oneline = xmemdupz(bol, eol - bol);
- } else {
- oneline = xstrdup(bol);
- }
- append_to_list(&subjects, oneline, NULL);
+ if (!sb.len)
+ string_list_append(sha1_to_hex(commit->object.sha1),
+ &subjects);
+ else
+ string_list_append(strbuf_detach(&sb, NULL), &subjects);
}
if (count > limit)
if (i >= limit)
strbuf_addf(out, " ...\n");
else
- strbuf_addf(out, " %s\n", subjects.list[i]);
+ strbuf_addf(out, " %s\n", subjects.items[i].string);
clear_commit_marks((struct commit *)branch, flags);
clear_commit_marks(head, flags);
rev->commits = NULL;
rev->pending.nr = 0;
- free_list(&subjects);
+ string_list_clear(&subjects, 0);
}
int fmt_merge_msg(int merge_summary, struct strbuf *in, struct strbuf *out) {
die ("Error in line %d: %.*s", i, len, p);
}
+ if (!srcs.nr)
+ return 0;
+
strbuf_addstr(out, "Merge ");
for (i = 0; i < srcs.nr; i++) {
- struct src_data *src_data = srcs.payload[i];
+ struct src_data *src_data = srcs.items[i].util;
const char *subsep = "";
strbuf_addstr(out, sep);
sep = "; ";
if (src_data->head_status == 1) {
- strbuf_addstr(out, srcs.list[i]);
+ strbuf_addstr(out, srcs.items[i].string);
continue;
}
if (src_data->head_status == 3) {
print_joined("commit ", "commits ", &src_data->generic,
out);
}
- if (strcmp(".", srcs.list[i]))
- strbuf_addf(out, " of %s", srcs.list[i]);
+ if (strcmp(".", srcs.items[i].string))
+ strbuf_addf(out, " of %s", srcs.items[i].string);
}
if (!strcmp("master", current_branch))
rev.limited = 1;
for (i = 0; i < origins.nr; i++)
- shortlog(origins.list[i], origins.payload[i],
+ shortlog(origins.items[i].string, origins.items[i].util,
head, &rev, limit, out);
}
return 0;
const char *inpath = NULL;
struct option options[] = {
OPT_BOOLEAN(0, "log", &merge_summary, "populate log with the shortlog"),
- OPT_BOOLEAN(0, "summary", &merge_summary, "alias for --log"),
+ { OPTION_BOOLEAN, 0, "summary", &merge_summary, NULL,
+ "alias for --log (deprecated)",
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
OPT_FILENAME('F', "file", &inpath, "file to read from"),
OPT_END()
};
strcpy(s, sha1_to_hex(obj->sha1));
v->s = s;
}
+ else if (!strcmp(name, "objectname:short")) {
+ v->s = find_unique_abbrev(obj->sha1, DEFAULT_ABBREV);
+ }
}
}
#include "dir.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static char const * const grep_usage[] = {
/* Signalled when we are finished with everything. */
static pthread_cond_t cond_result;
+static int print_hunk_marks_between_files;
+static int printed_something;
+
static void add_work(enum work_type type, char *name, void *id)
{
grep_lock();
for(; todo[todo_done].done && todo_done != todo_start;
todo_done = (todo_done+1) % ARRAY_SIZE(todo)) {
w = &todo[todo_done];
- write_or_die(1, w->out.buf, w->out.len);
+ if (w->out.len) {
+ if (print_hunk_marks_between_files && printed_something)
+ write_or_die(1, "--\n", 3);
+ write_or_die(1, w->out.buf, w->out.len);
+ printed_something = 1;
+ }
free(w->name);
free(w->identifier);
}
if (online_cpus() == 1 || !grep_threads_ok(&opt))
use_threads = 0;
- if (use_threads)
+ if (use_threads) {
+ if (opt.pre_context || opt.post_context)
+ print_hunk_marks_between_files = 1;
start_threads(&opt);
+ }
#else
use_threads = 0;
#endif
#include "exec_cmd.h"
static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
+"git index-pack [-v] [-o <index-file>] [{ --keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
struct object_entry
{
static char git_dir[PATH_MAX+1];
setenv(GIT_DIR_ENVIRONMENT,
- getcwd(git_dir, sizeof(git_dir)), 0);
+ getcwd(git_dir, sizeof(git_dir)), argc > 0);
}
if (init_shared_repository != -1)
static const char *default_date_mode = NULL;
static int default_show_root = 1;
+static int decoration_style;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
"git log [<options>] [<since>..<until>] [[--] <path>...]\n"
" or: git show [options] <object>...";
+static int parse_decoration_style(const char *var, const char *value)
+{
+ switch (git_config_maybe_bool(var, value)) {
+ case 1:
+ return DECORATE_SHORT_REFS;
+ case 0:
+ return 0;
+ default:
+ break;
+ }
+ if (!strcmp(value, "full"))
+ return DECORATE_FULL_REFS;
+ else if (!strcmp(value, "short"))
+ return DECORATE_SHORT_REFS;
+ return -1;
+}
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev, struct setup_revision_opt *opt)
{
int i;
- int decoration_style = 0;
+ int decoration_given = 0;
+ struct userformat_want w;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
usage(builtin_log_usage);
argc = setup_revisions(argc, argv, rev, opt);
- if (!rev->show_notes_given && !rev->pretty_given)
+ memset(&w, 0, sizeof(w));
+ userformat_find_requirements(NULL, &w);
+
+ if (!rev->show_notes_given && (!rev->pretty_given || w.notes))
rev->show_notes = 1;
if (rev->show_notes)
init_display_notes(&rev->notes_opt);
const char *arg = argv[i];
if (!strcmp(arg, "--decorate")) {
decoration_style = DECORATE_SHORT_REFS;
+ decoration_given = 1;
} else if (!prefixcmp(arg, "--decorate=")) {
const char *v = skip_prefix(arg, "--decorate=");
- if (!strcmp(v, "full"))
- decoration_style = DECORATE_FULL_REFS;
- else if (!strcmp(v, "short"))
- decoration_style = DECORATE_SHORT_REFS;
- else
+ decoration_style = parse_decoration_style(arg, v);
+ if (decoration_style < 0)
die("invalid --decorate option: %s", arg);
+ decoration_given = 1;
+ } else if (!strcmp(arg, "--no-decorate")) {
+ decoration_style = 0;
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
} else if (!strcmp(arg, "-h")) {
} else
die("unrecognized argument: %s", arg);
}
+
+ /*
+ * defeat log.decorate configuration interacting with --pretty=raw
+ * from the command line.
+ */
+ if (!decoration_given && rev->pretty_given
+ && rev->commit_format == CMIT_FMT_RAW)
+ decoration_style = 0;
+
if (decoration_style) {
rev->show_decorations = 1;
load_ref_decorations(decoration_style);
return git_config_string(&fmt_patch_subject_prefix, var, value);
if (!strcmp(var, "log.date"))
return git_config_string(&default_date_mode, var, value);
+ if (!strcmp(var, "log.decorate")) {
+ decoration_style = parse_decoration_style(var, value);
+ if (decoration_style < 0)
+ decoration_style = 0; /* maybe warn? */
+ return 0;
+ }
if (!strcmp(var, "log.showroot")) {
default_show_root = git_config_bool(var, value);
return 0;
return -1;
}
-static const char cherry_usage[] =
-"git cherry [-v] [<upstream> [<head> [<limit>]]]";
+static const char * const cherry_usage[] = {
+ "git cherry [-v] [<upstream> [<head> [<limit>]]]",
+ NULL
+};
+
int cmd_cherry(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
const char *upstream;
const char *head = "HEAD";
const char *limit = NULL;
- int verbose = 0;
+ int verbose = 0, abbrev = 0;
- if (argc > 1 && !strcmp(argv[1], "-v")) {
- verbose = 1;
- argc--;
- argv++;
- }
+ struct option options[] = {
+ OPT__ABBREV(&abbrev),
+ OPT__VERBOSE(&verbose),
+ OPT_END()
+ };
- if (argc > 1 && !strcmp(argv[1], "-h"))
- usage(cherry_usage);
+ argc = parse_options(argc, argv, prefix, options, cherry_usage, 0);
switch (argc) {
- case 4:
- limit = argv[3];
- /* FALLTHROUGH */
case 3:
- head = argv[2];
+ limit = argv[2];
/* FALLTHROUGH */
case 2:
- upstream = argv[1];
+ head = argv[1];
+ /* FALLTHROUGH */
+ case 1:
+ upstream = argv[0];
break;
default:
current_branch = branch_get(NULL);
fprintf(stderr, "Could not find a tracked"
" remote branch, please"
" specify <upstream> manually.\n");
- usage(cherry_usage);
+ usage_with_options(cherry_usage, options);
}
upstream = current_branch->merge[0]->dst;
pretty_print_commit(CMIT_FMT_ONELINE, commit,
&buf, &ctx);
printf("%c %s %s\n", sign,
- sha1_to_hex(commit->object.sha1), buf.buf);
+ find_unique_abbrev(commit->object.sha1, abbrev),
+ buf.buf);
strbuf_release(&buf);
}
else {
printf("%c %s\n", sign,
- sha1_to_hex(commit->object.sha1));
+ find_unique_abbrev(commit->object.sha1, abbrev));
}
list = list->next;
printf("%s%06o %s %d\t",
tag,
ce->ce_mode,
- abbrev ? find_unique_abbrev(ce->sha1,abbrev)
- : sha1_to_hex(ce->sha1),
+ find_unique_abbrev(ce->sha1,abbrev),
ce_stage(ce));
}
write_name_quoted(ce->name + offset, stdout, line_terminator);
if (!ui->mode[i])
continue;
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
- abbrev
- ? find_unique_abbrev(ui->sha1[i], abbrev)
- : sha1_to_hex(ui->sha1[i]),
+ find_unique_abbrev(ui->sha1[i], abbrev),
i + 1);
write_name_quoted(path + offset, stdout, line_terminator);
}
#include "remote.h"
static const char ls_remote_usage[] =
-"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>] <repository> <refs>...";
+"git ls-remote [--heads] [--tags] [-u <exec> | --upload-pack <exec>]\n"
+" [<repository> [<refs>...]]";
/*
* Is there one among the list of patterns that match the tail part
break;
}
- if (!dest)
- usage(ls_remote_usage);
-
if (argv[i]) {
int j;
pattern = xcalloc(sizeof(const char *), argc - i + 1);
}
}
remote = remote_get(dest);
+ if (!remote) {
+ if (dest)
+ die("bad repository '%s'", dest);
+ die("No remote configured to list refs from.");
+ }
if (!remote->url_nr)
die("remote %s has no configured URL", dest);
transport = transport_get(remote, NULL);
} else
strcpy(size_text, "-");
printf("%06o %s %s %7s\t", mode, type,
- abbrev ? find_unique_abbrev(sha1, abbrev)
- : sha1_to_hex(sha1),
+ find_unique_abbrev(sha1, abbrev),
size_text);
} else
printf("%06o %s %s\t", mode, type,
- abbrev ? find_unique_abbrev(sha1, abbrev)
- : sha1_to_hex(sha1));
+ find_unique_abbrev(sha1, abbrev));
}
write_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,
pathname, stdout, line_termination);
continue;
}
if (i + 1 < len &&
- (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2))) {
+ (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2) ||
+ !memcmp(buf + i, ">%", 2) || !memcmp(buf + i, "%<", 2))) {
in_perforation = 1;
perforation += 2;
scissors += 2;
argv[i]);
}
- ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
- &xmp, &result);
+ xmp.ancestor = names[1];
+ xmp.file1 = names[0];
+ xmp.file2 = names[2];
+ ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result);
for (i = 0; i < 3; i++)
free(mmfs[i].ptr);
die("git write-tree failed to write a tree");
}
-static int try_merge_strategy(const char *strategy, struct commit_list *common,
- const char *head_arg)
+int try_merge_command(const char *strategy, struct commit_list *common,
+ const char *head_arg, struct commit_list *remotes)
{
const char **args;
int i = 0, x = 0, ret;
struct commit_list *j;
struct strbuf buf = STRBUF_INIT;
+
+ args = xmalloc((4 + xopts_nr + commit_list_count(common) +
+ commit_list_count(remotes)) * sizeof(char *));
+ strbuf_addf(&buf, "merge-%s", strategy);
+ args[i++] = buf.buf;
+ for (x = 0; x < xopts_nr; x++) {
+ char *s = xmalloc(strlen(xopts[x])+2+1);
+ strcpy(s, "--");
+ strcpy(s+2, xopts[x]);
+ args[i++] = s;
+ }
+ for (j = common; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i++] = "--";
+ args[i++] = head_arg;
+ for (j = remotes; j; j = j->next)
+ args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
+ args[i] = NULL;
+ ret = run_command_v_opt(args, RUN_GIT_CMD);
+ strbuf_release(&buf);
+ i = 1;
+ for (x = 0; x < xopts_nr; x++)
+ free((void *)args[i++]);
+ for (j = common; j; j = j->next)
+ free((void *)args[i++]);
+ i += 2;
+ for (j = remotes; j; j = j->next)
+ free((void *)args[i++]);
+ free(args);
+ discard_cache();
+ if (read_cache() < 0)
+ die("failed to read the cache");
+ resolve_undo_clear();
+
+ return ret;
+}
+
+static int try_merge_strategy(const char *strategy, struct commit_list *common,
+ const char *head_arg)
+{
int index_fd;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
rollback_lock_file(lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
- int clean;
+ int clean, x;
struct commit *result;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
struct commit_list *reversed = NULL;
struct merge_options o;
+ struct commit_list *j;
if (remoteheads->next) {
error("Not handling anything other than two heads merge.");
rollback_lock_file(lock);
return clean ? 0 : 1;
} else {
- args = xmalloc((4 + xopts_nr + commit_list_count(common) +
- commit_list_count(remoteheads)) * sizeof(char *));
- strbuf_addf(&buf, "merge-%s", strategy);
- args[i++] = buf.buf;
- for (x = 0; x < xopts_nr; x++) {
- char *s = xmalloc(strlen(xopts[x])+2+1);
- strcpy(s, "--");
- strcpy(s+2, xopts[x]);
- args[i++] = s;
- }
- for (j = common; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- args[i++] = "--";
- args[i++] = head_arg;
- for (j = remoteheads; j; j = j->next)
- args[i++] = xstrdup(sha1_to_hex(j->item->object.sha1));
- args[i] = NULL;
- ret = run_command_v_opt(args, RUN_GIT_CMD);
- strbuf_release(&buf);
- i = 1;
- for (x = 0; x < xopts_nr; x++)
- free((void *)args[i++]);
- for (j = common; j; j = j->next)
- free((void *)args[i++]);
- i += 2;
- for (j = remoteheads; j; j = j->next)
- free((void *)args[i++]);
- free(args);
- discard_cache();
- if (read_cache() < 0)
- die("failed to read the cache");
- resolve_undo_clear();
- return ret;
+ return try_merge_command(strategy, common, head_arg, remoteheads);
}
}
#include "refs.h"
#ifndef NO_PTHREADS
-#include "thread-utils.h"
#include <pthread.h>
+#include "thread-utils.h"
#endif
static const char pack_usage[] =
#ifndef NO_PTHREADS
+static void try_to_free_from_threads(size_t size)
+{
+ read_lock();
+ release_pack_memory(size, -1);
+ read_unlock();
+}
+
/*
* The main thread waits on the condition that (at least) one of the workers
* has stopped working (which is indicated in the .working member of
*/
static void init_threaded_search(void)
{
- pthread_mutex_init(&read_mutex, NULL);
+ init_recursive_mutex(&read_mutex);
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
+ set_try_to_free_routine(try_to_free_from_threads);
}
static void cleanup_threaded_search(void)
{
+ set_try_to_free_routine(NULL);
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&read_mutex);
pthread_mutex_destroy(&cache_mutex);
#include "parse-options.h"
static const char * const push_usage[] = {
- "git push [<options>] [<repository> <refspec>...]",
+ "git push [<options>] [<repository> [<refspec>...]]",
NULL,
};
*/
static const char reflog_expire_usage[] =
-"git reflog (show|expire) [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+"git reflog expire [--verbose] [--dry-run] [--stale-fix] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
static const char reflog_delete_usage[] =
"git reflog delete [--verbose] [--dry-run] [--rewrite] [--updateref] <refs>...";
FILE *newlog;
const char *ref;
struct commit *ref_commit;
+ struct commit_list *mark_list;
+ unsigned long mark_limit;
struct cmd_reflog_expire_cb *cmd;
unsigned char last_kept_sha1[20];
};
return 1;
}
-static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+/*
+ * Starting from commits in the cb->mark_list, mark commits that are
+ * reachable from them. Stop the traversal at commits older than
+ * the expire_limit and queue them back, so that the caller can call
+ * us again to restart the traversal with longer expire_limit.
+ */
+static void mark_reachable(struct expire_reflog_cb *cb)
{
- /*
- * We may or may not have the commit yet - if not, look it
- * up using the supplied sha1.
- */
- if (!commit) {
- if (is_null_sha1(sha1))
- return 0;
-
- commit = lookup_commit_reference_gently(sha1, 1);
-
- /* Not a commit -- keep it */
- if (!commit)
- return 0;
- }
-
- /* Reachable from the current ref? Don't prune. */
- if (commit->object.flags & REACHABLE)
- return 0;
- if (in_merge_bases(commit, &cb->ref_commit, 1))
- return 0;
-
- /* We can't reach it - prune it. */
- return 1;
-}
+ struct commit *commit;
+ struct commit_list *pending;
+ unsigned long expire_limit = cb->mark_limit;
+ struct commit_list *leftover = NULL;
-static void mark_reachable(struct commit *commit, unsigned long expire_limit)
-{
- /*
- * We need to compute whether the commit on either side of a reflog
- * entry is reachable from the tip of the ref for all entries.
- * Mark commits that are reachable from the tip down to the
- * time threshold first; we know a commit marked thusly is
- * reachable from the tip without running in_merge_bases()
- * at all.
- */
- struct commit_list *pending = NULL;
+ for (pending = cb->mark_list; pending; pending = pending->next)
+ pending->item->object.flags &= ~REACHABLE;
- commit_list_insert(commit, &pending);
+ pending = cb->mark_list;
while (pending) {
struct commit_list *entry = pending;
struct commit_list *parent;
if (parse_commit(commit))
continue;
commit->object.flags |= REACHABLE;
- if (commit->date < expire_limit)
+ if (commit->date < expire_limit) {
+ commit_list_insert(commit, &leftover);
continue;
+ }
+ commit->object.flags |= REACHABLE;
parent = commit->parents;
while (parent) {
commit = parent->item;
commit_list_insert(commit, &pending);
}
}
+ cb->mark_list = leftover;
+}
+
+static int unreachable(struct expire_reflog_cb *cb, struct commit *commit, unsigned char *sha1)
+{
+ /*
+ * We may or may not have the commit yet - if not, look it
+ * up using the supplied sha1.
+ */
+ if (!commit) {
+ if (is_null_sha1(sha1))
+ return 0;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+
+ /* Not a commit -- keep it */
+ if (!commit)
+ return 0;
+ }
+
+ /* Reachable from the current ref? Don't prune. */
+ if (commit->object.flags & REACHABLE)
+ return 0;
+
+ if (cb->mark_list && cb->mark_limit) {
+ cb->mark_limit = 0; /* dig down to the root */
+ mark_reachable(cb);
+ }
+
+ return !(commit->object.flags & REACHABLE);
}
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
cb.ref = ref;
cb.cmd = cmd;
- if (cb.ref_commit)
- mark_reachable(cb.ref_commit, cmd->expire_total);
+ if (cb.ref_commit) {
+ cb.mark_list = NULL;
+ commit_list_insert(cb.ref_commit, &cb.mark_list);
+ cb.mark_limit = cmd->expire_total;
+ mark_reachable(&cb);
+ }
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
if (cb.ref_commit)
clear_commit_marks(cb.ref_commit, REACHABLE);
*/
if (graph_show_remainder(revs->graph))
putchar('\n');
+ if (revs->commit_format == CMIT_FMT_ONELINE)
+ putchar('\n');
}
} else {
- if (buf.len)
+ if (revs->commit_format != CMIT_FMT_USERFORMAT ||
+ buf.len)
printf("%s%c", buf.buf, info->hdr_termination);
}
strbuf_release(&buf);
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
- revs.abbrev = 0;
+ revs.abbrev = DEFAULT_ABBREV;
revs.commit_format = CMIT_FMT_UNSPECIFIED;
argc = setup_revisions(argc, argv, &revs, NULL);
static int allow_rerere_auto;
static const char *me;
+static const char *strategy;
#define GIT_REFLOG_ACTION "GIT_REFLOG_ACTION"
+static char *get_encoding(const char *message);
+
static void parse_args(int argc, const char **argv)
{
const char * const * usage_str =
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
+ OPT_STRING(0, "strategy", &strategy, "strategy", "merge strategy"),
OPT_END(),
OPT_END(),
OPT_END(),
exit(1);
}
-static char *get_oneline(const char *message)
+struct commit_message {
+ char *parent_label;
+ const char *label;
+ const char *subject;
+ char *reencoded_message;
+ const char *message;
+};
+
+static int get_message(const char *raw_message, struct commit_message *out)
{
- char *result;
- const char *p = message, *abbrev, *eol;
+ const char *encoding;
+ const char *p, *abbrev, *eol;
+ char *q;
int abbrev_len, oneline_len;
- if (!p)
- die ("Could not read commit message of %s",
- sha1_to_hex(commit->object.sha1));
+ if (!raw_message)
+ return -1;
+ encoding = get_encoding(raw_message);
+ if (!encoding)
+ encoding = "UTF-8";
+ if (!git_commit_encoding)
+ git_commit_encoding = "UTF-8";
+
+ out->reencoded_message = NULL;
+ out->message = raw_message;
+ if (strcmp(encoding, git_commit_encoding))
+ out->reencoded_message = reencode_string(raw_message,
+ git_commit_encoding, encoding);
+ if (out->reencoded_message)
+ out->message = out->reencoded_message;
+
+ abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+ abbrev_len = strlen(abbrev);
+
+ /* Find beginning and end of commit subject. */
+ p = out->message;
while (*p && (*p != '\n' || p[1] != '\n'))
p++;
-
if (*p) {
p += 2;
for (eol = p + 1; *eol && *eol != '\n'; eol++)
; /* do nothing */
} else
eol = p;
- abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
- abbrev_len = strlen(abbrev);
oneline_len = eol - p;
- result = xmalloc(abbrev_len + 5 + oneline_len);
- memcpy(result, abbrev, abbrev_len);
- memcpy(result + abbrev_len, "... ", 4);
- memcpy(result + abbrev_len + 4, p, oneline_len);
- result[abbrev_len + 4 + oneline_len] = '\0';
- return result;
+
+ out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
+ strlen("... ") + oneline_len + 1);
+ q = out->parent_label;
+ q = mempcpy(q, "parent of ", strlen("parent of "));
+ out->label = q;
+ q = mempcpy(q, abbrev, abbrev_len);
+ q = mempcpy(q, "... ", strlen("... "));
+ out->subject = q;
+ q = mempcpy(q, p, oneline_len);
+ *q = '\0';
+ return 0;
+}
+
+static void free_message(struct commit_message *msg)
+{
+ free(msg->parent_label);
+ free(msg->reencoded_message);
}
static char *get_encoding(const char *message)
return NULL;
}
-static struct lock_file msg_file;
-static int msg_fd;
-
-static void add_to_msg(const char *string)
-{
- int len = strlen(string);
- if (write_in_full(msg_fd, string, len) < 0)
- die_errno ("Could not write to MERGE_MSG");
-}
-
-static void add_message_to_msg(const char *message)
+static void add_message_to_msg(struct strbuf *msgbuf, const char *message)
{
const char *p = message;
while (*p && (*p != '\n' || p[1] != '\n'))
p++;
if (!*p)
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(msgbuf, sha1_to_hex(commit->object.sha1));
p += 2;
- add_to_msg(p);
- return;
+ strbuf_addstr(msgbuf, p);
}
static void set_author_ident_env(const char *message)
return strbuf_detach(&helpbuf, NULL);
}
+static void write_message(struct strbuf *msgbuf, const char *filename)
+{
+ static struct lock_file msg_file;
+
+ int msg_fd = hold_lock_file_for_update(&msg_file, filename,
+ LOCK_DIE_ON_ERROR);
+ if (write_in_full(msg_fd, msgbuf->buf, msgbuf->len) < 0)
+ die_errno("Could not write to %s.", filename);
+ strbuf_release(msgbuf);
+ if (commit_lock_file(&msg_file) < 0)
+ die("Error wrapping up %s", filename);
+}
+
static struct tree *empty_tree(void)
{
struct tree *tree = xcalloc(1, sizeof(struct tree));
return write_ref_sha1(ref_lock, to, "cherry-pick");
}
+static void do_recursive_merge(struct commit *base, struct commit *next,
+ const char *base_label, const char *next_label,
+ unsigned char *head, struct strbuf *msgbuf,
+ char *defmsg)
+{
+ struct merge_options o;
+ struct tree *result, *next_tree, *base_tree, *head_tree;
+ int clean, index_fd;
+ static struct lock_file index_lock;
+
+ index_fd = hold_locked_index(&index_lock, 1);
+
+ read_cache();
+ init_merge_options(&o);
+ o.ancestor = base ? base_label : "(empty tree)";
+ o.branch1 = "HEAD";
+ o.branch2 = next ? next_label : "(empty tree)";
+
+ head_tree = parse_tree_indirect(head);
+ next_tree = next ? next->tree : empty_tree();
+ base_tree = base ? base->tree : empty_tree();
+
+ clean = merge_trees(&o,
+ head_tree,
+ next_tree, base_tree, &result);
+
+ if (active_cache_changed &&
+ (write_cache(index_fd, active_cache, active_nr) ||
+ commit_locked_index(&index_lock)))
+ die("%s: Unable to write new index file", me);
+ rollback_lock_file(&index_lock);
+
+ if (!clean) {
+ int i;
+ strbuf_addstr(msgbuf, "\nConflicts:\n\n");
+ for (i = 0; i < active_nr;) {
+ struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_addch(msgbuf, '\t');
+ strbuf_addstr(msgbuf, ce->name);
+ strbuf_addch(msgbuf, '\n');
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
+ write_message(msgbuf, defmsg);
+ fprintf(stderr, "Automatic %s failed.%s\n",
+ me, help_msg(commit_name));
+ rerere(allow_rerere_auto);
+ exit(1);
+ }
+ write_message(msgbuf, defmsg);
+ fprintf(stderr, "Finished one %s.\n", me);
+}
+
static int revert_or_cherry_pick(int argc, const char **argv)
{
unsigned char head[20];
struct commit *base, *next, *parent;
- int i, index_fd, clean;
- char *oneline, *reencoded_message = NULL;
- const char *message, *encoding;
+ const char *base_label, *next_label;
+ struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
char *defmsg = NULL;
- struct merge_options o;
- struct tree *result, *next_tree, *base_tree, *head_tree;
- static struct lock_file index_lock;
+ struct strbuf msgbuf = STRBUF_INIT;
git_config(git_default_config, NULL);
me = action == REVERT ? "revert" : "cherry-pick";
if (allow_ff && !hashcmp(parent->object.sha1, head))
return fast_forward_to(commit->object.sha1, head);
- if (!(message = commit->buffer))
- die ("Cannot get commit message for %s",
- sha1_to_hex(commit->object.sha1));
-
if (parent && parse_commit(parent) < 0)
die("%s: cannot parse parent commit %s",
me, sha1_to_hex(parent->object.sha1));
+ if (get_message(commit->buffer, &msg) != 0)
+ die("Cannot get commit message for %s",
+ sha1_to_hex(commit->object.sha1));
+
/*
* "commit" is an existing commit. We would want to apply
* the difference it introduces since its first parent "prev"
*/
defmsg = git_pathdup("MERGE_MSG");
- msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
- LOCK_DIE_ON_ERROR);
-
- encoding = get_encoding(message);
- if (!encoding)
- encoding = "UTF-8";
- if (!git_commit_encoding)
- git_commit_encoding = "UTF-8";
- if ((reencoded_message = reencode_string(message,
- git_commit_encoding, encoding)))
- message = reencoded_message;
-
- oneline = get_oneline(message);
-
- index_fd = hold_locked_index(&index_lock, 1);
if (action == REVERT) {
- char *oneline_body = strchr(oneline, ' ');
-
base = commit;
+ base_label = msg.label;
next = parent;
- add_to_msg("Revert \"");
- add_to_msg(oneline_body + 1);
- add_to_msg("\"\n\nThis reverts commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
+ next_label = msg.parent_label;
+ strbuf_addstr(&msgbuf, "Revert \"");
+ strbuf_addstr(&msgbuf, msg.subject);
+ strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
if (commit->parents->next) {
- add_to_msg(", reversing\nchanges made to ");
- add_to_msg(sha1_to_hex(parent->object.sha1));
+ strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
}
- add_to_msg(".\n");
+ strbuf_addstr(&msgbuf, ".\n");
} else {
base = parent;
+ base_label = msg.parent_label;
next = commit;
- set_author_ident_env(message);
- add_message_to_msg(message);
+ next_label = msg.label;
+ set_author_ident_env(msg.message);
+ add_message_to_msg(&msgbuf, msg.message);
if (no_replay) {
- add_to_msg("(cherry picked from commit ");
- add_to_msg(sha1_to_hex(commit->object.sha1));
- add_to_msg(")\n");
+ strbuf_addstr(&msgbuf, "(cherry picked from commit ");
+ strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+ strbuf_addstr(&msgbuf, ")\n");
}
}
- read_cache();
- init_merge_options(&o);
- o.branch1 = "HEAD";
- o.branch2 = oneline;
-
- head_tree = parse_tree_indirect(head);
- next_tree = next ? next->tree : empty_tree();
- base_tree = base ? base->tree : empty_tree();
-
- clean = merge_trees(&o,
- head_tree,
- next_tree, base_tree, &result);
-
- if (active_cache_changed &&
- (write_cache(index_fd, active_cache, active_nr) ||
- commit_locked_index(&index_lock)))
- die("%s: Unable to write new index file", me);
- rollback_lock_file(&index_lock);
-
- if (!clean) {
- add_to_msg("\nConflicts:\n\n");
- for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- add_to_msg("\t");
- add_to_msg(ce->name);
- add_to_msg("\n");
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
+ if (!strategy || !strcmp(strategy, "recursive") || action == REVERT)
+ do_recursive_merge(base, next, base_label, next_label,
+ head, &msgbuf, defmsg);
+ else {
+ int res;
+ struct commit_list *common = NULL;
+ struct commit_list *remotes = NULL;
+ write_message(&msgbuf, defmsg);
+ commit_list_insert(base, &common);
+ commit_list_insert(next, &remotes);
+ res = try_merge_command(strategy, common,
+ sha1_to_hex(head), remotes);
+ free_commit_list(common);
+ free_commit_list(remotes);
+ if (res) {
+ fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
+ me, strategy, help_msg(commit_name));
+ rerere(allow_rerere_auto);
+ exit(1);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Automatic %s failed.%s\n",
- me, help_msg(commit_name));
- rerere(allow_rerere_auto);
- exit(1);
}
- if (commit_lock_file(&msg_file) < 0)
- die ("Error wrapping up %s", defmsg);
- fprintf(stderr, "Finished one %s.\n", me);
/*
*
args[i] = NULL;
return execv_git_cmd(args);
}
- free(reencoded_message);
+ free_message(&msg);
free(defmsg);
return 0;
static int verify_tag(const char *name, const char *ref,
const unsigned char *sha1)
{
- const char *argv_verify_tag[] = {"git-verify-tag",
+ const char *argv_verify_tag[] = {"verify-tag",
"-v", "SHA1_HEX", NULL};
argv_verify_tag[2] = sha1_to_hex(sha1);
- if (run_command_v_opt(argv_verify_tag, 0))
+ if (run_command_v_opt(argv_verify_tag, RUN_GIT_CMD))
return error("could not verify the tag '%s'", name);
return 0;
}
return read_sha1_file_repl(sha1, type, size, NULL);
}
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
-extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
+extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
extern int has_pack_index(const unsigned char *sha1);
+extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
+
extern const signed char hexval_table[256];
static inline unsigned int hexval(unsigned char c)
{
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int git_default_config(const char *, const char *, void *);
extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern int git_config_parse_parameter(const char *text);
+extern int git_config_from_parameters(config_fn_t fn, void *data);
extern int git_config(config_fn_t fn, void *);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_bool_or_int(const char *, const char *, int *);
extern int git_config_bool(const char *, const char *);
+extern int git_config_maybe_bool(const char *, const char *);
extern int git_config_string(const char **, const char *, const char *);
extern int git_config_pathname(const char **, const char *, const char *);
extern int git_config_set(const char *, const char *);
extern int git_config_rename_section(const char *, const char *);
extern const char *git_etc_gitconfig(void);
extern int check_repository_format_version(const char *var, const char *value, void *cb);
+extern int git_env_bool(const char *, int);
extern int git_config_system(void);
extern int git_config_global(void);
extern int config_error_nonbool(const char *);
#define WS_INDENT_WITH_NON_TAB 04
#define WS_CR_AT_EOL 010
#define WS_BLANK_AT_EOF 020
+#define WS_TAB_IN_INDENT 040
#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
extern unsigned whitespace_rule_cfg;
extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
extern char *whitespace_error_string(unsigned ws);
-extern int ws_fix_copy(char *, const char *, int, unsigned, int *);
+extern void ws_fix_copy(struct strbuf *, const char *, int, unsigned, int *);
extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
/* ls-files */
static void combine_diff(const unsigned char *parent, unsigned int mode,
mmfile_t *result_file,
struct sline *sline, unsigned int cnt, int n,
- int num_parent)
+ int num_parent, int result_deleted)
{
unsigned int p_lno, lno;
unsigned long nmask = (1UL << n);
xpparam_t xpp;
xdemitconf_t xecfg;
mmfile_t parent_file;
- xdemitcb_t ecb;
struct combine_diff_state state;
unsigned long sz;
- if (!cnt)
+ if (result_deleted)
return; /* result deleted */
parent_file.ptr = grab_blob(parent, mode, &sz);
state.n = n;
xdi_diff_outf(&parent_file, result_file, consume_line, &state,
- &xpp, &xecfg, &ecb);
+ &xpp, &xecfg);
free(parent_file.ptr);
/* Assign line numbers for this parent.
}
static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
- int use_color)
+ int use_color, int result_deleted)
{
unsigned long mark = (1UL<<num_parent);
unsigned long no_pre_delete = (2UL<<num_parent);
const char *c_plain = diff_get_color(use_color, DIFF_PLAIN);
const char *c_reset = diff_get_color(use_color, DIFF_RESET);
- if (!cnt)
+ if (result_deleted)
return; /* result deleted */
while (1) {
{
struct diff_options *opt = &rev->diffopt;
unsigned long result_size, cnt, lno;
+ int result_deleted = 0;
char *result, *cp;
struct sline *sline; /* survived lines */
int mode_differs = 0;
}
else {
deleted_file:
+ result_deleted = 1;
result_size = 0;
elem->mode = 0;
result = xcalloc(1, 1);
combine_diff(elem->parent[i].sha1,
elem->parent[i].mode,
&result_file, sline,
- cnt, i, num_parent);
+ cnt, i, num_parent, result_deleted);
if (elem->parent[i].mode != elem->mode)
mode_differs = 1;
}
dump_quoted_path("+++ ", b_prefix, elem->path,
c_meta, c_reset);
dump_sline(sline, cnt, num_parent,
- DIFF_OPT_TST(opt, COLOR_DIFF));
+ DIFF_OPT_TST(opt, COLOR_DIFF), result_deleted);
}
free(result);
free(other);
return result;
}
+
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
+int commit_tree(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author)
+{
+ int result;
+ int encoding_is_utf8;
+ struct strbuf buffer;
+
+ assert_sha1_type(tree, OBJ_TREE);
+
+ /* Not having i18n.commitencoding is the same as having utf-8 */
+ encoding_is_utf8 = is_encoding_utf8(git_commit_encoding);
+
+ strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */
+ strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));
+
+ /*
+ * NOTE! This ordering means that the same exact tree merged with a
+ * different order of parents will be a _different_ changeset even
+ * if everything else stays the same.
+ */
+ while (parents) {
+ struct commit_list *next = parents->next;
+ strbuf_addf(&buffer, "parent %s\n",
+ sha1_to_hex(parents->item->object.sha1));
+ free(parents);
+ parents = next;
+ }
+
+ /* Person/date information */
+ if (!author)
+ author = git_author_info(IDENT_ERROR_ON_NO_NAME);
+ strbuf_addf(&buffer, "author %s\n", author);
+ strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
+ if (!encoding_is_utf8)
+ strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
+ strbuf_addch(&buffer, '\n');
+
+ /* And add the comment */
+ strbuf_addstr(&buffer, msg);
+
+ /* And check the encoding */
+ if (encoding_is_utf8 && !is_utf8(buffer.buf))
+ fprintf(stderr, commit_utf8_warn);
+
+ result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+ strbuf_release(&buffer);
+ return result;
+}
struct reflog_walk_info *reflog_info;
};
+struct userformat_want {
+ unsigned notes:1;
+};
+
extern int has_non_ascii(const char *text);
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
extern char *reencode_commit_message(const struct commit *commit,
const char **encoding_p);
extern void get_commit_format(const char *arg, struct rev_info *);
+extern void userformat_find_requirements(const char *fmt, struct userformat_want *w);
extern void format_commit_message(const struct commit *commit,
const char *format, struct strbuf *sb,
const struct pretty_print_context *context);
struct commit_list *reduce_heads(struct commit_list *heads);
+extern int commit_tree(const char *msg, unsigned char *tree,
+ struct commit_list *parents, unsigned char *ret,
+ const char *author);
+
#endif /* COMMIT_H */
return fd;
}
+#undef write
+ssize_t mingw_write(int fd, const void *buf, size_t count)
+{
+ /*
+ * While write() calls to a file on a local disk are translated
+ * into WriteFile() calls with a maximum size of 64KB on Windows
+ * XP and 256KB on Vista, no such cap is placed on writes to
+ * files over the network on Windows XP. Unfortunately, there
+ * seems to be a limit of 32MB-28KB on X64 and 64MB-32KB on x86;
+ * bigger writes fail on Windows XP.
+ * So we cap to a nice 31MB here to avoid write failures over
+ * the net without changing the number of WriteFile() calls in
+ * the local case.
+ */
+ return write(fd, buf, min(count, 31 * 1024 * 1024));
+}
+
#undef fopen
FILE *mingw_fopen (const char *filename, const char *otype)
{
}
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
+ const char *dir,
int prepend_cmd, int fhin, int fhout, int fherr)
{
STARTUPINFO si;
memset(&pi, 0, sizeof(pi));
ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags,
- env ? envblk.buf : NULL, NULL, &si, &pi);
+ env ? envblk.buf : NULL, dir, &si, &pi);
if (env)
strbuf_release(&envblk);
static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
int prepend_cmd)
{
- return mingw_spawnve_fd(cmd, argv, env, prepend_cmd, 0, 1, 2);
+ return mingw_spawnve_fd(cmd, argv, env, NULL, prepend_cmd, 0, 1, 2);
}
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+ const char *dir,
int fhin, int fhout, int fherr)
{
pid_t pid;
pid = -1;
}
else {
- pid = mingw_spawnve_fd(iprog, argv, env, 1,
+ pid = mingw_spawnve_fd(iprog, argv, env, dir, 1,
fhin, fhout, fherr);
free(iprog);
}
argv[0] = argv0;
}
else
- pid = mingw_spawnve_fd(prog, argv, env, 0,
+ pid = mingw_spawnve_fd(prog, argv, env, dir, 0,
fhin, fhout, fherr);
free(prog);
}
int mingw_open (const char *filename, int oflags, ...);
#define open mingw_open
+ssize_t mingw_write(int fd, const void *buf, size_t count);
+#define write mingw_write
+
FILE *mingw_fopen (const char *filename, const char *otype);
#define fopen mingw_fopen
#define utime mingw_utime
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+ const char *dir,
int fhin, int fhout, int fherr);
void mingw_execvp(const char *cmd, char *const *argv);
#define execvp mingw_execvp
--- /dev/null
+/* Intentionally empty file to support building git with MSVC */
*/
#define pthread_mutex_t CRITICAL_SECTION
-#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
+#define pthread_mutex_init(a,b) (InitializeCriticalSection((a)), 0)
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
#define pthread_mutex_lock EnterCriticalSection
#define pthread_mutex_unlock LeaveCriticalSection
+typedef int pthread_mutexattr_t;
+#define pthread_mutexattr_init(a) (*(a) = 0)
+#define pthread_mutexattr_destroy(a) do {} while (0)
+#define pthread_mutexattr_settype(a, t) 0
+#define PTHREAD_MUTEX_RECURSIVE 0
+
/*
* Implement simple condition variable for Windows threads, based on ACE
* implementation.
{
HANDLE hmap;
void *temp;
- size_t len;
+ off_t len;
struct stat st;
uint64_t o = offset;
uint32_t l = o & 0xFFFFFFFF;
uint32_t h = (o >> 32) & 0xFFFFFFFF;
if (!fstat(fd, &st))
- len = xsize_t(st.st_size);
+ len = st.st_size;
else
die("mmap: could not determine filesize");
if ((length + offset) > len)
- length = len - offset;
+ length = xsize_t(len - offset);
if (!(flags & MAP_PRIVATE))
die("Invalid usage of mmap when built with USE_WIN32_MMAP");
*/
#include "cache.h"
#include "exec_cmd.h"
+#include "strbuf.h"
#define MAXNAME (256)
const char *config_exclusive_filename = NULL;
+struct config_item
+{
+ struct config_item *next;
+ char *name;
+ char *value;
+};
+static struct config_item *config_parameters;
+static struct config_item **config_parameters_tail = &config_parameters;
+
+static void lowercase(char *p)
+{
+ for (; *p; p++)
+ *p = tolower(*p);
+}
+
+int git_config_parse_parameter(const char *text)
+{
+ struct config_item *ct;
+ struct strbuf tmp = STRBUF_INIT;
+ struct strbuf **pair;
+ strbuf_addstr(&tmp, text);
+ pair = strbuf_split(&tmp, '=');
+ if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=')
+ strbuf_setlen(pair[0], pair[0]->len - 1);
+ strbuf_trim(pair[0]);
+ if (!pair[0]->len) {
+ strbuf_list_free(pair);
+ return -1;
+ }
+ ct = xcalloc(1, sizeof(struct config_item));
+ ct->name = strbuf_detach(pair[0], NULL);
+ if (pair[1]) {
+ strbuf_trim(pair[1]);
+ ct->value = strbuf_detach(pair[1], NULL);
+ }
+ strbuf_list_free(pair);
+ lowercase(ct->name);
+ *config_parameters_tail = ct;
+ config_parameters_tail = &ct->next;
+ return 0;
+}
+
static int get_next_char(void)
{
int c;
return ret;
}
-int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+int git_config_maybe_bool(const char *name, const char *value)
{
- *is_bool = 1;
if (!value)
return 1;
if (!*value)
return 0;
- if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))
+ if (!strcasecmp(value, "true")
+ || !strcasecmp(value, "yes")
+ || !strcasecmp(value, "on"))
return 1;
- if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))
+ if (!strcasecmp(value, "false")
+ || !strcasecmp(value, "no")
+ || !strcasecmp(value, "off"))
return 0;
+ return -1;
+}
+
+int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
+{
+ int v = git_config_maybe_bool(name, value);
+ if (0 <= v) {
+ *is_bool = 1;
+ return v;
+ }
*is_bool = 0;
return git_config_int(name, value);
}
return system_wide;
}
-static int git_env_bool(const char *k, int def)
+int git_env_bool(const char *k, int def)
{
const char *v = getenv(k);
return v ? git_config_bool(k, v) : def;
return !git_env_bool("GIT_CONFIG_NOGLOBAL", 0);
}
+int git_config_from_parameters(config_fn_t fn, void *data)
+{
+ const struct config_item *ct;
+ for (ct = config_parameters; ct; ct = ct->next)
+ if (fn(ct->name, ct->value, data) < 0)
+ return -1;
+ return 0;
+}
+
int git_config(config_fn_t fn, void *data)
{
int ret = 0, found = 0;
found += 1;
}
free(repo_config);
+
+ if (config_parameters) {
+ ret += git_config_from_parameters(fn, data);
+ found += 1;
+ }
+
if (found == 0)
return -1;
return ret;
AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads.])
])
+# Define option to enable JavaScript minification
+AC_ARG_ENABLE([jsmin],
+[AS_HELP_STRING([--enable-jsmin=PATH],
+ [PATH is the name of a JavaScript minifier or the absolute path to one.])],
+[
+ JSMIN=$enableval;
+ AC_MSG_NOTICE([Setting JSMIN to '$JSMIN' to enable JavaScript minifying])
+ GIT_CONF_APPEND_LINE(JSMIN=$enableval);
+])
+
+# Define option to enable CSS minification
+AC_ARG_ENABLE([cssmin],
+[AS_HELP_STRING([--enable-cssmin=PATH],
+ [PATH is the name of a CSS minifier or the absolute path to one.])],
+[
+ CSSMIN=$enableval;
+ AC_MSG_NOTICE([Setting CSSMIN to '$CSSMIN' to enable CSS minifying])
+ GIT_CONF_APPEND_LINE(CSSMIN=$enableval);
+])
+
## Site configuration (override autodetection)
## --with-PACKAGE[=ARG] and --without-PACKAGE
AC_MSG_NOTICE([CHECKS for site configuration])
local word cmdline=$(git --git-dir="$(__gitdir)" \
config --get "alias.$1")
for word in $cmdline; do
- if [ "${word##-*}" ]; then
- echo $word
+ case "$word" in
+ \!gitk|gitk)
+ echo "gitk"
return
- fi
+ ;;
+ \!*) : shell command alias ;;
+ -*) : option ;;
+ *=*) : setting env ;;
+ git) : git itself ;;
+ *)
+ echo "$word"
+ return
+ esac
done
}
COMPREPLY=()
}
+_git_gitk ()
+{
+ _gitk
+}
+
_git_grep ()
{
__git_has_doubledash && return
COMPREPLY=()
}
+_git_stage ()
+{
+ _git_add
+}
+
__git_config_get_set_variables ()
{
local prevword word config_file= c=$COMP_CWORD
esac
}
+_git_whatchanged ()
+{
+ _git_log
+}
+
_git ()
{
local i c=1 command __git_dir
return
fi
+ local completion_func="_git_${command//-/_}"
+ declare -F $completion_func >/dev/null && $completion_func && return
+
local expansion=$(__git_aliased_command "$command")
- [ "$expansion" ] && command="$expansion"
-
- case "$command" in
- am) _git_am ;;
- add) _git_add ;;
- apply) _git_apply ;;
- archive) _git_archive ;;
- bisect) _git_bisect ;;
- bundle) _git_bundle ;;
- branch) _git_branch ;;
- checkout) _git_checkout ;;
- cherry) _git_cherry ;;
- cherry-pick) _git_cherry_pick ;;
- clean) _git_clean ;;
- clone) _git_clone ;;
- commit) _git_commit ;;
- config) _git_config ;;
- describe) _git_describe ;;
- diff) _git_diff ;;
- difftool) _git_difftool ;;
- fetch) _git_fetch ;;
- format-patch) _git_format_patch ;;
- fsck) _git_fsck ;;
- gc) _git_gc ;;
- grep) _git_grep ;;
- help) _git_help ;;
- init) _git_init ;;
- log) _git_log ;;
- ls-files) _git_ls_files ;;
- ls-remote) _git_ls_remote ;;
- ls-tree) _git_ls_tree ;;
- merge) _git_merge;;
- mergetool) _git_mergetool;;
- merge-base) _git_merge_base ;;
- mv) _git_mv ;;
- name-rev) _git_name_rev ;;
- notes) _git_notes ;;
- pull) _git_pull ;;
- push) _git_push ;;
- rebase) _git_rebase ;;
- remote) _git_remote ;;
- replace) _git_replace ;;
- reset) _git_reset ;;
- revert) _git_revert ;;
- rm) _git_rm ;;
- send-email) _git_send_email ;;
- shortlog) _git_shortlog ;;
- show) _git_show ;;
- show-branch) _git_show_branch ;;
- stash) _git_stash ;;
- stage) _git_add ;;
- submodule) _git_submodule ;;
- svn) _git_svn ;;
- tag) _git_tag ;;
- whatchanged) _git_log ;;
- *) COMPREPLY=() ;;
- esac
+ if [ -n "$expansion" ]; then
+ completion_func="_git_${expansion//-/_}"
+ declare -F $completion_func >/dev/null && $completion_func
+ fi
}
_gitk ()
-#!/usr/bin/python
+#!/usr/bin/env python
## zip archive frontend for git-fast-import
##
-#! /usr/bin/python
+#!/usr/bin/env python
""" hg-to-git.py - A Mercurial to GIT converter
# possible for the email to be from someone other than the person doing the
# push.
#
+# To help with debugging and use on pre-v1.5.1 git servers, this script will
+# also obey the interface of hooks/update, taking its arguments on the
+# command line. Unfortunately, hooks/update is called once for each ref.
+# To avoid firing one email per ref, this script just prints its output to
+# the screen when used in this mode. The output can then be redirected if
+# wanted.
+#
# Config
# ------
# hooks.mailinglist
-#!/usr/bin/python
+#!/usr/bin/env python
#
# This tool is copyright (c) 2006, Sean Estabrooks.
# It is released under the Gnu Public License, version 2.
static int addrcmp(const struct sockaddr_storage *s1,
const struct sockaddr_storage *s2)
{
- if (s1->ss_family != s2->ss_family)
- return s1->ss_family - s2->ss_family;
- if (s1->ss_family == AF_INET)
+ const struct sockaddr *sa1 = (const struct sockaddr*) s1;
+ const struct sockaddr *sa2 = (const struct sockaddr*) s2;
+
+ if (sa1->sa_family != sa2->sa_family)
+ return sa1->sa_family - sa2->sa_family;
+ if (sa1->sa_family == AF_INET)
return memcmp(&((struct sockaddr_in *)s1)->sin_addr,
&((struct sockaddr_in *)s2)->sin_addr,
sizeof(struct in_addr));
#ifndef NO_IPV6
- if (s1->ss_family == AF_INET6)
+ if (sa1->sa_family == AF_INET6)
return memcmp(&((struct sockaddr_in6 *)s1)->sin6_addr,
&((struct sockaddr_in6 *)s2)->sin6_addr,
sizeof(struct in6_addr));
#include "userdiff.h"
#include "sigchain.h"
#include "submodule.h"
+#include "ll-merge.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
};
static void diff_filespec_load_driver(struct diff_filespec *one);
-static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+static size_t fill_textconv(struct userdiff_driver *driver,
+ struct diff_filespec *df, char **outbuf);
static int parse_diff_color_slot(const char *var, int ofs)
{
const char *name_b,
struct diff_filespec *one,
struct diff_filespec *two,
- const char *textconv_one,
- const char *textconv_two,
+ struct userdiff_driver *textconv_one,
+ struct userdiff_driver *textconv_two,
struct diff_options *o)
{
int lc_a, lc_b;
const char *reset = diff_get_color(color_diff, DIFF_RESET);
static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
const char *a_prefix, *b_prefix;
- const char *data_one, *data_two;
+ char *data_one, *data_two;
size_t size_one, size_two;
struct emit_callback ecbdata;
quote_two_c_style(&a_name, a_prefix, name_a, 0);
quote_two_c_style(&b_name, b_prefix, name_b, 0);
- diff_populate_filespec(one, 0);
- diff_populate_filespec(two, 0);
- if (textconv_one) {
- data_one = run_textconv(textconv_one, one, &size_one);
- if (!data_one)
- die("unable to read files to diff");
- }
- else {
- data_one = one->data;
- size_one = one->size;
- }
- if (textconv_two) {
- data_two = run_textconv(textconv_two, two, &size_two);
- if (!data_two)
- die("unable to read files to diff");
- }
- else {
- data_two = two->data;
- size_two = two->size;
- }
+ size_one = fill_textconv(textconv_one, one, &data_one);
+ size_two = fill_textconv(textconv_two, two, &data_two);
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.color_diff = color_diff;
emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
if (lc_b)
emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
+ if (textconv_one)
+ free((char *)data_one);
+ if (textconv_two)
+ free((char *)data_two);
}
struct diff_words_buffer {
{
xpparam_t xpp;
xdemitconf_t xecfg;
- xdemitcb_t ecb;
mmfile_t minus, plus;
struct diff_words_style *style = diff_words->style;
/* as only the hunk header will be parsed, we need a 0-context */
xecfg.ctxlen = 0;
xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
- &xpp, &xecfg, &ecb);
+ &xpp, &xecfg);
free(minus.ptr);
free(plus.ptr);
if (diff_words->current_plus != diff_words->plus.text.ptr +
unsigned is_unmerged:1;
unsigned is_binary:1;
unsigned is_renamed:1;
- unsigned int added, deleted;
+ uintmax_t added, deleted;
} **files;
};
static void show_stats(struct diffstat_t *data, struct diff_options *options)
{
int i, len, add, del, adds = 0, dels = 0;
- int max_change = 0, max_len = 0;
+ uintmax_t max_change = 0, max_len = 0;
int total_files = data->nr;
int width, name_width;
const char *reset, *set, *add_c, *del_c;
for (i = 0; i < data->nr; i++) {
struct diffstat_file *file = data->files[i];
- int change = file->added + file->deleted;
+ uintmax_t change = file->added + file->deleted;
fill_print_name(file);
len = strlen(file->print_name);
if (max_len < len)
for (i = 0; i < data->nr; i++) {
const char *prefix = "";
char *name = data->files[i]->print_name;
- int added = data->files[i]->added;
- int deleted = data->files[i]->deleted;
+ uintmax_t added = data->files[i]->added;
+ uintmax_t deleted = data->files[i]->deleted;
int name_len;
/*
if (data->files[i]->is_binary) {
show_name(options->file, prefix, name, len);
fprintf(options->file, " Bin ");
- fprintf(options->file, "%s%d%s", del_c, deleted, reset);
+ fprintf(options->file, "%s%"PRIuMAX"%s",
+ del_c, deleted, reset);
fprintf(options->file, " -> ");
- fprintf(options->file, "%s%d%s", add_c, added, reset);
+ fprintf(options->file, "%s%"PRIuMAX"%s",
+ add_c, added, reset);
fprintf(options->file, " bytes");
fprintf(options->file, "\n");
continue;
del = scale_linear(del, width, max_change);
}
show_name(options->file, prefix, name, len);
- fprintf(options->file, "%5d%s", added + deleted,
+ fprintf(options->file, "%5"PRIuMAX"%s", added + deleted,
added + deleted ? " " : "");
show_graph(options->file, '+', add, add_c, reset);
show_graph(options->file, '-', del, del_c, reset);
fprintf(options->file, "-\t-\t");
else
fprintf(options->file,
- "%d\t%d\t", file->added, file->deleted);
+ "%"PRIuMAX"\t%"PRIuMAX"\t",
+ file->added, file->deleted);
if (options->line_termination) {
fill_print_name(file);
if (!file->is_renamed)
struct checkdiff_t {
const char *filename;
int lineno;
+ int conflict_marker_size;
struct diff_options *o;
unsigned ws_rule;
unsigned status;
};
-static int is_conflict_marker(const char *line, unsigned long len)
+static int is_conflict_marker(const char *line, int marker_size, unsigned long len)
{
char firstchar;
int cnt;
- if (len < 8)
+ if (len < marker_size + 1)
return 0;
firstchar = line[0];
switch (firstchar) {
- case '=': case '>': case '<':
+ case '=': case '>': case '<': case '|':
break;
default:
return 0;
}
- for (cnt = 1; cnt < 7; cnt++)
+ for (cnt = 1; cnt < marker_size; cnt++)
if (line[cnt] != firstchar)
return 0;
- /* line[0] thru line[6] are same as firstchar */
- if (firstchar == '=') {
- /* divider between ours and theirs? */
- if (len != 8 || line[7] != '\n')
- return 0;
- } else if (len < 8 || !isspace(line[7])) {
- /* not divider before ours nor after theirs */
+ /* line[1] thru line[marker_size-1] are same as firstchar */
+ if (len < marker_size + 1 || !isspace(line[marker_size]))
return 0;
- }
return 1;
}
{
struct checkdiff_t *data = priv;
int color_diff = DIFF_OPT_TST(data->o, COLOR_DIFF);
+ int marker_size = data->conflict_marker_size;
const char *ws = diff_get_color(color_diff, DIFF_WHITESPACE);
const char *reset = diff_get_color(color_diff, DIFF_RESET);
const char *set = diff_get_color(color_diff, DIFF_FILE_NEW);
if (line[0] == '+') {
unsigned bad;
data->lineno++;
- if (is_conflict_marker(line + 1, len - 1)) {
+ if (is_conflict_marker(line + 1, marker_size, len - 1)) {
data->status |= 1;
fprintf(data->o->file,
"%s:%d: leftover conflict marker\n",
options->b_prefix = b;
}
-static const char *get_textconv(struct diff_filespec *one)
+static struct userdiff_driver *get_textconv(struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one))
return NULL;
if (!S_ISREG(one->mode))
return NULL;
diff_filespec_load_driver(one);
- return one->driver->textconv;
+ if (!one->driver->textconv)
+ return NULL;
+
+ if (one->driver->textconv_want_cache && !one->driver->textconv_cache) {
+ struct notes_cache *c = xmalloc(sizeof(*c));
+ struct strbuf name = STRBUF_INIT;
+
+ strbuf_addf(&name, "textconv/%s", one->driver->name);
+ notes_cache_init(c, name.buf, one->driver->textconv);
+ one->driver->textconv_cache = c;
+ }
+
+ return one->driver;
}
static void builtin_diff(const char *name_a,
const char *set = diff_get_color_opt(o, DIFF_METAINFO);
const char *reset = diff_get_color_opt(o, DIFF_RESET);
const char *a_prefix, *b_prefix;
- const char *textconv_one = NULL, *textconv_two = NULL;
+ struct userdiff_driver *textconv_one = NULL;
+ struct userdiff_driver *textconv_two = NULL;
struct strbuf header = STRBUF_INIT;
if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
}
}
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
- die("unable to read files to diff");
-
if (!DIFF_OPT_TST(o, TEXT) &&
- ( (diff_filespec_is_binary(one) && !textconv_one) ||
- (diff_filespec_is_binary(two) && !textconv_two) )) {
+ ( (!textconv_one && diff_filespec_is_binary(one)) ||
+ (!textconv_two && diff_filespec_is_binary(two)) )) {
+ if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ die("unable to read files to diff");
/* Quite common confusing case */
if (mf1.size == mf2.size &&
!memcmp(mf1.ptr, mf2.ptr, mf1.size))
const char *diffopts = getenv("GIT_DIFF_OPTS");
xpparam_t xpp;
xdemitconf_t xecfg;
- xdemitcb_t ecb;
struct emit_callback ecbdata;
const struct userdiff_funcname *pe;
strbuf_reset(&header);
}
- if (textconv_one) {
- size_t size;
- mf1.ptr = run_textconv(textconv_one, one, &size);
- if (!mf1.ptr)
- die("unable to read files to diff");
- mf1.size = size;
- }
- if (textconv_two) {
- size_t size;
- mf2.ptr = run_textconv(textconv_two, two, &size);
- if (!mf2.ptr)
- die("unable to read files to diff");
- mf2.size = size;
- }
+ mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
+ mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
pe = diff_funcname_pattern(one);
if (!pe)
}
}
xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
- &xpp, &xecfg, &ecb);
+ &xpp, &xecfg);
if (o->word_diff)
free_diff_words_data(&ecbdata);
if (textconv_one)
/* Crazy xdl interfaces.. */
xpparam_t xpp;
xdemitconf_t xecfg;
- xdemitcb_t ecb;
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
- &xpp, &xecfg, &ecb);
+ &xpp, &xecfg);
}
free_and_return:
data.lineno = 0;
data.o = o;
data.ws_rule = whitespace_rule(attr_path);
+ data.conflict_marker_size = ll_merge_marker_size(attr_path);
if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
die("unable to read files to diff");
/* Crazy xdl interfaces.. */
xpparam_t xpp;
xdemitconf_t xecfg;
- xdemitcb_t ecb;
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
xecfg.ctxlen = 1; /* at least one context line */
xpp.flags = XDF_NEED_MINIMAL;
xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
- &xpp, &xecfg, &ecb);
+ &xpp, &xecfg);
if (data.ws_rule & WS_BLANK_AT_EOF) {
struct emit_callback ecbdata;
const char *arg = av[0];
/* Output format options */
- if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
options->output_format |= DIFF_FORMAT_PATCH;
else if (opt_arg(arg, 'U', "unified", &options->context))
options->output_format |= DIFF_FORMAT_PATCH;
for (i = 0; i < q->nr; i++) {
xpparam_t xpp;
xdemitconf_t xecfg;
- xdemitcb_t ecb;
mmfile_t mf1, mf2;
struct diff_filepair *p = q->queue[i];
int len1, len2;
xecfg.ctxlen = 3;
xecfg.flags = XDL_EMIT_FUNCNAMES;
xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
- &xpp, &xecfg, &ecb);
+ &xpp, &xecfg);
}
git_SHA1_Final(sha1, &ctx);
return strbuf_detach(&buf, outsize);
}
+
+static size_t fill_textconv(struct userdiff_driver *driver,
+ struct diff_filespec *df,
+ char **outbuf)
+{
+ size_t size;
+
+ if (!driver || !driver->textconv) {
+ if (!DIFF_FILE_VALID(df)) {
+ *outbuf = "";
+ return 0;
+ }
+ if (diff_populate_filespec(df, 0))
+ die("unable to read files to diff");
+ *outbuf = df->data;
+ return df->size;
+ }
+
+ if (driver->textconv_cache) {
+ *outbuf = notes_cache_get(driver->textconv_cache, df->sha1,
+ &size);
+ if (*outbuf)
+ return size;
+ }
+
+ *outbuf = run_textconv(driver->textconv, df, &size);
+ if (!*outbuf)
+ die("unable to read files to diff");
+
+ if (driver->textconv_cache) {
+ /* ignore errors, as we might be in a readonly repository */
+ notes_cache_put(driver->textconv_cache, df->sha1, *outbuf,
+ size);
+ /*
+ * we could save up changes and flush them all at the end,
+ * but we would need an extra call after all diffing is done.
+ * Since generating a cache entry is the slow path anyway,
+ * this extra overhead probably isn't a big deal.
+ */
+ notes_cache_write(driver->textconv_cache);
+ }
+
+ return size;
+}
}
import_marks_file = make_fast_import_path(marks);
+ safe_create_leading_directories_const(import_marks_file);
import_marks_file_from_stream = from_stream;
}
static void option_export_marks(const char *marks)
{
export_marks_file = make_fast_import_path(marks);
+ safe_create_leading_directories_const(export_marks_file);
}
static void option_export_pack_edges(const char *edges)
#ifdef __GLIBC_PREREQ
#if __GLIBC_PREREQ(2, 1)
#define HAVE_STRCHRNUL
+#define HAVE_MEMPCPY
#endif
#endif
}
#endif
+#ifndef HAVE_MEMPCPY
+#define mempcpy gitmempcpy
+static inline void *gitmempcpy(void *dest, const void *src, size_t n)
+{
+ return (char *)memcpy(dest, src, n) + n;
+}
+#endif
+
extern void release_pack_memory(size_t, int);
+extern void set_try_to_free_routine(void (*routine)(size_t));
+
extern char *xstrdup(const char *str);
extern void *xmalloc(size_t size);
extern void *xmallocz(size_t size);
* Always returns the return value of unlink(2).
*/
int unlink_or_warn(const char *path);
+/*
+ * Likewise for rmdir(2).
+ */
+int rmdir_or_warn(const char *path);
+/*
+ * Calls the correct function out of {unlink,rmdir}_or_warn based on
+ * the supplied file mode.
+ */
+int remove_or_warn(unsigned int mode, const char *path);
#endif
# variable above and uncomment this
#accesslog.filename = "$fqgitdir/gitweb/access.log"
-setenv.add-environment = ( "PATH" => "/usr/local/bin:/usr/bin:/bin" )
+setenv.add-environment = ( "PATH" => env.PATH )
cgi.assign = ( ".cgi" => "" )
access_log $fqgitdir/gitweb/access.log
#cgi setup
-cgi_env PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_env PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
cgi_interp $PERL
cgi_ext cgi,pl
gitweb_css () {
cat > "$1" <<\EOFGITWEB
@@GITWEB_CSS@@
+
EOFGITWEB
}
gitweb_js () {
cat > "$1" <<\EOFGITWEB
@@GITWEB_JS@@
+
EOFGITWEB
}
gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
-gitweb_css "$GIT_DIR/gitweb/gitweb.css"
-gitweb_js "$GIT_DIR/gitweb/gitweb.js"
+gitweb_css "$GIT_DIR/@@GITWEB_CSS_NAME@@"
+gitweb_js "$GIT_DIR/@@GITWEB_JS_NAME@@"
case "$httpd" in
*lighttpd*)
onto= rebase onto given branch instead of upstream
p,preserve-merges try to recreate merges instead of ignoring them
s,strategy= use the given merge strategy
+no-ff cherry-pick all commits, even if unchanged
m,merge always used (no-op)
i,interactive always used (no-op)
Actions:
OK_TO_SKIP_PRE_REBASE=
REBASE_ROOT=
AUTOSQUASH=
+NEVER_FF=
GIT_CHERRY_PICK_HELP=" After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
pick_one () {
ff=--ff
case "$1" in -n) sha1=$2; ff= ;; *) sha1=$1 ;; esac
+ case "$NEVER_FF" in '') ;; ?*) ff= ;; esac
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
test -d "$REWRITTEN" &&
pick_one_preserving_merges "$@" && return
git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
} &&
{
+ test -s "$REWRITTEN_LIST" &&
git notes copy --for-rewrite=rebase < "$REWRITTEN_LIST" ||
true # we don't care if this copying failed
} &&
-i)
# yeah, we know
;;
+ --no-ff)
+ NEVER_FF=t
+ ;;
--root)
REBASE_ROOT=t
;;
if test ! -z "$1"
then
- output git show-ref --verify --quiet "refs/heads/$1" ||
- die "Invalid branchname: $1"
output git checkout "$1" ||
die "Could not checkout $1"
fi
has_action "$TODO" ||
die_abort "Nothing to do"
- test -d "$REWRITTEN" || skip_unnecessary_picks
+ test -d "$REWRITTEN" || test -n "$NEVER_FF" || skip_unnecessary_picks
git update-ref ORIG_HEAD $HEAD
output git checkout $ONTO && do_rest
# Copyright (c) 2005 Junio C Hamano.
#
-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'
LONG_USAGE='git-rebase replaces <branch> with a new branch of the
same name. When the --onto option is provided the new branch starts
out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>
--root)
rebase_root=t
;;
- -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
+ -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)
force_rebase=t
;;
--rerere-autoupdate|--no-rerere-autoupdate)
--- /dev/null
+#!/usr/bin/env python
+
+import hashlib
+import sys
+import os
+sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
+
+from git_remote_helpers.util import die, debug, warn
+from git_remote_helpers.git.repo import GitRepo
+from git_remote_helpers.git.exporter import GitExporter
+from git_remote_helpers.git.importer import GitImporter
+from git_remote_helpers.git.non_local import NonLocalGit
+
+def get_repo(alias, url):
+ """Returns a git repository object initialized for usage.
+ """
+
+ repo = GitRepo(url)
+ repo.get_revs()
+ repo.get_head()
+
+ hasher = hashlib.sha1()
+ hasher.update(repo.path)
+ repo.hash = hasher.hexdigest()
+
+ repo.get_base_path = lambda base: os.path.join(
+ base, 'info', 'fast-import', repo.hash)
+
+ prefix = 'refs/testgit/%s/' % alias
+ debug("prefix: '%s'", prefix)
+
+ repo.gitdir = ""
+ repo.alias = alias
+ repo.prefix = prefix
+
+ repo.exporter = GitExporter(repo)
+ repo.importer = GitImporter(repo)
+ repo.non_local = NonLocalGit(repo)
+
+ return repo
+
+
+def local_repo(repo, path):
+ """Returns a git repository object initalized for usage.
+ """
+
+ local = GitRepo(path)
+
+ local.non_local = None
+ local.gitdir = repo.gitdir
+ local.alias = repo.alias
+ local.prefix = repo.prefix
+ local.hash = repo.hash
+ local.get_base_path = repo.get_base_path
+ local.exporter = GitExporter(local)
+ local.importer = GitImporter(local)
+
+ return local
+
+
+def do_capabilities(repo, args):
+ """Prints the supported capabilities.
+ """
+
+ print "import"
+ print "export"
+ print "gitdir"
+ print "refspec refs/heads/*:%s*" % repo.prefix
+
+ print # end capabilities
+
+
+def do_list(repo, args):
+ """Lists all known references.
+
+ Bug: This will always set the remote head to master for non-local
+ repositories, since we have no way of determining what the remote
+ head is at clone time.
+ """
+
+ for ref in repo.revs:
+ debug("? refs/heads/%s", ref)
+ print "? refs/heads/%s" % ref
+
+ if repo.head:
+ debug("@refs/heads/%s HEAD" % repo.head)
+ print "@refs/heads/%s HEAD" % repo.head
+ else:
+ debug("@refs/heads/master HEAD")
+ print "@refs/heads/master HEAD"
+
+ print # end list
+
+
+def update_local_repo(repo):
+ """Updates (or clones) a local repo.
+ """
+
+ if repo.local:
+ return repo
+
+ path = repo.non_local.clone(repo.gitdir)
+ repo.non_local.update(repo.gitdir)
+ repo = local_repo(repo, path)
+ return repo
+
+
+def do_import(repo, args):
+ """Exports a fast-import stream from testgit for git to import.
+ """
+
+ if len(args) != 1:
+ die("Import needs exactly one ref")
+
+ if not repo.gitdir:
+ die("Need gitdir to import")
+
+ repo = update_local_repo(repo)
+ repo.exporter.export_repo(repo.gitdir)
+
+
+def do_export(repo, args):
+ """Imports a fast-import stream from git to testgit.
+ """
+
+ if not repo.gitdir:
+ die("Need gitdir to export")
+
+ dirname = repo.get_base_path(repo.gitdir)
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ path = os.path.join(dirname, 'testgit.marks')
+ print path
+ print path if os.path.exists(path) else ""
+ sys.stdout.flush()
+
+ update_local_repo(repo)
+ repo.importer.do_import(repo.gitdir)
+ repo.non_local.push(repo.gitdir)
+
+
+def do_gitdir(repo, args):
+ """Stores the location of the gitdir.
+ """
+
+ if not args:
+ die("gitdir needs an argument")
+
+ repo.gitdir = ' '.join(args)
+
+
+COMMANDS = {
+ 'capabilities': do_capabilities,
+ 'list': do_list,
+ 'import': do_import,
+ 'export': do_export,
+ 'gitdir': do_gitdir,
+}
+
+
+def sanitize(value):
+ """Cleans up the url.
+ """
+
+ if value.startswith('testgit::'):
+ value = value[9:]
+
+ return value
+
+
+def read_one_line(repo):
+ """Reads and processes one command.
+ """
+
+ line = sys.stdin.readline()
+
+ cmdline = line
+
+ if not cmdline:
+ warn("Unexpected EOF")
+ return False
+
+ cmdline = cmdline.strip().split()
+ if not cmdline:
+ # Blank line means we're about to quit
+ return False
+
+ cmd = cmdline.pop(0)
+ debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
+
+ if cmd not in COMMANDS:
+ die("Unknown command, %s", cmd)
+
+ func = COMMANDS[cmd]
+ func(repo, cmdline)
+ sys.stdout.flush()
+
+ return True
+
+
+def main(args):
+ """Starts a new remote helper for the specified repository.
+ """
+
+ if len(args) != 3:
+ die("Expecting exactly three arguments.")
+ sys.exit(1)
+
+ if os.getenv("GIT_DEBUG_TESTGIT"):
+ import git_remote_helpers.util
+ git_remote_helpers.util.DEBUG = True
+
+ alias = sanitize(args[1])
+ url = sanitize(args[2])
+
+ if not alias.isalnum():
+ warn("non-alnum alias '%s'", alias)
+ alias = "tmp"
+
+ args[1] = alias
+ args[2] = url
+
+ repo = get_repo(alias, url)
+
+ debug("Got arguments %s", args[1:])
+
+ more = True
+
+ while (more):
+ more = read_one_line(repo)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
--smtp-pass <str> * Password for SMTP-AUTH; not necessary.
--smtp-encryption <str> * tls or ssl; anything else disables.
--smtp-ssl * Deprecated. Use '--smtp-encryption ssl'.
+ --smtp-domain <str> * The domain name sent to HELO/EHLO handshake
+ --smtp-debug <0|1> * Disable, enable Net::SMTP debug.
Automating:
--identity <str> * Use the sendemail.<id> options.
# Handle interactive edition of files.
my $multiedit;
-my $editor = Git::command_oneline('var', 'GIT_EDITOR');
+my $editor;
sub do_edit {
+ if (!defined($editor)) {
+ $editor = Git::command_oneline('var', 'GIT_EDITOR');
+ }
if (defined($multiedit) && !$multiedit) {
map {
system('sh', '-c', $editor.' "$@"', $editor, $_);
# Variables with corresponding config settings
my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
-my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
+my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts, $smtp_domain);
my ($validate, $confirm);
my (@suppress_cc);
+my ($debug_net_smtp) = 0; # Net::SMTP, see send_message()
+
my $not_set_by_user = "true but not set by the user";
my %config_bool_settings = (
"smtpserverport" => \$smtp_server_port,
"smtpuser" => \$smtp_authuser,
"smtppass" => \$smtp_authpass,
+ "smtpdomain" => \$smtp_domain,
"to" => \@to,
"cc" => \@initial_cc,
"cccmd" => \$cc_cmd,
"smtp-pass:s" => \$smtp_authpass,
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
"smtp-encryption=s" => \$smtp_encryption,
+ "smtp-debug:i" => \$debug_net_smtp,
+ "smtp-domain:s" => \$smtp_domain,
"identity=s" => \$identity,
"annotate" => \$annotate,
"compose" => \$compose,
# We'll setup a template for the message id, using the "from" address:
my ($message_id_stamp, $message_id_serial);
-sub make_message_id
-{
+sub make_message_id {
my $uniq;
if (!defined $message_id_stamp) {
$message_id_stamp = sprintf("%s-%s", time, $$);
}
# use the simplest quoting being able to handle the recipient
-sub sanitize_address
-{
+sub sanitize_address {
my ($recipient) = @_;
my ($recipient_name, $recipient_addr) = ($recipient =~ /^(.*?)\s*(<.*)/);
}
+# Returns the local Fully Qualified Domain Name (FQDN) if available.
+#
+# Tightly configured MTAa require that a caller sends a real DNS
+# domain name that corresponds the IP address in the HELO/EHLO
+# handshake. This is used to verify the connection and prevent
+# spammers from trying to hide their identity. If the DNS and IP don't
+# match, the receiveing MTA may deny the connection.
+#
+# Here is a deny example of Net::SMTP with the default "localhost.localdomain"
+#
+# Net::SMTP=GLOB(0x267ec28)>>> EHLO localhost.localdomain
+# Net::SMTP=GLOB(0x267ec28)<<< 550 EHLO argument does not match calling host
+#
+# This maildomain*() code is based on ideas in Perl library Test::Reporter
+# /usr/share/perl5/Test/Reporter/Mail/Util.pm ==> sub _maildomain ()
+
+sub valid_fqdn {
+ my $domain = shift;
+ return !($^O eq 'darwin' && $domain =~ /\.local$/) && $domain =~ /\./;
+}
+
+sub maildomain_net {
+ my $maildomain;
+
+ if (eval { require Net::Domain; 1 }) {
+ my $domain = Net::Domain::domainname();
+ $maildomain = $domain if valid_fqdn($domain);
+ }
+
+ return $maildomain;
+}
+
+sub maildomain_mta {
+ my $maildomain;
+
+ if (eval { require Net::SMTP; 1 }) {
+ for my $host (qw(mailhost localhost)) {
+ my $smtp = Net::SMTP->new($host);
+ if (defined $smtp) {
+ my $domain = $smtp->domain;
+ $smtp->quit;
+
+ $maildomain = $domain if valid_fqdn($domain);
+
+ last if $maildomain;
+ }
+ }
+ }
+
+ return $maildomain;
+}
+
+sub maildomain {
+ return maildomain_net() || maildomain_mta() || 'localhost.localdomain';
+}
+
# Returns 1 if the message was sent, and 0 otherwise.
# In actuality, the whole program dies when there
# is an error sending a message.
-sub send_message
-{
+sub send_message {
my @recipients = unique_email_list(@to);
@cc = (grep { my $cc = extract_valid_address($_);
not grep { $cc eq $_ } @recipients
if ($smtp_encryption eq 'ssl') {
$smtp_server_port ||= 465; # ssmtp
require Net::SMTP::SSL;
- $smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port);
+ $smtp_domain ||= maildomain();
+ $smtp ||= Net::SMTP::SSL->new($smtp_server,
+ Hello => $smtp_domain,
+ Port => $smtp_server_port);
}
else {
require Net::SMTP;
+ $smtp_domain ||= maildomain();
$smtp ||= Net::SMTP->new((defined $smtp_server_port)
? "$smtp_server:$smtp_server_port"
- : $smtp_server);
+ : $smtp_server,
+ Hello => $smtp_domain,
+ Debug => $debug_net_smtp);
if ($smtp_encryption eq 'tls' && $smtp) {
require Net::SMTP::SSL;
$smtp->command('STARTTLS');
}
if (!$smtp) {
- die "Unable to initialize SMTP properly. Is there something wrong with your config?";
+ die "Unable to initialize SMTP properly. Check config and use --smtp-debug. ",
+ "VALUES: server=$smtp_server ",
+ "encryption=$smtp_encryption ",
+ "hello=$smtp_domain",
+ defined $smtp_server_port ? "port=$smtp_server_port" : "";
}
if (defined $smtp_authuser) {
# state of the base commit
if b_commit=$(git rev-parse --verify HEAD)
then
- head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
+ head=$(git rev-list --oneline -n 1 HEAD --)
else
die "You do not have the initial commit yet"
fi
}
show_stash () {
+ have_stash || die 'No stash found'
+
flags=$(git rev-parse --no-revs --flags "$@")
if test -z "$flags"
then
flags=--stat
fi
- w_commit=$(git rev-parse --verify --default $ref_stash "$@") &&
- b_commit=$(git rev-parse --verify "$w_commit^") &&
+ w_commit=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
+ b_commit=$(git rev-parse --quiet --verify "$w_commit^") ||
+ die "'$*' is not a stash"
+
git diff $flags $b_commit $w_commit
}
branch=
reference=
cached=
+recursive=
+init=
files=
nofetch=
update=
range=$sha1_dst
fi
GIT_DIR="$name/.git" \
- git log --pretty=oneline --first-parent $range | wc -l
+ git rev-list --first-parent $range -- | wc -l
)
total_commits=" ($(($total_commits + 0)))"
;;
"history\n";
}
my ($r, $c) = $gs->find_rev_before($target, not $_fetch_parent);
+ die "Cannot find SVN revision $target\n" unless defined($c);
$gs->rev_map_set($r, $c, 'reset', $uuid);
print "r$r = $c ($gs->{ref_id})\n";
}
# .. becomes %2E%2E
$refname =~ s{\.\.}{%2E%2E}g;
+ # trailing dots and .lock are not allowed
+ # .$ becomes %2E and .lock becomes %2Elock
+ $refname =~ s{\.(?=$|lock$)}{%2E};
+
+ # the sequence @{ is used to access the reflog
+ # @{ becomes %40{
+ $refname =~ s{\@\{}{%40\{}g;
+
return $refname;
}
sub rev_map_set {
my ($self, $rev, $commit, $update_ref, $uuid) = @_;
+ defined $commit or die "missing arg3\n";
length $commit == 40 or die "arg3 must be a full SHA1 hexsum\n";
my $db = $self->map_path($uuid);
my $db_lock = "$db.lock";
use strict;
use warnings;
use Carp qw/croak/;
-use File::Temp qw/tempfile/;
use IO::File qw//;
use vars qw/$_ignore_regex/;
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
" [-p|--paginate|--no-pager] [--no-replace-objects]\n"
" [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+ " [-c name=value\n"
" [--help] COMMAND [ARGS]";
const char git_more_info_string[] =
setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir, sizeof(git_dir)), 0);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "-c")) {
+ if (*argc < 2) {
+ fprintf(stderr, "-c expects a configuration string\n" );
+ usage(git_usage_string);
+ }
+ git_config_parse_parameter((*argv)[1]);
+ (*argv)++;
+ (*argc)--;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(git_usage_string);
rm -rf $RPM_BUILD_ROOT%{_mandir}
%endif
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
+install -m 644 -T contrib/completion/git-completion.bash $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d/git
+
%clean
rm -rf $RPM_BUILD_ROOT
%doc README COPYING Documentation/*.txt
%{!?_without_docs: %doc Documentation/*.html Documentation/howto}
%{!?_without_docs: %doc Documentation/technical}
+%{_sysconfdir}/bash_completion.d
%files svn
%defattr(-,root,root)
# No files for you!
%changelog
+* Fri Mar 26 2010 Ian Ward Comfort <icomfort@stanford.edu>
+- Ship bash completion support from contrib/ in the core package.
+
* Sun Jan 31 2010 Junio C Hamano <gitster@pobox.com>
- Do not use %define inside %{!?...} construct.
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
ifndef PYTHON_PATH
- PYTHON_PATH = /usr/bin/python
+ ifeq ($(uname_S),FreeBSD)
+ PYTHON_PATH = /usr/local/bin/python
+ else
+ PYTHON_PATH = /usr/bin/python
+ endif
endif
ifndef prefix
prefix = $(HOME)
--- /dev/null
+import os
+import subprocess
+import sys
+
+
+class GitExporter(object):
+ """An exporter for testgit repositories.
+
+ The exporter simply delegates to git fast-export.
+ """
+
+ def __init__(self, repo):
+ """Creates a new exporter for the specified repo.
+ """
+
+ self.repo = repo
+
+ def export_repo(self, base):
+ """Exports a fast-export stream for the given directory.
+
+ Simply delegates to git fast-epxort and pipes it through sed
+ to make the refs show up under the prefix rather than the
+ default refs/heads. This is to demonstrate how the export
+ data can be stored under it's own ref (using the refspec
+ capability).
+ """
+
+ dirname = self.repo.get_base_path(base)
+ path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ print "feature relative-marks"
+ if os.path.exists(os.path.join(dirname, 'git.marks')):
+ print "feature import-marks=%s/git.marks" % self.repo.hash
+ print "feature export-marks=%s/git.marks" % self.repo.hash
+ sys.stdout.flush()
+
+ args = ["git", "--git-dir=" + self.repo.gitpath, "fast-export", "--export-marks=" + path]
+
+ if os.path.exists(path):
+ args.append("--import-marks=" + path)
+
+ args.append("HEAD")
+
+ p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
+
+ args = ["sed", "s_refs/heads/_" + self.repo.prefix + "_g"]
+
+ subprocess.check_call(args, stdin=p1.stdout)
--- /dev/null
+import os
+import subprocess
+
+
+class GitImporter(object):
+ """An importer for testgit repositories.
+
+ This importer simply delegates to git fast-import.
+ """
+
+ def __init__(self, repo):
+ """Creates a new importer for the specified repo.
+ """
+
+ self.repo = repo
+
+ def do_import(self, base):
+ """Imports a fast-import stream to the given directory.
+
+ Simply delegates to git fast-import.
+ """
+
+ dirname = self.repo.get_base_path(base)
+ if self.repo.local:
+ gitdir = self.repo.gitpath
+ else:
+ gitdir = os.path.abspath(os.path.join(dirname, '.git'))
+ path = os.path.abspath(os.path.join(dirname, 'git.marks'))
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ args = ["git", "--git-dir=" + gitdir, "fast-import", "--quiet", "--export-marks=" + path]
+
+ if os.path.exists(path):
+ args.append("--import-marks=" + path)
+
+ subprocess.check_call(args)
--- /dev/null
+import os
+import subprocess
+
+from git_remote_helpers.util import die, warn
+
+
+class NonLocalGit(object):
+ """Handler to interact with non-local repos.
+ """
+
+ def __init__(self, repo):
+ """Creates a new non-local handler for the specified repo.
+ """
+
+ self.repo = repo
+
+ def clone(self, base):
+ """Clones the non-local repo to base.
+
+ Does nothing if a clone already exists.
+ """
+
+ path = os.path.join(self.repo.get_base_path(base), '.git')
+
+ # already cloned
+ if os.path.exists(path):
+ return path
+
+ os.makedirs(path)
+ args = ["git", "clone", "--bare", "--quiet", self.repo.gitpath, path]
+
+ subprocess.check_call(args)
+
+ return path
+
+ def update(self, base):
+ """Updates checkout of the non-local repo in base.
+ """
+
+ path = os.path.join(self.repo.get_base_path(base), '.git')
+
+ if not os.path.exists(path):
+ die("could not find repo at %s", path)
+
+ args = ["git", "--git-dir=" + path, "fetch", "--quiet", self.repo.gitpath]
+ subprocess.check_call(args)
+
+ args = ["git", "--git-dir=" + path, "update-ref", "refs/heads/master", "FETCH_HEAD"]
+ subprocess.check_call(args)
+
+ def push(self, base):
+ """Pushes from the non-local repo to base.
+ """
+
+ path = os.path.join(self.repo.get_base_path(base), '.git')
+
+ if not os.path.exists(path):
+ die("could not find repo at %s", path)
+
+ args = ["git", "--git-dir=" + path, "push", "--quiet", self.repo.gitpath]
+ subprocess.check_call(args)
--- /dev/null
+import os
+import subprocess
+
+def sanitize(rev, sep='\t'):
+ """Converts a for-each-ref line to a name/value pair.
+ """
+
+ splitrev = rev.split(sep)
+ branchval = splitrev[0]
+ branchname = splitrev[1].strip()
+ if branchname.startswith("refs/heads/"):
+ branchname = branchname[11:]
+
+ return branchname, branchval
+
+def is_remote(url):
+ """Checks whether the specified value is a remote url.
+ """
+
+ prefixes = ["http", "file", "git"]
+
+ return any(url.startswith(i) for i in prefixes)
+
+class GitRepo(object):
+ """Repo object representing a repo.
+ """
+
+ def __init__(self, path):
+ """Initializes a new repo at the given path.
+ """
+
+ self.path = path
+ self.head = None
+ self.revmap = {}
+ self.local = not is_remote(self.path)
+
+ if(self.path.endswith('.git')):
+ self.gitpath = self.path
+ else:
+ self.gitpath = os.path.join(self.path, '.git')
+
+ if self.local and not os.path.exists(self.gitpath):
+ os.makedirs(self.gitpath)
+
+ def get_revs(self):
+ """Fetches all revs from the remote.
+ """
+
+ args = ["git", "ls-remote", self.gitpath]
+ path = ".cached_revs"
+ ofile = open(path, "w")
+
+ subprocess.check_call(args, stdout=ofile)
+ output = open(path).readlines()
+ self.revmap = dict(sanitize(i) for i in output)
+ if "HEAD" in self.revmap:
+ del self.revmap["HEAD"]
+ self.revs = self.revmap.keys()
+ ofile.close()
+
+ def get_head(self):
+ """Determines the head of a local repo.
+ """
+
+ if not self.local:
+ return
+
+ path = os.path.join(self.gitpath, "HEAD")
+ head = open(path).readline()
+ self.head, _ = sanitize(head, ' ')
option add *Menubutton.font uifont startupFile
option add *Label.font uifont startupFile
option add *Message.font uifont startupFile
- option add *Entry.font uifont startupFile
+ option add *Entry.font textfont startupFile
+ option add *Text.font textfont startupFile
option add *Labelframe.font uifont startupFile
+ option add *Spinbox.font textfont startupFile
+ option add *Listbox.font mainfont startupFile
}
# Make a menu and submenus.
set findstring {}
set fstring .tf.lbar.findstring
lappend entries $fstring
- ${NS}::entry $fstring -width 30 -font textfont -textvariable findstring
+ ${NS}::entry $fstring -width 30 -textvariable findstring
trace add variable findstring write find_change
set findtype [mc "Exact"]
set findtypemenu [makedroplist .tf.lbar.findtype \
pack .bleft.top.search -side left -padx 5
set sstring .bleft.top.sstring
set searchstring ""
- ${NS}::entry $sstring -width 20 -font textfont -textvariable searchstring
+ ${NS}::entry $sstring -width 20 -textvariable searchstring
lappend entries $sstring
trace add variable searchstring write incrsearch
pack $sstring -side left -expand 1 -fill x
-command changediffdisp -variable diffelide -value {1 0}
${NS}::label .bleft.mid.labeldiffcontext -text " [mc "Lines of context"]: "
pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
- spinbox .bleft.mid.diffcontext -width 5 -font textfont \
+ spinbox .bleft.mid.diffcontext -width 5 \
-from 0 -increment 1 -to 10000000 \
-validate all -validatecommand "diffcontextvalidate %P" \
-textvariable diffcontextstring
}
bindall <$::BM> "canvscan mark %W %x %y"
bindall <B$::BM-Motion> "canvscan dragto %W %x %y"
+ bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
+ bind . <$M1B-Key-w> doquit
bindkey <Home> selfirstline
bindkey <End> sellastline
bind . <Key-Up> "selnextline -1"
message $w.m -text [mc "
Gitk - a commit viewer for git
-Copyright © 2005-2009 Paul Mackerras
+Copyright \u00a9 2005-2010 Paul Mackerras
Use and redistribute under the terms of the GNU General Public License"] \
-justify center -aspect 400 -border 2 -bg white -relief groove
[mc "Gitk key bindings:"]
[mc "<%s-Q> Quit" $M1T]
+[mc "<%s-W> Close window" $M1T]
[mc "<Home> Move to first commit"]
[mc "<End> Move to last commit"]
[mc "<Up>, p, i Move up one commit"]
raise $top
return
}
+ decode_view_opts $nextviewnum $revtreeargs
set newviewname($nextviewnum) "[mc "View"] $nextviewnum"
set newviewopts($nextviewnum,perm) 0
set newviewopts($nextviewnum,cmd) $viewargscmd($curview)
- decode_view_opts $nextviewnum $revtreeargs
vieweditor $top $nextviewnum [mc "Gitk view definition"]
}
{cmd t50= + {} {mc "Command to generate more commits to include:"}}
}
+# Convert $newviewopts($n, ...) into args for git log.
proc encode_view_opts {n} {
global known_view_options newviewopts
return [concat $rargs [shellsplit $newviewopts($n,args)]]
}
+# Fill $newviewopts($n, ...) based on args for git log.
proc decode_view_opts {n view_args} {
global known_view_options newviewopts
raise $top
return
}
+ decode_view_opts $curview $viewargs($curview)
set newviewname($curview) $viewname($curview)
set newviewopts($curview,perm) $viewperm($curview)
set newviewopts($curview,cmd) $viewargscmd($curview)
- decode_view_opts $curview $viewargs($curview)
vieweditor $top $curview "[mc "Gitk: edit view"] $viewname($curview)"
}
} elseif {$type eq "path"} {
${NS}::label $top.l -text $title
pack $top.l -in $top -side top -pady [list 3 0] -anchor w -padx 3
- text $top.t -width 40 -height 5 -background $bgcolor -font uifont
+ text $top.t -width 40 -height 5 -background $bgcolor
if {[info exists viewfiles($n)]} {
foreach f $viewfiles($n) {
$top.t insert end $f
global ignorespace
global limitdiffs vfilelimit curview
global diffencoding targetline diffnparents
- global git_version
+ global git_version currdiffsubmod
set textconv {}
if {[package vcompare $git_version "1.6.1"] >= 0} {
set diffencoding [get_path_encoding {}]
fconfigure $bdf -blocking 0 -encoding binary -eofchar {}
set blobdifffd($ids) $bdf
+ set currdiffsubmod ""
filerun $bdf [list getblobdiffline $bdf $diffids]
}
global diffnexthead diffnextnote difffilestart
global ctext_file_names ctext_file_lines
global diffinhdr treediffs mergemax diffnparents
- global diffencoding jump_to_here targetline diffline
+ global diffencoding jump_to_here targetline diffline currdiffsubmod
set nr 0
$ctext conf -state normal
} elseif {![string compare -length 10 "Submodule " $line]} {
# start of a new submodule
- if {[string compare [$ctext get "end - 4c" end] "\n \n\n"]} {
+ if {[regexp -indices "\[0-9a-f\]+\\.\\." $line nameend]} {
+ set fname [string range $line 10 [expr [lindex $nameend 0] - 2]]
+ } else {
+ set fname [string range $line 10 [expr [string first "contains " $line] - 2]]
+ }
+ if {$currdiffsubmod != $fname} {
$ctext insert end "\n"; # Add newline after commit message
}
set curdiffstart [$ctext index "end - 1c"]
lappend ctext_file_names ""
- set fname [string range $line 10 [expr [string last " " $line] - 1]]
- lappend ctext_file_lines $fname
- makediffhdr $fname $ids
- $ctext insert end "\n$line\n" filesep
+ if {$currdiffsubmod != $fname} {
+ lappend ctext_file_lines $fname
+ makediffhdr $fname $ids
+ set currdiffsubmod $fname
+ $ctext insert end "\n$line\n" filesep
+ } else {
+ $ctext insert end "$line\n" filesep
+ }
} elseif {![string compare -length 3 " >" $line]} {
+ set $currdiffsubmod ""
set line [encoding convertfrom $diffencoding $line]
$ctext insert end "$line\n" dresult
} elseif {![string compare -length 3 " <" $line]} {
+ set $currdiffsubmod ""
set line [encoding convertfrom $diffencoding $line]
$ctext insert end "$line\n" d0
} elseif {$diffinhdr} {
}
proc diffcommits {a b} {
- global diffcontext diffids blobdifffd diffinhdr
+ global diffcontext diffids blobdifffd diffinhdr currdiffsubmod
set tmpdir [gitknewtmpdir]
set fna [file join $tmpdir "commit-[string range $a 0 7]"]
set diffids [list commits $a $b]
set blobdifffd($diffids) $fd
set diffinhdr 0
+ set currdiffsubmod ""
filerun $fd [list getblobdiffline $fd $diffids]
}
set fontpref($font) [set $font]
${NS}::button $top.${font}but -text $which \
-command [list choosefont $font $which]
- if {!$use_ttk} {$top.${font}but configure -font optionfont}
${NS}::label $top.$font -relief flat -font $font \
-text $fontattr($font,family) -justify left
grid x $top.${font}but $top.$font -sticky w
mkfontdisp textfont $top [mc "Diff display font"]
mkfontdisp uifont $top [mc "User interface font"]
- if {!$use_ttk} {
- foreach w {maxpctl maxwidthl showlocal autoselect tabstopl ntag
- ldiff lattr extdifff.l extdifff.b bgbut fgbut
- diffoldbut diffnewbut hunksepbut markbgbut selbgbut
- want_ttk ttk_note} {
- $top.$w configure -font optionfont
- }
- }
-
${NS}::frame $top.buts
${NS}::button $top.buts.ok -text [mc "OK"] -command prefsok -default active
${NS}::button $top.buts.can -text [mc "Cancel"] -command prefscan -default normal
# radiobuttons look bad. This chooses white for selectColor if the
# background color is light, or black if it is dark.
proc setui {c} {
+ if {[tk windowingsystem] eq "win32"} { return }
set bg [winfo rgb . $c]
set selc black
if {[lindex $bg 0] + 1.5 * [lindex $bg 1] + 0.5 * [lindex $bg 2] > 100000} {
catch {source ~/.gitk}
-font create optionfont -family sans-serif -size -12
-
parsefont mainfont $mainfont
eval font create mainfont [fontflags mainfont]
eval font create mainfontbold [fontflags mainfont 1]
}
getcommits {}
+
+# Local variables:
+# mode: tcl
+# indent-tabs-mode: t
+# tab-width: 8
+# End:
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright ©9 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - eine Visualisierung der Git-Historie\n"
"\n"
-"Copyright © 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public License"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - un visualizador de revisiones para git\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Uso y redistribución permitidos según los términos de la Licencia Pública "
"General de GNU (GNU GPL)"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - visualisateur de commit pour git\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Utilisation et redistribution soumises aux termes de la GNU General Public "
"License"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright ©9 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - commit nézegető a githez\n"
"\n"
-"Szerzői jog ©9 2005-2009 Paul Mackerras\n"
+"Szerzői jog \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Használd és terjeszd a GNU General Public License feltételei mellett"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright © 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - un visualizzatore di revisioni per git\n"
"\n"
-"Copyright © 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
"License"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - gitコミットビューア\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"使用および再配布は GNU General Public License に従ってください"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright © 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - программа просмотра истории репозиториев Git\n"
"\n"
-"Copyright (c) 2005-2008 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Использование и распространение согласно условиям GNU General Public License"
"\n"
"Gitk - a commit viewer for git\n"
"\n"
-"Copyright ©9 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Use and redistribute under the terms of the GNU General Public License"
msgstr ""
"\n"
"Gitk - en incheckningsvisare för git\n"
"\n"
-"Copyright © 2005-2009 Paul Mackerras\n"
+"Copyright \\u00a9 2005-2010 Paul Mackerras\n"
"\n"
"Använd och vidareförmedla enligt villkoren i GNU General Public License"
=======================================
First you have to generate gitweb.cgi from gitweb.perl using
-"make gitweb/gitweb.cgi", then copy appropriate files (gitweb.cgi,
+"make gitweb", then copy appropriate files (gitweb.cgi, gitweb.js,
gitweb.css, git-logo.png and git-favicon.png) to their destination.
For example if git was (or is) installed with /usr prefix, you can do
- $ make prefix=/usr gitweb/gitweb.cgi ;# as yourself
+ $ make prefix=/usr gitweb ;# as yourself
# cp gitweb/git* /var/www/cgi-bin/ ;# as root
Alternatively you can use autoconf generated ./configure script to
$ make configure ;# as yourself
$ ./configure --prefix=/usr ;# as yourself
- $ make gitweb/gitweb.cgi ;# as yourself
+ $ make gitweb ;# as yourself
# cp gitweb/git* /var/www/cgi-bin/ ;# as root
The above example assumes that your web server is configured to run
- There are many configuration variables which affect building of
gitweb.cgi; see "default configuration for gitweb" section in main
- (top dir) Makefile, and instructions for building gitweb/gitweb.cgi
- target.
+ (top dir) Makefile, and instructions for building gitweb target.
One of the most important is where to find the git wrapper binary. Gitweb
tries to find the git wrapper at $(bindir)/git, so you have to set $bindir
a suggestion).
- You can control where gitweb tries to find its main CSS style file,
- its favicon and logo with the GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
- build configuration variables. By default gitweb tries to find them
- in the same directory as gitweb.cgi script.
+ its JavaScript file, its favicon and logo with the GITWEB_CSS, GITWEB_JS
+ GITWEB_FAVICON and GITWEB_LOGO build configuration variables. By default
+ gitweb tries to find them in the same directory as gitweb.cgi script.
+
+- You can optionally generate minified versions of gitweb.js and gitweb.css
+ by defining the JSMIN and CSSMIN build configuration variables. By default
+ the non-minified versions will be used. NOTE: if you enable this option,
+ substitute gitweb.min.js and gitweb.min.css for all uses of gitweb.js and
+ gitweb.css in the help files.
Build example
~~~~~~~~~~~~~
we want to display are under /home/local/scm, you can do
make GITWEB_PROJECTROOT="/home/local/scm" \
+ GITWEB_JS="/gitweb/gitweb.js" \
GITWEB_CSS="/gitweb/gitweb.css" \
GITWEB_LOGO="/gitweb/git-logo.png" \
GITWEB_FAVICON="/gitweb/git-favicon.png" \
bindir=/usr/local/bin \
- gitweb/gitweb.cgi
+ gitweb
- cp -fv ~/git/gitweb/gitweb.{cgi,css} \
+ cp -fv ~/git/gitweb/gitweb.{cgi,js,css} \
~/git/gitweb/git-{favicon,logo}.png \
/var/www/cgi-bin/gitweb/
# Define JSMIN to point to JavaScript minifier that functions as
# a filter to have gitweb.js minified.
#
+# Define CSSMIN to point to a CSS minifier in order to generate a minified
+# version of gitweb.css
+#
prefix ?= $(HOME)
bindir ?= $(prefix)/bin
RM ?= rm -f
-# JavaScript minifier invocation that can function as filter
-JSMIN ?=
-
# default configuration for gitweb
GITWEB_CONFIG = gitweb_config.perl
GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
GITWEB_CSS = gitweb.css
GITWEB_LOGO = git-logo.png
GITWEB_FAVICON = git-favicon.png
-ifdef JSMIN
-GITWEB_JS = gitweb.min.js
-else
GITWEB_JS = gitweb.js
-endif
GITWEB_SITE_HEADER =
GITWEB_SITE_FOOTER =
all:: gitweb.cgi
ifdef JSMIN
-FILES=gitweb.cgi gitweb.min.js
-gitweb.cgi: gitweb.perl gitweb.min.js
-else # !JSMIN
-FILES=gitweb.cgi
-gitweb.cgi: gitweb.perl
-endif # JSMIN
-
-gitweb.cgi:
+GITWEB_JS = gitweb.min.js
+all:: gitweb.min.js
+gitweb.min.js: gitweb.js GITWEB-BUILD-OPTIONS
+ $(QUIET_GEN)$(JSMIN) <$< >$@
+endif
+
+ifdef CSSMIN
+GITWEB_CSS = gitweb.min.css
+all:: gitweb.min.css
+gitweb.min.css: gitweb.css GITWEB-BUILD-OPTIONS
+ $(QUIET_GEN)$(CSSMIN) <$ >$@
+endif
+
+GITWEB_REPLACE = \
+ -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
+ -e 's|++GIT_BINDIR++|$(bindir)|g' \
+ -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
+ -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
+ -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
+ -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
+ -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
+ -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
+ -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
+ -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
+ -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
+ -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
+ -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
+ -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
+ -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
+ -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
+ -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
+ -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
+ -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g'
+
+GITWEB-BUILD-OPTIONS: FORCE
+ @rm -f $@+
+ @echo "x" '$(PERL_PATH_SQ)' $(GITWEB_REPLACE) "$(JSMIN)|$(CSSMIN)" >$@+
+ @cmp -s $@+ $@ && rm -f $@+ || mv -f $@+ $@
+
+gitweb.cgi: gitweb.perl GITWEB-BUILD-OPTIONS
$(QUIET_GEN)$(RM) $@ $@+ && \
sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
- -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
- -e 's|++GIT_BINDIR++|$(bindir)|g' \
- -e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
- -e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
- -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
- -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
- -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
- -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
- -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
- -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
- -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
- -e 's|++GITWEB_LIST++|$(GITWEB_LIST)|g' \
- -e 's|++GITWEB_HOMETEXT++|$(GITWEB_HOMETEXT)|g' \
- -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
- -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
- -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
- -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
- -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
- -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
- $< >$@+ && \
+ $(GITWEB_REPLACE) $< >$@+ && \
chmod +x $@+ && \
mv $@+ $@
-ifdef JSMIN
-gitweb.min.js: gitweb.js
- $(QUIET_GEN)$(JSMIN) <$< >$@
-endif # JSMIN
-
clean:
- $(RM) $(FILES)
+ $(RM) gitweb.cgi gitweb.min.js gitweb.min.css GITWEB-BUILD-OPTIONS
+
+.PHONY: all clean .FORCE-GIT-VERSION-FILE FORCE
-.PHONY: all clean .FORCE-GIT-VERSION-FILE
Points to the location where you put gitweb.css on your web server
(or to be more generic, the URI of gitweb stylesheet). Relative to the
base URI of gitweb. Note that you can setup multiple stylesheets from
- the gitweb config file. [Default: gitweb.css]
+ the gitweb config file. [Default: gitweb.css (or gitweb.min.css if the
+ CSSMIN variable is defined / CSS minifier is used)]
* GITWEB_LOGO
Points to the location where you put git-logo.png on your web server
(or to be more generic URI of logo, 72x27 size, displayed in top right
}
push @commit_spec, '--root', $hash;
}
- open $fd, "-|", git_cmd(), "format-patch", '--encoding=utf8',
- '--stdout', @commit_spec
+ open $fd, "-|", git_cmd(), "format-patch", @diff_opts,
+ '--encoding=utf8', '--stdout', @commit_spec
or die_error(500, "Open git-format-patch failed");
} else {
die_error(400, "Unknown commitdiff format");
if (opt->show_hunk_mark) {
output_color(opt, "--", 2, opt->color_sep);
opt->output(opt, "\n", 1);
- } else
- opt->show_hunk_mark = 1;
+ }
} else if (lno > opt->last_shown + 1) {
output_color(opt, "--", 2, opt->color_sep);
opt->output(opt, "\n", 1);
!opt->name_only)
return 0;
- /* If we are showing hunk marks, we should not do it for the
- * first match. The synchronization problem we get for this
- * constraint is not yet solved, so we disable threading in
- * this case.
- */
- if (opt->pre_context || opt->post_context)
- return 0;
-
return 1;
}
enum grep_context ctx = GREP_CONTEXT_HEAD;
xdemitconf_t xecfg;
- opt->last_shown = 0;
-
if (!opt->output)
opt->output = std_output;
+ if (opt->last_shown && (opt->pre_context || opt->post_context) &&
+ opt->output == std_output)
+ opt->show_hunk_mark = 1;
+ opt->last_shown = 0;
+
if (buffer_is_binary(buf, size)) {
switch (opt->binary) {
case GREP_BINARY_DEFAULT:
static NORETURN void die_webcgi(const char *err, va_list params)
{
- char buffer[1000];
+ static int dead;
- http_status(500, "Internal Server Error");
- hdr_nocache();
- end_headers();
+ if (!dead) {
+ char buffer[1000];
+ dead = 1;
- vsnprintf(buffer, sizeof(buffer), err, params);
- fprintf(stderr, "fatal: %s\n", buffer);
- exit(0);
+ vsnprintf(buffer, sizeof(buffer), err, params);
+ fprintf(stderr, "fatal: %s\n", buffer);
+ http_status(500, "Internal Server Error");
+ hdr_nocache();
+ end_headers();
+ }
+ exit(0); /* we successfully reported a failure ;-) */
}
static char* getdir(void)
return 'A' + v - 10;
}
-static void end_url_with_slash(struct strbuf *buf, const char *url)
+void end_url_with_slash(struct strbuf *buf, const char *url)
{
strbuf_addstr(buf, url);
if (buf->len && buf->buf[buf->len - 1] != '/')
ret = HTTP_OK;
else if (missing_target(&results))
ret = HTTP_MISSING_TARGET;
- else
+ else if (results.http_code == 401) {
+ if (user_name) {
+ ret = HTTP_NOAUTH;
+ } else {
+ /*
+ * git_getpass is needed here because its very likely stdin/stdout are
+ * pipes to our parent process. So we instead need to use /dev/tty,
+ * but that is non-portable. Using git_getpass() can at least be stubbed
+ * on other platforms with a different implementation if/when necessary.
+ */
+ user_name = xstrdup(git_getpass("Username: "));
+ init_curl_http_auth(slot->curl);
+ ret = HTTP_REAUTH;
+ }
+ } else
ret = HTTP_ERROR;
} else {
error("Unable to start HTTP request for %s", url);
int http_get_strbuf(const char *url, struct strbuf *result, int options)
{
- return http_request(url, result, HTTP_REQUEST_STRBUF, options);
+ int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
+ if (http_ret == HTTP_REAUTH) {
+ http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
+ }
+ return http_ret;
}
/*
int only_two_digit_prefix);
extern char *get_remote_object_url(const char *url, const char *hex,
int only_two_digit_prefix);
+extern void end_url_with_slash(struct strbuf *buf, const char *url);
/* Options for http_request_*() */
#define HTTP_NO_CACHE 1
#define HTTP_MISSING_TARGET 1
#define HTTP_ERROR 2
#define HTTP_START_FAILED 3
+#define HTTP_REAUTH 4
+#define HTTP_NOAUTH 5
/*
* Requests an url and stores the result in a strbuf.
while (1) {
if (!prefixcmp(p, "From ")) {
+ p = strstr(p+5, "\nFrom: ");
+ if (!p) break;
+ p = strstr(p+7, "\nDate: ");
+ if (!p) break;
+ p = strstr(p+7, "\nSubject: ");
+ if (!p) break;
+ p += 10;
count++;
- p += 5;
}
p = strstr(p+5, "\nFrom ");
if (!p)
typedef int (*ll_merge_fn)(const struct ll_merge_driver *,
mmbuffer_t *result,
const char *path,
- mmfile_t *orig,
+ mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
int flag,
static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
mmbuffer_t *result,
const char *path_unused,
- mmfile_t *orig,
+ mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
int flag, int marker_size)
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
mmbuffer_t *result,
const char *path,
- mmfile_t *orig,
+ mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
int flag, int marker_size)
path, name1, name2);
return ll_binary_merge(drv_unused, result,
path,
- orig, src1, name1,
+ orig, orig_name,
+ src1, name1,
src2, name2,
flag, marker_size);
}
xmp.style = git_xmerge_style;
if (marker_size > 0)
xmp.marker_size = marker_size;
- return xdl_merge(orig, src1, name1, src2, name2, &xmp, result);
+ xmp.ancestor = orig_name;
+ xmp.file1 = name1;
+ xmp.file2 = name2;
+ return xdl_merge(orig, src1, src2, &xmp, result);
}
static int ll_union_merge(const struct ll_merge_driver *drv_unused,
mmbuffer_t *result,
const char *path_unused,
- mmfile_t *orig,
+ mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
int flag, int marker_size)
/* Use union favor */
flag = (flag & 1) | (XDL_MERGE_FAVOR_UNION << 1);
return ll_xdl_merge(drv_unused, result, path_unused,
- orig, src1, NULL, src2, NULL,
+ orig, NULL, src1, NULL, src2, NULL,
flag, marker_size);
return 0;
}
static int ll_ext_merge(const struct ll_merge_driver *fn,
mmbuffer_t *result,
const char *path,
- mmfile_t *orig,
+ mmfile_t *orig, const char *orig_name,
mmfile_t *src1, const char *name1,
mmfile_t *src2, const char *name2,
int flag, int marker_size)
int ll_merge(mmbuffer_t *result_buf,
const char *path,
- mmfile_t *ancestor,
+ mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
int flag)
driver = find_ll_merge_driver(ll_driver_name);
if (virtual_ancestor && driver->recursive)
driver = find_ll_merge_driver(driver->recursive);
- return driver->fn(driver, result_buf, path, ancestor,
+ return driver->fn(driver, result_buf, path, ancestor, ancestor_label,
ours, our_label, theirs, their_label,
flag, marker_size);
}
int ll_merge(mmbuffer_t *result_buf,
const char *path,
- mmfile_t *ancestor,
+ mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
int flag);
int merge_status;
mmbuffer_t res;
- merge_status = ll_merge(&res, path, base,
+ /*
+ * This function is only used by cmd_merge_tree, which
+ * does not respect the merge.conflictstyle option.
+ * There is no need to worry about a label for the
+ * common ancestor.
+ */
+ merge_status = ll_merge(&res, path, base, NULL,
our, ".our", their, ".their", 0);
if (merge_status < 0)
return NULL;
return -1;
}
if (update_working_directory) {
- if (remove_path(path) && errno != ENOENT)
+ if (remove_path(path))
return -1;
}
return 0;
const char *branch2)
{
mmfile_t orig, src1, src2;
- char *name1, *name2;
+ char *base_name, *name1, *name2;
int merge_status;
int favor;
}
}
- if (strcmp(a->path, b->path)) {
+ if (strcmp(a->path, b->path) ||
+ (o->ancestor != NULL && strcmp(a->path, one->path) != 0)) {
+ base_name = o->ancestor == NULL ? NULL :
+ xstrdup(mkpath("%s:%s", o->ancestor, one->path));
name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
} else {
+ base_name = o->ancestor == NULL ? NULL :
+ xstrdup(mkpath("%s", o->ancestor));
name1 = xstrdup(mkpath("%s", branch1));
name2 = xstrdup(mkpath("%s", branch2));
}
read_mmblob(&src1, a->sha1);
read_mmblob(&src2, b->sha1);
- merge_status = ll_merge(result_buf, a->path, &orig,
+ merge_status = ll_merge(result_buf, a->path, &orig, base_name,
&src1, name1, &src2, name2,
(!!o->call_depth) | (favor << 1));
if (!o->call_depth)
read_cache();
+ o->ancestor = "merged common ancestors";
clean = merge_trees(o, h1->tree, h2->tree, merged_common_ancestors->tree,
&mrtree);
#include "string-list.h"
struct merge_options {
+ const char *ancestor;
const char *branch1;
const char *branch2;
enum {
void init_merge_options(struct merge_options *o);
struct tree *write_tree_from_memory(struct merge_options *o);
+/* builtin/merge.c */
+int try_merge_command(const char *strategy, struct commit_list *common, const char *head_arg, struct commit_list *remotes);
+
#endif
--- /dev/null
+#include "cache.h"
+#include "notes-cache.h"
+#include "commit.h"
+#include "refs.h"
+
+static int notes_cache_match_validity(const char *ref, const char *validity)
+{
+ unsigned char sha1[20];
+ struct commit *commit;
+ struct pretty_print_context pretty_ctx;
+ struct strbuf msg = STRBUF_INIT;
+ int ret;
+
+ if (read_ref(ref, sha1) < 0)
+ return 0;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+ if (!commit)
+ return 0;
+
+ memset(&pretty_ctx, 0, sizeof(pretty_ctx));
+ format_commit_message(commit, "%s", &msg, &pretty_ctx);
+ strbuf_trim(&msg);
+
+ ret = !strcmp(msg.buf, validity);
+ strbuf_release(&msg);
+
+ return ret;
+}
+
+void notes_cache_init(struct notes_cache *c, const char *name,
+ const char *validity)
+{
+ struct strbuf ref = STRBUF_INIT;
+ int flags = 0;
+
+ memset(c, 0, sizeof(*c));
+ c->validity = xstrdup(validity);
+
+ strbuf_addf(&ref, "refs/notes/%s", name);
+ if (!notes_cache_match_validity(ref.buf, validity))
+ flags = NOTES_INIT_EMPTY;
+ init_notes(&c->tree, ref.buf, combine_notes_overwrite, flags);
+ strbuf_release(&ref);
+}
+
+int notes_cache_write(struct notes_cache *c)
+{
+ unsigned char tree_sha1[20];
+ unsigned char commit_sha1[20];
+
+ if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref)
+ return -1;
+ if (!c->tree.dirty)
+ return 0;
+
+ if (write_notes_tree(&c->tree, tree_sha1))
+ return -1;
+ if (commit_tree(c->validity, tree_sha1, NULL, commit_sha1, NULL) < 0)
+ return -1;
+ if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
+ 0, QUIET_ON_ERR) < 0)
+ return -1;
+
+ return 0;
+}
+
+char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20],
+ size_t *outsize)
+{
+ const unsigned char *value_sha1;
+ enum object_type type;
+ char *value;
+ unsigned long size;
+
+ value_sha1 = get_note(&c->tree, key_sha1);
+ if (!value_sha1)
+ return NULL;
+ value = read_sha1_file(value_sha1, &type, &size);
+
+ *outsize = size;
+ return value;
+}
+
+int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20],
+ const char *data, size_t size)
+{
+ unsigned char value_sha1[20];
+
+ if (write_sha1_file(data, size, "blob", value_sha1) < 0)
+ return -1;
+ add_note(&c->tree, key_sha1, value_sha1, NULL);
+ return 0;
+}
--- /dev/null
+#ifndef NOTES_CACHE_H
+#define NOTES_CACHE_H
+
+#include "notes.h"
+
+struct notes_cache {
+ struct notes_tree tree;
+ char *validity;
+};
+
+void notes_cache_init(struct notes_cache *c, const char *name,
+ const char *validity);
+int notes_cache_write(struct notes_cache *c);
+
+char *notes_cache_get(struct notes_cache *c, unsigned char sha1[20], size_t
+ *outsize);
+int notes_cache_put(struct notes_cache *c, unsigned char sha1[20],
+ const char *data, size_t size);
+
+#endif /* NOTES_CACHE_H */
struct string_list;
struct display_notes_opt {
- int suppress_default_notes:1;
+ unsigned int suppress_default_notes:1;
struct string_list *extra_notes_refs;
};
}
return 0; /* unknown %g placeholder */
case 'N':
- format_display_notes(commit->object.sha1, sb,
- git_log_output_encoding ? git_log_output_encoding
- : git_commit_encoding, 0);
- return 1;
+ if (c->pretty_ctx->show_notes) {
+ format_display_notes(commit->object.sha1, sb,
+ git_log_output_encoding ? git_log_output_encoding
+ : git_commit_encoding, 0);
+ return 1;
+ }
+ return 0;
}
/* For the rest we have to parse the commit header. */
case 'e': /* encoding */
strbuf_add(sb, msg + c->encoding.off, c->encoding.len);
return 1;
+ case 'B': /* raw body */
+ /* message_off is always left at the initial newline */
+ strbuf_addstr(sb, msg + c->message_off + 1);
+ return 1;
}
/* Now we need to parse the commit message. */
return consumed + 1;
}
+static size_t userformat_want_item(struct strbuf *sb, const char *placeholder,
+ void *context)
+{
+ struct userformat_want *w = context;
+
+ if (*placeholder == '+' || *placeholder == '-')
+ placeholder++;
+
+ switch (*placeholder) {
+ case 'N':
+ w->notes = 1;
+ break;
+ }
+ return 0;
+}
+
+void userformat_find_requirements(const char *fmt, struct userformat_want *w)
+{
+ struct strbuf dummy = STRBUF_INIT;
+
+ if (!fmt) {
+ if (!user_format)
+ return;
+ fmt = user_format;
+ }
+ strbuf_expand(&dummy, user_format, userformat_want_item, w);
+ strbuf_release(&dummy);
+}
+
void format_commit_message(const struct commit *commit,
const char *format, struct strbuf *sb,
const struct pretty_print_context *pretty_ctx)
if (log_all_ref_updates &&
(!prefixcmp(ref_name, "refs/heads/") ||
!prefixcmp(ref_name, "refs/remotes/") ||
+ !prefixcmp(ref_name, "refs/notes/") ||
!strcmp(ref_name, "HEAD"))) {
if (safe_create_leading_directories(log_file) < 0)
return error("unable to create directory for %s",
#include "sideband.h"
static struct remote *remote;
-static const char *url;
+static const char *url; /* always ends with a trailing slash */
struct options {
int verbosity;
return last;
free_discovery(last);
- strbuf_addf(&buffer, "%s/info/refs", url);
+ strbuf_addf(&buffer, "%sinfo/refs", url);
if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
is_http = 1;
if (!strchr(url, '?'))
strbuf_reset(&buffer);
proto_git_candidate = 0;
- strbuf_addf(&buffer, "%s/info/refs", url);
+ strbuf_addf(&buffer, "%sinfo/refs", url);
refs_url = strbuf_detach(&buffer, NULL);
http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
case HTTP_MISSING_TARGET:
die("%s not found: did you run git update-server-info on the"
" server?", refs_url);
+ case HTTP_NOAUTH:
+ die("Authentication failed");
default:
http_error(refs_url, http_ret);
die("HTTP request failed");
rpc->out = client.out;
strbuf_init(&rpc->result, 0);
- strbuf_addf(&buf, "%s/%s", url, svc);
+ strbuf_addf(&buf, "%s%s", url, svc);
rpc->service_url = strbuf_detach(&buf, NULL);
strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
remote = remote_get(argv[1]);
if (argc > 2) {
- url = argv[2];
+ end_url_with_slash(&buf, argv[2]);
} else {
- url = remote->url[0];
+ end_url_with_slash(&buf, remote->url[0]);
}
+ url = strbuf_detach(&buf, NULL);
+
http_init(remote);
do {
if (!mmfile[i].ptr && !mmfile[i].size)
mmfile[i].ptr = xstrdup("");
}
- ll_merge(&result, path, &mmfile[0],
+ ll_merge(&result, path, &mmfile[0], NULL,
&mmfile[1], "ours",
&mmfile[2], "theirs", 0);
for (i = 0; i < 3; i++)
ret = 1;
goto out;
}
- ret = ll_merge(&result, path, &base, &cur, "", &other, "", 0);
+ ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", 0);
if (!ret) {
FILE *f = fopen(path, "w");
if (!f)
else if (cmd->out > 1)
fhout = dup(cmd->out);
- if (cmd->dir)
- die("chdir in start_command() not implemented");
if (cmd->env)
env = make_augmented_environ(cmd->env);
cmd->argv = prepare_shell_cmd(cmd->argv);
}
- cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env,
+ cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env, cmd->dir,
fhin, fhout, fherr);
failed_errno = errno;
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
close(cmd->out);
if (need_err)
close_pair(fderr);
+ else if (cmd->err)
+ close(cmd->err);
errno = failed_errno;
return -1;
}
const char *gitdirenv;
const char *gitfile_dir;
int len, offset, ceil_offset, root_len;
+ int current_device = 0, one_filesystem = 1;
+ struct stat buf;
/*
* Let's assume that we are in a git repository.
* etc.
*/
offset = len = strlen(cwd);
+ one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
+ if (one_filesystem) {
+ if (stat(".", &buf))
+ die_errno("failed to stat '.'");
+ current_device = buf.st_dev;
+ }
for (;;) {
gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
if (gitfile_dir) {
}
die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
}
- if (chdir(".."))
+ if (one_filesystem) {
+ if (stat("..", &buf)) {
+ cwd[offset] = '\0';
+ die_errno("failed to stat '%s/..'", cwd);
+ }
+ if (buf.st_dev != current_device) {
+ if (nongit_ok) {
+ if (chdir(cwd))
+ die_errno("Cannot come back to cwd");
+ *nongit_ok = 1;
+ return NULL;
+ }
+ cwd[offset] = '\0';
+ die("Not a git repository (or any parent up to mount parent %s)\n"
+ "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
+ }
+ }
+ if (chdir("..")) {
+ cwd[offset] = '\0';
die_errno("Cannot change to '%s/..'", cwd);
+ }
}
inside_git_dir = 0;
}
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
- void *buf, unsigned long len, time_t mtime)
+ const void *buf, unsigned long len, time_t mtime)
{
int fd, ret;
unsigned char compressed[4096];
git_SHA1_Update(&c, hdr, hdrlen);
/* Then the data itself.. */
- stream.next_in = buf;
+ stream.next_in = (void *)buf;
stream.avail_in = len;
do {
unsigned char *in0 = stream.next_in;
return move_temp_to_file(tmpfile, filename);
}
-int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
unsigned char sha1[20];
char hdr[32];
else
ret = -1;
strbuf_release(&sbuf);
+ } else if (!size) {
+ ret = index_mem(sha1, NULL, size, write_object, type, path);
} else if (size <= SMALL_FILE_SIZE) {
char *buf = xmalloc(size);
if (size == read_in_full(fd, buf, size))
else
ret = error("short read %s", strerror(errno));
free(buf);
- } else if (size) {
+ } else {
void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
ret = index_mem(sha1, buf, size, write_object, type, path);
munmap(buf, size);
- } else
- ret = index_mem(sha1, NULL, size, write_object, type, path);
+ }
close(fd);
return ret;
}
return PH_ERROR_PROTOCOL;
return 0;
}
+
+void assert_sha1_type(const unsigned char *sha1, enum object_type expect)
+{
+ enum object_type type = sha1_object_info(sha1, NULL);
+ if (type < 0)
+ die("%s is not a valid object", sha1_to_hex(sha1));
+ if (type != expect)
+ die("%s is not a valid '%s' object", sha1_to_hex(sha1),
+ typename(expect));
+}
qsort(list->items, list->nr, sizeof(*list->items), cmp_items);
}
-int unsorted_string_list_has_string(struct string_list *list, const char *string)
+struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
+ const char *string)
{
int i;
for (i = 0; i < list->nr; i++)
if (!strcmp(string, list->items[i].string))
- return 1;
- return 0;
+ return list->items + i;
+ return NULL;
+}
+
+int unsorted_string_list_has_string(struct string_list *list,
+ const char *string)
+{
+ return unsorted_string_list_lookup(list, string) != NULL;
}
struct string_list_item *string_list_append(const char *string, struct string_list *list);
void sort_string_list(struct string_list *list);
int unsorted_string_list_has_string(struct string_list *list, const char *string);
-
+struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
+ const char *string);
#endif /* STRING_LIST_H */
struct strbuf objects_directory = STRBUF_INIT;
struct alternate_object_database *alt_odb;
int ret = 0;
+ const char *git_dir;
- strbuf_addf(&objects_directory, "%s/.git/objects/", path);
+ strbuf_addf(&objects_directory, "%s/.git", path);
+ git_dir = read_gitfile_gently(objects_directory.buf);
+ if (git_dir) {
+ strbuf_reset(&objects_directory);
+ strbuf_addstr(&objects_directory, git_dir);
+ }
+ strbuf_addstr(&objects_directory, "/objects/");
if (!is_directory(objects_directory.buf)) {
ret = -1;
goto done;
unsigned is_submodule_modified(const char *path, int ignore_untracked)
{
- int i;
ssize_t len;
struct child_process cp;
const char *argv[] = {
NULL,
NULL,
};
- const char *env[LOCAL_REPO_ENV_SIZE + 3];
struct strbuf buf = STRBUF_INIT;
unsigned dirty_submodule = 0;
const char *line, *next_line;
+ const char *git_dir;
- for (i = 0; i < LOCAL_REPO_ENV_SIZE; i++)
- env[i] = local_repo_env[i];
-
- strbuf_addf(&buf, "%s/.git/", path);
- if (!is_directory(buf.buf)) {
+ strbuf_addf(&buf, "%s/.git", path);
+ git_dir = read_gitfile_gently(buf.buf);
+ if (!git_dir)
+ git_dir = buf.buf;
+ if (!is_directory(git_dir)) {
strbuf_release(&buf);
/* The submodule is not checked out, so it is not modified */
return 0;
}
strbuf_reset(&buf);
- strbuf_addf(&buf, "GIT_WORK_TREE=%s", path);
- env[i++] = strbuf_detach(&buf, NULL);
- strbuf_addf(&buf, "GIT_DIR=%s/.git", path);
- env[i++] = strbuf_detach(&buf, NULL);
- env[i] = NULL;
-
if (ignore_untracked)
argv[2] = "-uno";
memset(&cp, 0, sizeof(cp));
cp.argv = argv;
- cp.env = env;
+ cp.env = local_repo_env;
cp.git_cmd = 1;
cp.no_stdin = 1;
cp.out = -1;
+ cp.dir = path;
if (start_command(&cp))
die("Could not run git status --porcelain");
if (finish_command(&cp))
die("git status --porcelain failed");
- for (i = LOCAL_REPO_ENV_SIZE; env[i]; i++)
- free((char *)env[i]);
strbuf_release(&buf);
return dirty_submodule;
}
exit 1
fi
+clean=no
+test_expect_success 'tests clean up after themselves' '
+ test_when_finished clean=yes
+'
+
+cleaner=no
+test_expect_code 1 'tests clean up even after a failure' '
+ test_when_finished cleaner=yes &&
+ (exit 1)
+'
+
+if test $clean$cleaner != yesyes
+then
+ say "bug in test framework: cleanup commands do not work reliably"
+ exit 1
+fi
+
+test_expect_code 2 'failure to clean up causes the test to fail' '
+ test_when_finished "(exit 2)"
+'
+
################################################################
# Basics of the basics
)
'
+test_expect_success 'init creates a new bare directory with global --bare' '
+ rm -rf newdir &&
+ git --bare init newdir &&
+ test -d newdir/refs
+'
+
+test_expect_success 'init prefers command line to GIT_DIR' '
+ rm -rf newdir &&
+ mkdir otherdir &&
+ GIT_DIR=otherdir git --bare init newdir &&
+ test -d newdir/refs &&
+ ! test -d otherdir/refs
+'
+
test_done
mkdir -p a/b/d a/c &&
(
+ echo "[attr]notest !test"
echo "f test=f"
echo "a/i test=a/i"
+ echo "onoff test -test"
+ echo "offon -test test"
+ echo "no notest"
) >.gitattributes &&
(
echo "g test=a/g" &&
(
echo "h test=a/b/h" &&
echo "d/* test=a/b/d/*"
+ echo "d/yes notest"
) >a/b/.gitattributes
'
attr_check b/g unspecified &&
attr_check a/b/h a/b/h &&
attr_check a/b/d/g "a/b/d/*"
+ attr_check onoff unset
+ attr_check offon set
+ attr_check no unspecified
+ attr_check a/b/d/no "a/b/d/*"
+ attr_check a/b/d/yes unspecified
'
b/g: test: unspecified
a/b/h: test: a/b/h
a/b/d/g: test: a/b/d/*
+onoff: test: unset
+offon: test: set
+no: test: unspecified
+a/b/d/no: test: a/b/d/*
+a/b/d/yes: test: unspecified
EOF
sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
test_cmp tree.missing actual
'
-test_expect_failure 'mktree reads ls-tree -r output (1)' '
- git mktree <all >actual &&
- test_cmp tree actual
+test_expect_success 'mktree refuses to read ls-tree -r output (1)' '
+ test_must_fail git mktree <all >actual
'
-test_expect_failure 'mktree reads ls-tree -r output (2)' '
- git mktree <all.withsub >actual &&
- test_cmp tree.withsub actual
+test_expect_success 'mktree refuses to read ls-tree -r output (2)' '
+ test_must_fail git mktree <all.withsub >actual
'
test_done
test_must_fail git merge master
"
+test_expect_success 'git -c "key=value" support' '
+ test "z$(git -c name=value config name)" = zvalue &&
+ test "z$(git -c core.name=value config core.name)" = zvalue &&
+ test "z$(git -c CamelCase=value config camelcase)" = zvalue &&
+ test "z$(git -c flag config --bool flag)" = ztrue &&
+ test_must_fail git -c core.name=value config name
+'
+
test_done
test_done
fi
-modebits () {
- ls -l "$1" | sed -e 's|^\(..........\).*|\1|'
-}
-
check_perms_and_acl () {
- actual=$(modebits "$1") &&
- case "$actual" in
- -r--r-----*)
- : happy
- ;;
- *)
- echo "Got permission '$actual', expected '-r--r-----'"
- false
- ;;
- esac &&
+ test -r "$1" &&
getfacl "$1" > actual &&
grep -q "user:root:rwx" actual &&
grep -q "user:${LOGNAME}:rwx" actual &&
- grep -q "mask::r--" actual &&
+ egrep "mask::?r--" actual > /dev/null 2>&1 &&
grep -q "group::---" actual || false
}
dirs_to_set="./ .git/ .git/objects/ .git/objects/pack/"
test_expect_success 'Setup test repo' '
+ setfacl -m d:u::rwx,d:g::---,d:o:---,d:m:rwx $dirs_to_set &&
+ setfacl -m m:rwx $dirs_to_set &&
setfacl -m u:root:rwx $dirs_to_set &&
- setfacl -d -m u:"$LOGNAME":rwx $dirs_to_set &&
- setfacl -d -m u:root:rwx $dirs_to_set &&
+ setfacl -m d:u:"$LOGNAME":rwx $dirs_to_set &&
+ setfacl -m d:u:root:rwx $dirs_to_set &&
touch file.txt &&
git add file.txt &&
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2010 Erick Mattos
+#
+
+test_description='git checkout --orphan
+
+Main Tests for --orphan functionality.'
+
+. ./test-lib.sh
+
+TEST_FILE=foo
+
+test_expect_success 'Setup' '
+ echo "Initial" >"$TEST_FILE" &&
+ git add "$TEST_FILE" &&
+ git commit -m "First Commit"
+ test_tick &&
+ echo "State 1" >>"$TEST_FILE" &&
+ git add "$TEST_FILE" &&
+ test_tick &&
+ git commit -m "Second Commit"
+'
+
+test_expect_success '--orphan creates a new orphan branch from HEAD' '
+ git checkout --orphan alpha &&
+ test_must_fail git rev-parse --verify HEAD &&
+ test "refs/heads/alpha" = "$(git symbolic-ref HEAD)" &&
+ test_tick &&
+ git commit -m "Third Commit" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git diff-tree --quiet master alpha
+'
+
+test_expect_success '--orphan creates a new orphan branch from <start_point>' '
+ git checkout master &&
+ git checkout --orphan beta master^ &&
+ test_must_fail git rev-parse --verify HEAD &&
+ test "refs/heads/beta" = "$(git symbolic-ref HEAD)" &&
+ test_tick &&
+ git commit -m "Fourth Commit" &&
+ test_must_fail git rev-parse --verify HEAD^ &&
+ git diff-tree --quiet master^ beta
+'
+
+test_expect_success '--orphan must be rejected with -b' '
+ git checkout master &&
+ test_must_fail git checkout --orphan new -b newer &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan is rejected with an existing name' '
+ git checkout master &&
+ test_must_fail git checkout --orphan master &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan refuses to switch if a merge is needed' '
+ git checkout master &&
+ git reset --hard &&
+ echo local >>"$TEST_FILE" &&
+ cat "$TEST_FILE" >"$TEST_FILE.saved" &&
+ test_must_fail git checkout --orphan gamma master^ &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)" &&
+ test_cmp "$TEST_FILE" "$TEST_FILE.saved" &&
+ git diff-index --quiet --cached HEAD &&
+ git reset --hard
+'
+
+test_expect_success '--orphan does not mix well with -t' '
+ git checkout master &&
+ test_must_fail git checkout -t master --orphan gamma &&
+ test refs/heads/master = "$(git symbolic-ref HEAD)"
+'
+
+test_expect_success '--orphan ignores branch.autosetupmerge' '
+ git checkout -f master &&
+ git config branch.autosetupmerge always &&
+ git checkout --orphan delta &&
+ test -z "$(git config branch.delta.merge)" &&
+ test refs/heads/delta = "$(git symbolic-ref HEAD)" &&
+ test_must_fail git rev-parse --verify HEAD^
+'
+
+test_expect_success '--orphan does not mix well with -l' '
+ git checkout -f master &&
+ test_must_fail git checkout -l --orphan gamma
+'
+
+test_done
git notes show ; test 1 = $?
'
+test_expect_success 'show non-existent notes entry with %N' '
+ for l in A B
+ do
+ echo "$l"
+ done >expect &&
+ git show -s --format='A%n%NB' >output &&
+ test_cmp expect output
+'
+
test_expect_success 'create notes' '
git config core.notesRef refs/notes/commits &&
MSG=b4 git notes add &&
test_must_fail git notes show HEAD^
'
+test_expect_success 'show notes entry with %N' '
+ for l in A b4 B
+ do
+ echo "$l"
+ done >expect &&
+ git show -s --format='A%n%NB' >output &&
+ test_cmp expect output
+'
+
+cat >expect <<EOF
+d423f8c refs/notes/commits@{0}: notes: Notes added by 'git notes add'
+EOF
+
+test_expect_success 'create reflog entry' '
+ git reflog show refs/notes/commits >output &&
+ test_cmp expect output
+'
+
test_expect_success 'edit existing notes' '
MSG=b3 git notes edit &&
test ! -f .git/NOTES_EDITMSG &&
# | \
# | F - G - H (branch1)
# | \
-# \ I (branch2)
-# \
-# J - K - L - M (no-conflict-branch)
+# |\ I (branch2)
+# | \
+# | J - K - L - M (no-conflict-branch)
+# \
+# N - O - P (no-ff-branch)
#
# where A, B, D and G all touch file1, and one, two, three, four all
# touch file "conflict".
+#
+# WARNING: Modifications to the initial repository can change the SHA ID used
+# in the expect2 file for the 'stop on conflicting pick' test.
+
test_expect_success 'setup' '
test_commit A file1 &&
done &&
git checkout -b no-conflict-branch A &&
for n in J K L M
+ do
+ test_commit $n file$n
+ done &&
+ git checkout -b no-ff-branch A &&
+ for n in N O P
do
test_commit $n file$n
done
D
=======
G
->>>>>>> 51047de... G
+>>>>>>> 5d18e54... G
EOF
test_expect_success 'stop on conflicting pick' '
test_cmp expect output
'
+test_expect_success 'rebase while detaching HEAD' '
+ git symbolic-ref HEAD &&
+ grandparent=$(git rev-parse HEAD~2) &&
+ test_tick &&
+ FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 &&
+ test $grandparent = $(git rev-parse HEAD~2) &&
+ test_must_fail git symbolic-ref HEAD
+'
+
+test_tick # Ensure that the rebased commits get a different timestamp.
+test_expect_success 'always cherry-pick with --no-ff' '
+ git checkout no-ff-branch &&
+ git tag original-no-ff-branch &&
+ git rebase -i --no-ff A &&
+ touch empty &&
+ for p in 0 1 2
+ do
+ test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&
+ git diff HEAD~$p original-no-ff-branch~$p > out &&
+ test_cmp empty out
+ done &&
+ test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&
+ git diff HEAD~3 original-no-ff-branch~3 > out &&
+ test_cmp empty out
+'
+
test_done
'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
'echo First > A &&
git update-index --add A &&
+ test_tick &&
git commit -m "Add A." &&
git checkout -b my-topic-branch &&
echo Second > B &&
git update-index --add B &&
+ test_tick &&
git commit -m "Add B." &&
- sleep 2 &&
echo AnotherSecond > C &&
git update-index --add C &&
+ test_tick &&
git commit -m "Add C." &&
git checkout -f master &&
echo Third >> A &&
git update-index A &&
+ test_tick &&
git commit -m "Modify A." &&
expr "$(echo $(git cherry master my-topic-branch) )" : "+ [^ ]* + .*"
--- /dev/null
+#!/bin/sh
+
+test_description='test cherry-pick and revert with conflicts
+
+ -
+ + picked: rewrites foo to c
+ + base: rewrites foo to b
+ + initial: writes foo as a, unrelated as unrelated
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ echo unrelated >unrelated &&
+ git add unrelated &&
+ test_commit initial foo a &&
+ test_commit base foo b &&
+ test_commit picked foo c &&
+ git config advice.detachedhead false
+
+'
+
+test_expect_success 'failed cherry-pick does not advance HEAD' '
+
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ head=$(git rev-parse HEAD) &&
+ test_must_fail git cherry-pick picked &&
+ newhead=$(git rev-parse HEAD) &&
+
+ test "$head" = "$newhead"
+'
+
+test_expect_success 'failed cherry-pick produces dirty index' '
+
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+
+ test_must_fail git update-index --refresh -q &&
+ test_must_fail git diff-index --exit-code HEAD
+'
+
+test_expect_success 'failed cherry-pick registers participants in index' '
+
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ {
+ git checkout base -- foo &&
+ git ls-files --stage foo &&
+ git checkout initial -- foo &&
+ git ls-files --stage foo &&
+ git checkout picked -- foo &&
+ git ls-files --stage foo
+ } > stages &&
+ sed "
+ 1 s/ 0 / 1 /
+ 2 s/ 0 / 2 /
+ 3 s/ 0 / 3 /
+ " < stages > expected &&
+ git checkout -f initial^0 &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+ git ls-files --stage --unmerged > actual &&
+
+ test_cmp expected actual
+'
+
+test_expect_success 'failed cherry-pick describes conflict in work tree' '
+
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ =======
+ c
+ >>>>>>> objid picked
+ EOF
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'diff3 -m style' '
+
+ git config merge.conflictstyle diff3 &&
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ ||||||| parent of objid picked
+ b
+ =======
+ c
+ >>>>>>> objid picked
+ EOF
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git cherry-pick picked &&
+
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'revert also handles conflicts sanely' '
+
+ git config --unset merge.conflictstyle &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ =======
+ b
+ >>>>>>> parent of objid picked
+ EOF
+ {
+ git checkout picked -- foo &&
+ git ls-files --stage foo &&
+ git checkout initial -- foo &&
+ git ls-files --stage foo &&
+ git checkout base -- foo &&
+ git ls-files --stage foo
+ } > stages &&
+ sed "
+ 1 s/ 0 / 1 /
+ 2 s/ 0 / 2 /
+ 3 s/ 0 / 3 /
+ " < stages > expected-stages &&
+ git checkout -f initial^0 &&
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ head=$(git rev-parse HEAD) &&
+ test_must_fail git revert picked &&
+ newhead=$(git rev-parse HEAD) &&
+ git ls-files --stage --unmerged > actual-stages &&
+
+ test "$head" = "$newhead" &&
+ test_must_fail git update-index --refresh -q &&
+ test_must_fail git diff-index --exit-code HEAD &&
+ test_cmp expected-stages actual-stages &&
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'revert conflict, diff3 -m style' '
+ git config merge.conflictstyle diff3 &&
+ git checkout -f initial^0 &&
+ git read-tree -u --reset HEAD &&
+ git clean -d -f -f -q -x &&
+ cat <<-EOF > expected &&
+ <<<<<<< HEAD
+ a
+ ||||||| objid picked
+ c
+ =======
+ b
+ >>>>>>> parent of objid picked
+ EOF
+
+ git update-index --refresh &&
+ git diff-index --exit-code HEAD &&
+
+ test_must_fail git revert picked &&
+
+ sed "s/[a-f0-9]*\.\.\./objid/" foo > actual &&
+ test_cmp expected actual
+'
+
+test_done
test_expect_success \
'diff removed symlink' \
- 'rm frotz &&
+ 'mv frotz frotz2 &&
git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
test_expect_success \
'diff identical, but newly created symlink' \
- 'sleep 3 &&
- ln -s xyzzy frotz &&
+ 'ln -s xyzzy frotz &&
git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
'
+test_expect_success 'check tabs as indentation (tab-in-indent: off)' '
+
+ git config core.whitespace "-tab-in-indent" &&
+ echo " foo ();" > x &&
+ git diff --check
+
+'
+
+test_expect_success 'check tabs as indentation (tab-in-indent: on)' '
+
+ git config core.whitespace "tab-in-indent" &&
+ echo " foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tabs and spaces as indentation (tab-in-indent: on)' '
+
+ git config core.whitespace "tab-in-indent" &&
+ echo " foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent and indent-with-non-tab conflict' '
+
+ git config core.whitespace "tab-in-indent,indent-with-non-tab" &&
+ echo "foo ();" > x &&
+ test_must_fail git diff --check
+
+'
+
+test_expect_success 'check tab-in-indent excluded from wildcard whitespace attribute' '
+
+ git config --unset core.whitespace &&
+ echo "x whitespace" > .gitattributes &&
+ echo " foo ();" > x &&
+ git diff --check &&
+ rm -f .gitattributes
+
+'
+
test_expect_success 'line numbers in --check output are correct' '
echo "" > x &&
'
-
test_expect_success 'check should test not just the last line' '
echo "" >>a &&
git --no-pager diff --check
git reset --hard
'
+test_expect_success 'check honors conflict marker length' '
+ git reset --hard &&
+ echo ">>>>>>> boo" >>b &&
+ echo "======" >>a &&
+ git diff --check a &&
+ (
+ git diff --check b
+ test $? = 2
+ ) &&
+ git reset --hard &&
+ echo ">>>>>>>> boo" >>b &&
+ echo "========" >>a &&
+ git diff --check &&
+ echo "b conflict-marker-size=8" >.gitattributes &&
+ (
+ git diff --check b
+ test $? = 2
+ ) &&
+ git diff --check a &&
+ git reset --hard
+'
+
test_done
verify_helper sidesansone
'
+test_expect_success 'diagnose truncated file' '
+ >file &&
+ git add file &&
+ git commit --amend -C HEAD &&
+ git show >out &&
+ grep "diff --cc file" out
+'
+
test_done
EOF
"
+test_expect_success 'setup .git file for sm2' '
+ (cd sm2 &&
+ REAL="$(pwd)/../.real" &&
+ mv .git "$REAL"
+ echo "gitdir: $REAL" >.git)
+'
+
+test_expect_success 'diff --submodule with .git file' '
+ git diff --submodule HEAD^ >actual &&
+ diff actual - <<-EOF
+Submodule sm1 $head6...0000000 (submodule deleted)
+Submodule sm2 0000000...$head7 (new submodule)
+EOF
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='test textconv caching'
+. ./test-lib.sh
+
+cat >helper <<'EOF'
+#!/bin/sh
+sed 's/^/converted: /' "$@" >helper.out
+cat helper.out
+EOF
+chmod +x helper
+
+test_expect_success 'setup' '
+ echo foo content 1 >foo.bin &&
+ echo bar content 1 >bar.bin &&
+ git add . &&
+ git commit -m one &&
+ echo foo content 2 >foo.bin &&
+ echo bar content 2 >bar.bin &&
+ git commit -a -m two &&
+ echo "*.bin diff=magic" >.gitattributes &&
+ git config diff.magic.textconv ./helper &&
+ git config diff.magic.cachetextconv true
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1 +1 @@
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1 +1 @@
+-converted: foo content 1
++converted: foo content 2
+EOF
+
+test_expect_success 'first textconv works' '
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cached textconv produces same output' '
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'cached textconv does not run helper' '
+ rm -f helper.out &&
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual &&
+ ! test -r helper.out
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: foo content 1
++converted: foo content 2
+EOF
+test_expect_success 'changing textconv invalidates cache' '
+ echo other >other &&
+ git config diff.magic.textconv "./helper other" &&
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+diff --git a/bar.bin b/bar.bin
+index fcf9166..28283d5 100644
+--- a/bar.bin
++++ b/bar.bin
+@@ -1,2 +1,2 @@
+ converted: other
+-converted: bar content 1
++converted: bar content 2
+diff --git a/foo.bin b/foo.bin
+index d5b9fe3..1345db2 100644
+--- a/foo.bin
++++ b/foo.bin
+@@ -1 +1 @@
+-converted: foo content 1
++converted: foo content 2
+EOF
+test_expect_success 'switching diff driver produces correct results' '
+ git config diff.moremagic.textconv ./helper &&
+ echo foo.bin diff=moremagic >>.gitattributes &&
+ git diff HEAD^ HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_done
# ! trailing-space
# @ space-before-tab
# # indent-with-non-tab
+ # % tab-in-indent
sed -e "s/_/ /g" -e "s/>/ /" <<-\EOF
An_SP in an ordinary line>and a HT.
- >A HT.
- _>A SP and a HT (@).
- _>_A SP, a HT and a SP (@).
+ >A HT (%).
+ _>A SP and a HT (@%).
+ _>_A SP, a HT and a SP (@%).
_______Seven SP.
________Eight SP (#).
- _______>Seven SP and a HT (@).
- ________>Eight SP and a HT (@#).
- _______>_Seven SP, a HT and a SP (@).
- ________>_Eight SP, a HT and a SP (@#).
+ _______>Seven SP and a HT (@%).
+ ________>Eight SP and a HT (@#%).
+ _______>_Seven SP, a HT and a SP (@%).
+ ________>_Eight SP, a HT and a SP (@#%).
_______________Fifteen SP (#).
- _______________>Fifteen SP and a HT (@#).
+ _______________>Fifteen SP and a HT (@#%).
________________Sixteen SP (#).
- ________________>Sixteen SP and a HT (@#).
+ ________________>Sixteen SP and a HT (@#%).
_____a__Five SP, a non WS, two SP.
A line with a (!) trailing SP_
A line with a (!) trailing HT>
}
test_fix () {
-
# fix should not barf
apply_patch --whitespace=fix || return 1
for i in - ''
do
case "$i" in '') ti='#' ;; *) ti= ;; esac
- rule=${t}trailing,${s}space,${i}indent
-
- rm -f .gitattributes
- test_expect_success "rule=$rule" '
- git config core.whitespace "$rule" &&
- test_fix "$tt$ts$ti"
- '
-
- test_expect_success "rule=$rule (attributes)" '
- git config --unset core.whitespace &&
- echo "target whitespace=$rule" >.gitattributes &&
- test_fix "$tt$ts$ti"
- '
-
+ for h in - ''
+ do
+ [ -z "$h$i" ] && continue
+ case "$h" in '') th='%' ;; *) th= ;; esac
+ rule=${t}trailing,${s}space,${i}indent,${h}tab
+
+ rm -f .gitattributes
+ test_expect_success "rule=$rule" '
+ git config core.whitespace "$rule" &&
+ test_fix "$tt$ts$ti$th"
+ '
+
+ test_expect_success "rule=$rule (attributes)" '
+ git config --unset core.whitespace &&
+ echo "target whitespace=$rule" >.gitattributes &&
+ test_fix "$tt$ts$ti$th"
+ '
+
+ done
done
done
done
test_cmp one expect
'
+test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
+ { echo a; echo; } >one &&
+ git add one &&
+ { echo b; echo a; echo; } >one &&
+ cp one expect &&
+ git diff -- one >patch &&
+ echo a >one &&
+ test_must_fail git apply patch &&
+ git apply --whitespace=fix patch &&
+ test_cmp one expect
+'
+
test_expect_success 'shrink file with tons of missing blanks at end of file' '
{ echo a; echo b; echo c; } >one &&
cp one no-blank-lines &&
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2010 Peter Collingbourne
+#
+
+test_description='git apply submodule tests'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ cat > create-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+new file mode 160000
+index 0000000..0123456
+--- /dev/null
++++ b/dir/sm
+@@ -0,0 +1 @@
++Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+ cat > remove-sm.patch <<EOF
+diff --git a/dir/sm b/dir/sm
+deleted file mode 160000
+index 0123456..0000000
+--- a/dir/sm
++++ /dev/null
+@@ -1 +0,0 @@
+-Subproject commit 0123456789abcdef0123456789abcdef01234567
+EOF
+'
+
+test_expect_success 'removing a submodule also removes all leading subdirectories' '
+ git apply --index create-sm.patch &&
+ test -d dir/sm &&
+ git apply --index remove-sm.patch &&
+ test \! -d dir
+'
+
+test_done
test_cmp expect actual
'
+test_expect_success 'log.decorate configuration' '
+ git config --unset-all log.decorate || :
+
+ git log --oneline >expect.none &&
+ git log --oneline --decorate >expect.short &&
+ git log --oneline --decorate=full >expect.full &&
+
+ echo "[log] decorate" >>.git/config &&
+ git log --oneline >actual &&
+ test_cmp expect.short actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate true &&
+ git log --oneline >actual &&
+ test_cmp expect.short actual &&
+ git log --oneline --decorate=full >actual &&
+ test_cmp expect.full actual &&
+ git log --oneline --decorate=no >actual &&
+ test_cmp expect.none actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate no &&
+ git log --oneline >actual &&
+ test_cmp expect.none actual &&
+ git log --oneline --decorate >actual &&
+ test_cmp expect.short actual &&
+ git log --oneline --decorate=full >actual &&
+ test_cmp expect.full actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate short &&
+ git log --oneline >actual &&
+ test_cmp expect.short actual &&
+ git log --oneline --no-decorate >actual &&
+ test_cmp expect.none actual &&
+ git log --oneline --decorate=full >actual &&
+ test_cmp expect.full actual &&
+
+ git config --unset-all log.decorate &&
+ git config log.decorate full &&
+ git log --oneline >actual &&
+ test_cmp expect.full actual &&
+ git log --oneline --no-decorate >actual &&
+ test_cmp expect.none actual &&
+ git log --oneline --decorate >actual &&
+ test_cmp expect.short actual
+
+'
+
test_done
test_expect_success 'remove remote protects non-remote branches' '
(
cd test &&
- (cat >expect1 <<EOF
+ { cat >expect1 <<EOF
Note: A non-remote branch was not removed; to delete it, use:
git branch -d master
EOF
- cat >expect2 <<EOF
+ } &&
+ { cat >expect2 <<EOF
Note: Non-remote branches were not removed; to delete them, use:
git branch -d foobranch
git branch -d master
EOF
-) &&
- git tag footag
+ } &&
+ git tag footag &&
git config --add remote.oops.fetch "+refs/*:refs/*" &&
git remote rm oops 2>actual1 &&
git branch foobranch &&
'
test_expect_success 'new remote' '
-(
git remote add someremote foo &&
echo foo >expect &&
git config --get-all remote.someremote.url >actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url bar' '
-(
git remote set-url someremote bar &&
echo bar >expect &&
git config --get-all remote.someremote.url >actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url baz bar' '
-(
git remote set-url someremote baz bar &&
echo baz >expect &&
git config --get-all remote.someremote.url >actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url zot bar' '
-(
test_must_fail git remote set-url someremote zot bar &&
echo baz >expect &&
git config --get-all remote.someremote.url >actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push zot baz' '
-(
test_must_fail git remote set-url --push someremote zot baz &&
echo "YYY" >expect &&
echo baz >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push zot' '
-(
git remote set-url --push someremote zot &&
echo zot >expect &&
echo "YYY" >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push qux zot' '
-(
git remote set-url --push someremote qux zot &&
echo qux >expect &&
echo "YYY" >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push foo qu+x' '
-(
git remote set-url --push someremote foo qu+x &&
echo foo >expect &&
echo "YYY" >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push --add aaa' '
-(
git remote set-url --push --add someremote aaa &&
echo foo >expect &&
echo aaa >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push bar aaa' '
-(
git remote set-url --push someremote bar aaa &&
echo foo >expect &&
echo bar >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push --delete bar' '
-(
git remote set-url --push --delete someremote bar &&
echo foo >expect &&
echo "YYY" >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --push --delete foo' '
-(
git remote set-url --push --delete someremote foo &&
echo "YYY" >expect &&
echo baz >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --add bbb' '
-(
git remote set-url --add someremote bbb &&
echo "YYY" >expect &&
echo baz >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --delete .*' '
-(
- test_must_fail git remote set-url --delete someremote .* &&
+ test_must_fail git remote set-url --delete someremote .\* &&
echo "YYY" >expect &&
echo baz >>expect &&
echo bbb >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --delete bbb' '
-(
git remote set-url --delete someremote bbb &&
echo "YYY" >expect &&
echo baz >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --delete baz' '
-(
test_must_fail git remote set-url --delete someremote baz &&
echo "YYY" >expect &&
echo baz >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --add ccc' '
-(
git remote set-url --add someremote ccc &&
echo "YYY" >expect &&
echo baz >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_expect_success 'remote set-url --delete baz' '
-(
git remote set-url --delete someremote baz &&
echo "YYY" >expect &&
echo ccc >>expect &&
echo "YYY" >>actual &&
git config --get-all remote.someremote.url >>actual &&
cmp expect actual
-)
'
test_done
'
+test_expect_success 'dies when no remote specified and no default remotes found' '
+
+ test_must_fail git ls-remote
+
+'
+
+test_expect_success 'use "origin" when no remote specified' '
+
+ git remote add origin "$(pwd)/.git" &&
+ git ls-remote >actual &&
+ test_cmp expected.all actual
+
+'
+
+test_expect_success 'use branch.<name>.remote if possible' '
+
+ #
+ # Test that we are indeed using branch.<name>.remote, not "origin", even
+ # though the "origin" remote has been set.
+ #
+
+ # setup a new remote to differentiate from "origin"
+ git clone . other.git &&
+ (
+ cd other.git &&
+ echo "$(git rev-parse HEAD) HEAD"
+ git show-ref | sed -e "s/ / /"
+ ) >exp &&
+
+ git remote add other other.git &&
+ git config branch.master.remote other &&
+
+ git ls-remote >actual &&
+ test_cmp exp actual
+
+'
+
+cat >exp <<EOF
+fatal: 'refs*master' does not appear to be a git repository
+fatal: The remote end hung up unexpectedly
+EOF
+test_expect_success 'confuses pattern as remote when no remote specified' '
+ #
+ # Do not expect "git ls-remote <pattern>" to work; ls-remote, correctly,
+ # confuses <pattern> for <remote>. Although ugly, this behaviour is akin
+ # to the confusion of refspecs for remotes by git-fetch and git-push,
+ # eg:
+ #
+ # $ git fetch branch
+ #
+
+ # We could just as easily have used "master"; the "*" emphasizes its
+ # role as a pattern.
+ test_must_fail git ls-remote refs*master >actual 2>&1 &&
+ test_cmp exp actual
+
+'
+
test_done
mk_test heads/master &&
mk_child child &&
mkdir testrepo/.git/hooks &&
- echo exit 1 >testrepo/.git/hooks/pre-receive &&
+ echo "#!/no/frobnication/today" >testrepo/.git/hooks/pre-receive &&
chmod +x testrepo/.git/hooks/pre-receive &&
(cd child &&
git pull .. master
mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
'
-test_expect_success 'clone remote repository' '
+cat >exp <<EOF
+GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+EOF
+test_expect_success 'no empty path components' '
+ # In the URL, add a trailing slash, and see if git appends yet another
+ # slash.
cd "$ROOT_PATH" &&
+ git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+
+ sed -e "
+ s/^.* \"//
+ s/\"//
+ s/ [1-9][0-9]*\$//
+ s/^GET /GET /
+ " >act <"$HTTPD_ROOT_PATH"/access.log &&
+
+ # Clear the log, so that it does not affect the "used receive-pack
+ # service" test which reads the log too.
+ #
+ # We do this before the actual comparison to ensure the log is cleared.
+ echo > "$HTTPD_ROOT_PATH"/access.log &&
+
+ test_cmp exp act
+'
+
+test_expect_success 'clone remote repository' '
+ rm -rf test_repo_clone &&
git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
'
'
cat >exp <<EOF
+
GET /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
GET /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
test_expect_success 'output from clone' '
rm -fr dst &&
git clone -n "file://$(pwd)/src" dst >output &&
- test $(grep Initialized output | wc -l) = 1
+ test $(grep Clon output | wc -l) = 1
'
test_expect_success 'clone does not keep pack' '
git config pack.compression 0 &&
git config pack.depth 0 &&
- blobsize=$((20*1024*1024)) &&
+ blobsize=$((100*1024*1024)) &&
blobcount=$((2*1024*1024*1024/$blobsize+1)) &&
i=1 &&
(while test $i -le $blobcount
'
-test_expect_success 'clone' '
+test_expect_success 'clone - bare' '
- git clone --bare --no-hardlinks . clone
+ git clone --bare --no-hardlinks . clone-bare
+
+'
+
+test_expect_success 'clone - with worktree, file:// protocol' '
+
+ git clone file://. clone-wt
'
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2010 Sverre Rabbelier
+#
+
+test_description='Test remote-helper import and export commands'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON
+then
+ say 'skipping git remote-testgit tests: requires Python support'
+ test_done
+fi
+
+test_expect_success 'setup repository' '
+ git init --bare server/.git &&
+ git clone server public &&
+ (cd public &&
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ git push origin master)
+'
+
+test_expect_success 'cloning from local repo' '
+ git clone "testgit::${PWD}/server" localclone &&
+ test_cmp public/file localclone/file
+'
+
+test_expect_success 'cloning from remote repo' '
+ git clone "testgit::file://${PWD}/server" clone &&
+ test_cmp public/file clone/file
+'
+
+test_expect_success 'create new commit on remote' '
+ (cd public &&
+ echo content >>file &&
+ git commit -a -m two &&
+ git push)
+'
+
+test_expect_success 'pulling from local repo' '
+ (cd localclone && git pull) &&
+ test_cmp public/file localclone/file
+'
+
+test_expect_success 'pulling from remote remote' '
+ (cd clone && git pull) &&
+ test_cmp public/file clone/file
+'
+
+test_expect_success 'pushing to local repo' '
+ (cd localclone &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git push) &&
+ HEAD=$(git --git-dir=localclone/.git rev-parse --verify HEAD) &&
+ test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
+'
+
+test_expect_success 'synch with changes from localclone' '
+ (cd clone &&
+ git pull)
+'
+
+test_expect_success 'pushing remote local repo' '
+ (cd clone &&
+ echo content >>file &&
+ git commit -a -m four &&
+ git push) &&
+ HEAD=$(git --git-dir=clone/.git rev-parse --verify HEAD) &&
+ test $HEAD = $(git --git-dir=server/.git rev-parse --verify HEAD)
+'
+
+test_done
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
EOF
+test_format raw-body %B <<'EOF'
+commit 131a310eb913d107dd3c09a65d1651175898735d
+changed foo
+
+commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
+added foo
+
+EOF
+
test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
\e[31mfoo\e[32mbar\e[34mbaz\e[mxyzzy
test_cmp expect.gd-short actual.gd-short
'
+test_expect_success 'oneline with empty message' '
+ git commit -m "dummy" --allow-empty &&
+ git commit -m "dummy" --allow-empty &&
+ git filter-branch --msg-filter "sed -e s/dummy//" HEAD^^.. &&
+ git rev-list --oneline HEAD >test.txt &&
+ test $(git rev-list --oneline HEAD | wc -l) -eq 5 &&
+ test $(git rev-list --oneline --graph HEAD | wc -l) -eq 5
+'
+
test_done
In loco pascuae ibi me collocavit;
super aquam refectionis educavit me.
-|||||||
+||||||| new5.txt
et nihil mihi deerit.
In loco pascuae ibi me collocavit,
super aquam refectionis educavit me;
In loco pascuae ibi me collocavit;
super aquam refectionis educavit me.
-||||||||||
+|||||||||| new5.txt
et nihil mihi deerit.
In loco pascuae ibi me collocavit,
super aquam refectionis educavit me;
o----o----o----o----o----. /
\ A c /
.------------o---o---o
- D e
+ D,R e
'
. ./test-lib.sh
echo D >another && git add another && git commit -m D &&
test_tick &&
git tag -a -m D D &&
+ test_tick &&
+ git tag -a -m R R &&
test_tick &&
echo DD >another && git commit -a -m another &&
check_describe A-* HEAD
check_describe A-* HEAD^
-check_describe D-* HEAD^^
+check_describe R-* HEAD^^
check_describe A-* HEAD^^2
check_describe B HEAD^^2^
-check_describe D-* HEAD^^^
+check_describe R-* HEAD^^^
check_describe c-* --tags HEAD
check_describe c-* --tags HEAD^
. ./test-lib.sh
-datestamp=1151939923
-setdate () {
- GIT_COMMITTER_DATE="$datestamp +0200"
- GIT_AUTHOR_DATE="$datestamp +0200"
- datestamp=`expr "$datestamp" + 1`
- export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
-}
-
test_expect_success setup '
echo one >one &&
git add one &&
- setdate &&
+ test_tick &&
git commit -m "Initial" &&
+ git clone . remote &&
+
echo uno >one &&
echo dos >two &&
git add two &&
- setdate &&
+ test_tick &&
git commit -a -m "Second" &&
git checkout -b left &&
- echo $datestamp >one &&
- setdate &&
+ echo "c1" >one &&
+ test_tick &&
git commit -a -m "Common #1" &&
- echo $datestamp >one &&
- setdate &&
+ echo "c2" >one &&
+ test_tick &&
git commit -a -m "Common #2" &&
git branch right &&
- echo $datestamp >two &&
- setdate &&
+ echo "l3" >two &&
+ test_tick &&
git commit -a -m "Left #3" &&
- echo $datestamp >two &&
- setdate &&
+ echo "l4" >two &&
+ test_tick &&
git commit -a -m "Left #4" &&
- echo $datestamp >two &&
- setdate &&
+ echo "l5" >two &&
+ test_tick &&
git commit -a -m "Left #5" &&
+ git tag tag-l5 &&
git checkout right &&
- echo $datestamp >three &&
+ echo "r3" >three &&
git add three &&
- setdate &&
+ test_tick &&
git commit -a -m "Right #3" &&
+ git tag tag-r3 &&
- echo $datestamp >three &&
- setdate &&
+ echo "r4" >three &&
+ test_tick &&
git commit -a -m "Right #4" &&
- echo $datestamp >three &&
- setdate &&
+ echo "r5" >three &&
+ test_tick &&
git commit -a -m "Right #5" &&
+ git checkout -b long &&
+ i=0 &&
+ while test $i -lt 30
+ do
+ test_commit $i one &&
+ i=$(($i+1))
+ done &&
+
git show-branch
'
git config merge.log true &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
git config merge.summary true &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
git config merge.log true &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
git config merge.summary true &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
git config merge.log yes &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
git config merge.summary yes &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left right &&
git fmt-merge-msg <.git/FETCH_HEAD >actual &&
git config merge.summary yes &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left right &&
git fmt-merge-msg -F .git/FETCH_HEAD >actual &&
git config merge.summary yes &&
git checkout master &&
- setdate &&
+ test_tick &&
git fetch . left right &&
mkdir sub &&
cp .git/FETCH_HEAD sub/FETCH_HEAD &&
test_cmp expected actual
'
+test_expect_success 'merge-msg with nothing to merge' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ (
+ cd remote &&
+ git checkout -b unrelated &&
+ test_tick &&
+ git fetch origin &&
+ git fmt-merge-msg <.git/FETCH_HEAD >../actual
+ ) &&
+
+ test_cmp /dev/null actual
+'
+
+cat >expected <<\EOF
+Merge tag 'tag-r3'
+
+* tag 'tag-r3':
+ Right #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg tag' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . tag tag-r3 &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge tags 'tag-r3' and 'tag-l5'
+
+* tag 'tag-r3':
+ Right #3
+ Common #2
+ Common #1
+
+* tag 'tag-l5':
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg two tags' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . tag tag-r3 tag tag-l5 &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge branch 'left', tag 'tag-r3'
+
+* tag 'tag-r3':
+ Right #3
+ Common #2
+ Common #1
+
+* left:
+ Left #5
+ Left #4
+ Left #3
+ Common #2
+ Common #1
+EOF
+
+test_expect_success 'merge-msg tag and branch' '
+
+ git config --unset-all merge.log
+ git config --unset-all merge.summary
+ git config merge.summary yes &&
+
+ git checkout master &&
+ test_tick &&
+ git fetch . tag tag-r3 left &&
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
+cat >expected <<\EOF
+Merge branch 'long'
+
+* long: (35 commits)
+EOF
+
+test_expect_success 'merge-msg lots of commits' '
+
+ git checkout master &&
+ test_tick &&
+ git fetch . long &&
+
+ i=29 &&
+ while test $i -gt 9
+ do
+ echo " $i" &&
+ i=$(($i-1))
+ done >>expected &&
+ echo " ..." >>expected
+
+ git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+ test_cmp expected actual
+'
+
test_done
test_cmp expected actual
'
+cat >expected <<EOF
+67a36f1
+EOF
+
+test_expect_success 'Check short objectname format' '
+ git for-each-ref --format="%(objectname:short)" refs/heads >actual &&
+ test_cmp expected actual
+'
+
test_expect_success 'Check for invalid refname format' '
test_must_fail git for-each-ref --format="%(refname:INVALID)"
'
test_cmp expected result
'
-test_expect_failure 'git-apply adds file' false
-test_expect_failure 'git-apply updates file' false
-test_expect_failure 'git-apply removes file' false
-test_expect_failure 'git-mv to skip-worktree' false
-test_expect_failure 'git-mv from skip-worktree' false
-test_expect_failure 'git-checkout' false
+#TODO test_expect_failure 'git-apply adds file' false
+#TODO test_expect_failure 'git-apply updates file' false
+#TODO test_expect_failure 'git-apply removes file' false
+#TODO test_expect_failure 'git-mv to skip-worktree' false
+#TODO test_expect_failure 'git-mv from skip-worktree' false
+#TODO test_expect_failure 'git-checkout' false
test_done
! [master] Initial A one, A two
* [renamer] Renamer R one->uno, M two
! [side] Side M one, D two, A three
- ---
- + [side] Side M one, D two, A three
- * [renamer] Renamer R one->uno, M two
- +*+ [master] Initial A one, A two
+ ! [simple] Simple D one, M two
+ ----
+ + [simple] Simple D one, M two
+ + [side] Side M one, D two, A three
+ * [renamer] Renamer R one->uno, M two
+ +*++ [master] Initial A one, A two
'
git update-index --add --remove one two three &&
git commit -m "Side M one, D two, A three" &&
+ git checkout -b simple master &&
+ rm -f one &&
+ fill a c e > two &&
+ git commit -a -m "Simple D one, M two" &&
+
git checkout master
'
! test -s current
'
+test_expect_success 'format of merge conflict from checkout -m' '
+
+ git checkout -f master && git clean -f &&
+
+ fill b d > two &&
+ git checkout -m simple &&
+
+ git ls-files >current &&
+ fill same two two two >expect &&
+ test_cmp current expect &&
+
+ cat <<-EOF >expect &&
+ <<<<<<< simple
+ a
+ c
+ e
+ =======
+ b
+ d
+ >>>>>>> local
+ EOF
+ test_cmp two expect
+'
+
+test_expect_success 'checkout --merge --conflict=diff3 <branch>' '
+
+ git checkout -f master && git reset --hard && git clean -f &&
+
+ fill b d > two &&
+ git checkout --merge --conflict=diff3 simple &&
+
+ cat <<-EOF >expect &&
+ <<<<<<< simple
+ a
+ c
+ e
+ ||||||| master
+ a
+ b
+ c
+ d
+ e
+ =======
+ b
+ d
+ >>>>>>> local
+ EOF
+ test_cmp two expect
+'
+
test_expect_success 'checkout to detach HEAD (with advice declined)' '
git config advice.detachedHead false &&
(
echo "<<<<<<< ours"
echo ourside
- echo "|||||||"
+ echo "||||||| base"
echo original
echo "======="
echo theirside
(
echo "<<<<<<< ours"
echo ourside
- echo "|||||||"
+ echo "||||||| base"
echo original
echo "======="
echo theirside
commit_msg_is "-F log"
'
+test_expect_success 'Commit without message is allowed with --allow-empty-message' '
+ echo "more content" >>foo &&
+ git add foo &&
+ >empty &&
+ git commit --allow-empty-message <empty &&
+ commit_msg_is ""
+'
+
+test_expect_success 'Commit without message is no-no without --allow-empty-message' '
+ echo "more content" >>foo &&
+ git add foo &&
+ >empty &&
+ test_must_fail git commit <empty
+'
+
+test_expect_success 'Commit a message with --allow-empty-message' '
+ echo "even more content" >>foo &&
+ git add foo &&
+ git commit --allow-empty-message -m"hello there" &&
+ commit_msg_is "hello there"
+'
+
test_done
EOF
'
+test_expect_success 'setup .git file for sub' '
+ (cd sub &&
+ rm -f new-file
+ REAL="$(pwd)/../.real" &&
+ mv .git "$REAL"
+ echo "gitdir: $REAL" >.git) &&
+ echo .real >>.gitignore &&
+ git commit -m "added .real to .gitignore" .gitignore
+'
+
+test_expect_success 'status with added file in modified submodule with .git file' '
+ (cd sub && git reset --hard && echo >foo && git add foo) &&
+ git status >output &&
+ grep "modified: sub (new commits, modified content)" output
+'
+
test_expect_success 'rm submodule contents' '
rm -rf sub/* sub/.git
'
test_cmp expect output
'
+cat >expect <<EOF
+:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M dir1/modified
+EOF
+test_expect_success 'status refreshes the index' '
+ touch dir2/added &&
+ git status &&
+ git diff-files >output &&
+ test_cmp expect output
+'
+
test_expect_success 'setup status submodule summary' '
test_create_repo sm && (
cd sm &&
test_cmp expect output
'
+test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
+ (
+ chmod a-w .git &&
+ # make dir1/tracked stat-dirty
+ >dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked &&
+ git status -s >output &&
+ ! grep dir1/tracked output &&
+ # make sure "status" succeeded without writing index out
+ git diff-files | grep dir1/tracked
+ )
+ status=$?
+ chmod 775 .git
+ (exit $status)
+'
+
test_done
test_expect_success 'refresh the index before merging' '
git reset --hard c1 &&
- sleep 1 &&
- touch file &&
+ cp file file.n && mv -f file.n file &&
git merge c3
'
"$svnrepo/pr ject/branches/more fun plugin!" &&
svn_cmd cp -m "scary" "$svnrepo/pr ject/branches/fun plugin" \
"$svnrepo/pr ject/branches/$scary_uri" &&
+ svn_cmd cp -m "leading dot" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/.leading_dot" &&
+ svn_cmd cp -m "trailing dot" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/trailing_dot." &&
+ svn_cmd cp -m "trailing .lock" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/trailing_dotlock.lock" &&
+ svn_cmd cp -m "reflog" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/not-a@{0}reflog" &&
start_httpd
'
git rev-parse "refs/remotes/fun%20plugin" &&
git rev-parse "refs/remotes/more%20fun%20plugin!" &&
git rev-parse "refs/remotes/$scary_ref" &&
+ git rev-parse "refs/remotes/%2Eleading_dot" &&
+ git rev-parse "refs/remotes/trailing_dot%2E" &&
+ git rev-parse "refs/remotes/trailing_dotlock%2Elock" &&
+ git rev-parse "refs/remotes/not-a%40{0}reflog" &&
cd ..
'
cd ..
'
+test_expect_success 'test dcommit to trailing_dotlock branch' '
+ cd project &&
+ git reset --hard "refs/remotes/trailing_dotlock%2Elock" &&
+ echo who names branches like this anyway? >> foo &&
+ git commit -m "bar" -- foo &&
+ git svn dcommit &&
+ cd ..
+ '
+
stop_httpd
test_done
#
# Copyright (c) 2005 Junio C Hamano
#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see http://www.gnu.org/licenses/ .
# if --tee was passed, write the output not only to the terminal, but
# additionally to the file test-results/$BASENAME.out, too.
}
test_run_ () {
+ test_cleanup=:
eval >&3 2>&4 "$1"
- eval_ret="$?"
+ eval_ret=$?
+ eval >&3 2>&4 "$test_cleanup"
return 0
}
$GIT_TEST_CMP "$@"
}
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+# test_expect_success 'test core.capslock' '
+# git config core.capslock true &&
+# test_when_finished "git config --unset core.capslock" &&
+# hello world
+# '
+#
+# That would be roughly equivalent to
+#
+# test_expect_success 'test core.capslock' '
+# git config core.capslock true &&
+# hello world
+# git config --unset core.capslock
+# '
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+
+test_when_finished () {
+ test_cleanup="{ $*
+ } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
# Most tests can use the created repository, but some may need to create more.
# Usage: test_create_repo <directory>
test_create_repo () {
return (struct tag *) obj;
}
+static unsigned long parse_tag_date(const char *buf, const char *tail)
+{
+ const char *dateptr;
+
+ while (buf < tail && *buf++ != '>')
+ /* nada */;
+ if (buf >= tail)
+ return 0;
+ dateptr = buf;
+ while (buf < tail && *buf++ != '\n')
+ /* nada */;
+ if (buf >= tail)
+ return 0;
+ /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
+ return strtoul(dateptr, NULL, 10);
+}
+
int parse_tag_buffer(struct tag *item, void *data, unsigned long size)
{
- int typelen, taglen;
unsigned char sha1[20];
- const char *type_line, *tag_line, *sig_line;
char type[20];
- const char *start = data;
+ const char *bufptr = data;
+ const char *tail = bufptr + size;
+ const char *nl;
- if (item->object.parsed)
- return 0;
- item->object.parsed = 1;
+ if (item->object.parsed)
+ return 0;
+ item->object.parsed = 1;
if (size < 64)
return -1;
- if (memcmp("object ", data, 7) || get_sha1_hex((char *) data + 7, sha1))
+ if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n')
return -1;
+ bufptr += 48; /* "object " + sha1 + "\n" */
- type_line = (char *) data + 48;
- if (memcmp("\ntype ", type_line-1, 6))
+ if (prefixcmp(bufptr, "type "))
return -1;
-
- tag_line = memchr(type_line, '\n', size - (type_line - start));
- if (!tag_line || memcmp("tag ", ++tag_line, 4))
+ bufptr += 5;
+ nl = memchr(bufptr, '\n', tail - bufptr);
+ if (!nl || sizeof(type) <= (nl - bufptr))
return -1;
-
- sig_line = memchr(tag_line, '\n', size - (tag_line - start));
- if (!sig_line)
- return -1;
- sig_line++;
-
- typelen = tag_line - type_line - strlen("type \n");
- if (typelen >= 20)
- return -1;
- memcpy(type, type_line + 5, typelen);
- type[typelen] = '\0';
- taglen = sig_line - tag_line - strlen("tag \n");
- item->tag = xmemdupz(tag_line + 4, taglen);
+ strncpy(type, bufptr, nl - bufptr);
+ type[nl - bufptr] = '\0';
+ bufptr = nl + 1;
if (!strcmp(type, blob_type)) {
item->tagged = &lookup_blob(sha1)->object;
item->tagged = NULL;
}
+ if (prefixcmp(bufptr, "tag "))
+ return -1;
+ bufptr += 4;
+ nl = memchr(bufptr, '\n', tail - bufptr);
+ if (!nl)
+ return -1;
+ item->tag = xmemdupz(bufptr, nl - bufptr);
+ bufptr = nl + 1;
+
+ if (!prefixcmp(bufptr, "tagger "))
+ item->date = parse_tag_date(bufptr, tail);
+ else
+ item->date = 0;
+
return 0;
}
struct object object;
struct object *tagged;
char *tag;
- char *signature; /* not actually implemented */
+ unsigned long date;
};
extern struct tag *lookup_tag(const unsigned char *sha1);
template_instdir ?= $(prefix)/share/git-core/templates
# DESTDIR=
+ifndef SHELL_PATH
+ SHELL_PATH = /bin/sh
+endif
+ifndef PERL_PATH
+ PERL_PATH = perl
+endif
+
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+
# Shell quote (do not use $(call) to accommodate ancient setups);
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
template_instdir_SQ = $(subst ','\'',$(template_instdir))
case "$$boilerplate" in \
*--) continue;; \
esac && \
- cp $$boilerplate blt/$$dst && \
- if test -x "blt/$$dst"; then rx=rx; else rx=r; fi && \
+ sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+ -e 's|@SHELL_PATH@|$(SHELL_PATH_SQ)|' \
+ -e 's|@PERL_PATH@|$(PERL_PATH_SQ)|g' $$boilerplate > \
+ blt/$$dst && \
+ if test -x "$$boilerplate"; then rx=rx; else rx=r; fi && \
chmod a+$$rx "blt/$$dst" || exit; \
done && \
date >$@
#!/bin/sh
#
# An example hook script to check the commit log message.
-# Called by git-commit with one argument, the name of the file
+# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
#
# To enable this hook, rename this file to "post-update".
-exec git-update-server-info
+exec git update-server-info
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
-# Called by git-commit with no arguments. The hook should
+# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
-if git-rev-parse --verify HEAD >/dev/null 2>&1
+if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
#
# Copyright (c) 2006, 2008 Junio C Hamano
#
-# The "pre-rebase" hook is run just before "git-rebase" starts doing
+# The "pre-rebase" hook is run just before "git rebase" starts doing
# its job, and can prevent the command from running by exiting with
# non-zero status.
#
}
# Is topic fully merged to master?
-not_in_master=`git-rev-list --pretty=oneline ^master "$topic"`
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
if test -z "$not_in_master"
then
echo >&2 "$topic is fully merged to master; better remove it."
fi
# Is topic ever merged to next? If so you should not be rebasing it.
-only_next_1=`git-rev-list ^master "^$topic" ${publish} | sort`
-only_next_2=`git-rev-list ^master ${publish} | sort`
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master ${publish} | sort`
if test "$only_next_1" = "$only_next_2"
then
- not_in_topic=`git-rev-list "^$topic" master`
+ not_in_topic=`git rev-list "^$topic" master`
if test -z "$not_in_topic"
then
echo >&2 "$topic is already up-to-date with master"
exit 0
fi
else
- not_in_next=`git-rev-list --pretty=oneline ^${publish} "$topic"`
- perl -e '
+ not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+ @PERL_PATH@ -e '
my $topic = $ARGV[0];
my $msg = "* $topic has commits already merged to public branch:\n";
my (%not_in_next) = map {
To compute (1):
- git-rev-list ^master ^topic next
- git-rev-list ^master next
+ git rev-list ^master ^topic next
+ git rev-list ^master next
if these match, topic has not merged in next at all.
To compute (2):
- git-rev-list master..topic
+ git rev-list master..topic
if this is empty, it is fully merged to "master".
#!/bin/sh
#
# An example hook script to prepare the commit log message.
-# Called by git-commit with the name of the file that has the
+# Called by "git commit" with the name of the file that has the
# commit message, followed by the description of the commit
# message's source. The hook's purpose is to edit the commit
# message file. If the hook fails with a non-zero status,
case "$2,$3" in
merge,)
- perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+ @PERL_PATH@ -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
# ,|template,)
-# perl -i.bak -pe '
+# @PERL_PATH@ -i.bak -pe '
# print "\n" . `git diff --cached --name-status -r`
# if /^#/ && $first++ == 0' "$1" ;;
#!/bin/sh
#
# An example hook script to blocks unannotated tags from entering.
-# Called by git-receive-pack with arguments: refname sha1-old sha1-new
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
#
# To enable this hook, rename this file to "update".
#
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
- newrev_type=$(git-cat-file -t $newrev)
+ newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
-# git-ls-files --others --exclude-from=.git/info/exclude
+# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
#include "cache.h"
+#include <pthread.h>
#if defined(hpux) || defined(__hpux) || defined(_hpux)
# include <sys/pstat.h>
return 1;
}
+
+int init_recursive_mutex(pthread_mutex_t *m)
+{
+ pthread_mutexattr_t a;
+ int ret;
+
+ ret = pthread_mutexattr_init(&a);
+ if (!ret) {
+ ret = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_RECURSIVE);
+ if (!ret)
+ ret = pthread_mutex_init(m, &a);
+ pthread_mutexattr_destroy(&a);
+ }
+ return ret;
+}
#define THREAD_COMPAT_H
extern int online_cpus(void);
+extern int init_recursive_mutex(pthread_mutex_t*);
#endif /* THREAD_COMPAT_H */
#include "revision.h"
#include "quote.h"
#include "remote.h"
+#include "string-list.h"
static int debug;
FILE *out;
unsigned fetch : 1,
import : 1,
+ export : 1,
option : 1,
push : 1,
connect : 1,
data->push = 1;
else if (!strcmp(capname, "import"))
data->import = 1;
+ else if (!strcmp(capname, "export"))
+ data->export = 1;
else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
ALLOC_GROW(refspecs,
refspec_nr + 1,
refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
} else if (!strcmp(capname, "connect")) {
data->connect = 1;
+ } else if (!strcmp(buf.buf, "gitdir")) {
+ struct strbuf gitdir = STRBUF_INIT;
+ strbuf_addf(&gitdir, "gitdir %s\n", get_git_dir());
+ sendline(data, &gitdir);
+ strbuf_release(&gitdir);
} else if (mandatory) {
die("Unknown mandatory capability %s. This remote "
"helper probably needs newer version of Git.\n",
return start_command(fastimport);
}
+static int get_exporter(struct transport *transport,
+ struct child_process *fastexport,
+ const char *export_marks,
+ const char *import_marks,
+ struct string_list *revlist_args)
+{
+ struct child_process *helper = get_helper(transport);
+ int argc = 0, i;
+ memset(fastexport, 0, sizeof(*fastexport));
+
+ /* we need to duplicate helper->in because we want to use it after
+ * fastexport is done with it. */
+ fastexport->out = dup(helper->in);
+ fastexport->argv = xcalloc(4 + revlist_args->nr, sizeof(*fastexport->argv));
+ fastexport->argv[argc++] = "fast-export";
+ if (export_marks)
+ fastexport->argv[argc++] = export_marks;
+ if (import_marks)
+ fastexport->argv[argc++] = import_marks;
+
+ for (i = 0; i < revlist_args->nr; i++)
+ fastexport->argv[argc++] = revlist_args->items[i].string;
+
+ fastexport->git_cmd = 1;
+ return start_command(fastexport);
+}
+
static int fetch_with_import(struct transport *transport,
int nr_heads, struct ref **to_fetch)
{
return -1;
}
-static int push_refs(struct transport *transport,
+static int push_refs_with_push(struct transport *transport,
struct ref *remote_refs, int flags)
{
int force_all = flags & TRANSPORT_PUSH_FORCE;
struct child_process *helper;
struct ref *ref;
- if (process_connect(transport, 1)) {
- do_take_over(transport);
- return transport->push_refs(transport, remote_refs, flags);
- }
-
- if (!remote_refs) {
- fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
- "Perhaps you should specify a branch such as 'master'.\n");
- return 0;
- }
-
helper = get_helper(transport);
if (!data->push)
return 1;
return 0;
}
+static int push_refs_with_export(struct transport *transport,
+ struct ref *remote_refs, int flags)
+{
+ struct ref *ref;
+ struct child_process *helper, exporter;
+ struct helper_data *data = transport->data;
+ char *export_marks = NULL, *import_marks = NULL;
+ struct string_list revlist_args = { NULL, 0, 0 };
+ struct strbuf buf = STRBUF_INIT;
+
+ helper = get_helper(transport);
+
+ write_constant(helper->in, "export\n");
+
+ recvline(data, &buf);
+ if (debug)
+ fprintf(stderr, "Debug: Got export_marks '%s'\n", buf.buf);
+ if (buf.len) {
+ struct strbuf arg = STRBUF_INIT;
+ strbuf_addstr(&arg, "--export-marks=");
+ strbuf_addbuf(&arg, &buf);
+ export_marks = strbuf_detach(&arg, NULL);
+ }
+
+ recvline(data, &buf);
+ if (debug)
+ fprintf(stderr, "Debug: Got import_marks '%s'\n", buf.buf);
+ if (buf.len) {
+ struct strbuf arg = STRBUF_INIT;
+ strbuf_addstr(&arg, "--import-marks=");
+ strbuf_addbuf(&arg, &buf);
+ import_marks = strbuf_detach(&arg, NULL);
+ }
+
+ strbuf_reset(&buf);
+
+ for (ref = remote_refs; ref; ref = ref->next) {
+ char *private;
+ unsigned char sha1[20];
+
+ if (!data->refspecs)
+ continue;
+ private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
+ if (private && !get_sha1(private, sha1)) {
+ strbuf_addf(&buf, "^%s", private);
+ string_list_append(strbuf_detach(&buf, NULL), &revlist_args);
+ }
+
+ string_list_append(ref->name, &revlist_args);
+
+ }
+
+ if (get_exporter(transport, &exporter,
+ export_marks, import_marks, &revlist_args))
+ die("Couldn't run fast-export");
+
+ data->no_disconnect_req = 1;
+ finish_command(&exporter);
+ disconnect_helper(transport);
+ return 0;
+}
+
+static int push_refs(struct transport *transport,
+ struct ref *remote_refs, int flags)
+{
+ struct helper_data *data = transport->data;
+
+ if (process_connect(transport, 1)) {
+ do_take_over(transport);
+ return transport->push_refs(transport, remote_refs, flags);
+ }
+
+ if (!remote_refs) {
+ fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
+ "Perhaps you should specify a branch such as 'master'.\n");
+ return 0;
+ }
+
+ if (data->push)
+ return push_refs_with_push(transport, remote_refs, flags);
+
+ if (data->export)
+ return push_refs_with_export(transport, remote_refs, flags);
+
+ return -1;
+}
+
+
static int has_attribute(const char *attrs, const char *attr) {
int len;
if (!attrs)
{
if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
return;
- if (S_ISGITLINK(ce->ce_mode)) {
- if (rmdir(ce->name)) {
- warning("unable to rmdir %s: %s",
- ce->name, strerror(errno));
- return;
- }
- }
- else
- if (unlink_or_warn(ce->name))
- return;
+ if (remove_or_warn(ce->ce_mode, ce->name))
+ return;
schedule_dir_for_removal(ce->name, ce_namelen(ce));
}
+#include "cache.h"
#include "userdiff.h"
#include "cache.h"
#include "attr.h"
return 1;
}
+static int parse_bool(int *b, const char *k, const char *v)
+{
+ *b = git_config_bool(k, v);
+ return 1;
+}
+
int userdiff_config(const char *k, const char *v)
{
struct userdiff_driver *drv;
return parse_string(&drv->external, k, v);
if ((drv = parse_driver(k, v, "textconv")))
return parse_string(&drv->textconv, k, v);
+ if ((drv = parse_driver(k, v, "cachetextconv")))
+ return parse_bool(&drv->textconv_want_cache, k, v);
if ((drv = parse_driver(k, v, "wordregex")))
return parse_string(&drv->word_regex, k, v);
#ifndef USERDIFF_H
#define USERDIFF_H
+#include "notes-cache.h"
+
struct userdiff_funcname {
const char *pattern;
int cflags;
struct userdiff_funcname funcname;
const char *word_regex;
const char *textconv;
+ struct notes_cache *textconv_cache;
+ int textconv_want_cache;
};
int userdiff_config(const char *k, const char *v);
*/
#include "cache.h"
+static void try_to_free_builtin(size_t size)
+{
+ release_pack_memory(size, -1);
+}
+
+static void (*try_to_free_routine)(size_t size) = try_to_free_builtin;
+
+void set_try_to_free_routine(void (*routine)(size_t))
+{
+ try_to_free_routine = (routine) ? routine : try_to_free_builtin;
+}
+
char *xstrdup(const char *str)
{
char *ret = strdup(str);
if (!ret) {
- release_pack_memory(strlen(str) + 1, -1);
+ try_to_free_routine(strlen(str) + 1);
ret = strdup(str);
if (!ret)
die("Out of memory, strdup failed");
if (!ret && !size)
ret = malloc(1);
if (!ret) {
- release_pack_memory(size, -1);
+ try_to_free_routine(size);
ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret) {
- release_pack_memory(size, -1);
+ try_to_free_routine(size);
ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
if (!ret) {
- release_pack_memory(nmemb * size, -1);
+ try_to_free_routine(nmemb * size);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
}
-int unlink_or_warn(const char *file)
+static int warn_if_unremovable(const char *op, const char *file, int rc)
{
- int rc = unlink(file);
-
if (rc < 0) {
int err = errno;
if (ENOENT != err) {
- warning("unable to unlink %s: %s",
- file, strerror(errno));
+ warning("unable to %s %s: %s",
+ op, file, strerror(errno));
errno = err;
}
}
return rc;
}
+int unlink_or_warn(const char *file)
+{
+ return warn_if_unremovable("unlink", file, unlink(file));
+}
+
+int rmdir_or_warn(const char *file)
+{
+ return warn_if_unremovable("rmdir", file, rmdir(file));
+}
+
+int remove_or_warn(unsigned int mode, const char *file)
+{
+ return S_ISGITLINK(mode) ? rmdir_or_warn(file) : unlink_or_warn(file);
+}
static struct whitespace_rule {
const char *rule_name;
unsigned rule_bits;
- unsigned loosens_error;
+ unsigned loosens_error:1,
+ exclude_default:1;
} whitespace_rule_names[] = {
{ "trailing-space", WS_TRAILING_SPACE, 0 },
{ "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
{ "cr-at-eol", WS_CR_AT_EOL, 1 },
{ "blank-at-eol", WS_BLANK_AT_EOL, 0 },
{ "blank-at-eof", WS_BLANK_AT_EOF, 0 },
+ { "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 },
};
unsigned parse_whitespace_rule(const char *string)
}
string = ep;
}
+
+ if (rule & WS_TAB_IN_INDENT && rule & WS_INDENT_WITH_NON_TAB)
+ die("cannot enforce both tab-in-indent and indent-with-non-tab");
return rule;
}
unsigned all_rule = 0;
int i;
for (i = 0; i < ARRAY_SIZE(whitespace_rule_names); i++)
- if (!whitespace_rule_names[i].loosens_error)
+ if (!whitespace_rule_names[i].loosens_error &&
+ !whitespace_rule_names[i].exclude_default)
all_rule |= whitespace_rule_names[i].rule_bits;
return all_rule;
} else if (ATTR_FALSE(value)) {
strbuf_addstr(&err, ", ");
strbuf_addstr(&err, "indent with spaces");
}
+ if (ws & WS_TAB_IN_INDENT) {
+ if (err.len)
+ strbuf_addstr(&err, ", ");
+ strbuf_addstr(&err, "tab in indent");
+ }
return strbuf_detach(&err, NULL);
}
}
}
- /* Check for space before tab in initial indent. */
+ /* Check indentation */
for (i = 0; i < len; i++) {
if (line[i] == ' ')
continue;
fputs(ws, stream);
fwrite(line + written, i - written, 1, stream);
fputs(reset, stream);
+ fwrite(line + i, 1, 1, stream);
}
- } else if (stream)
- fwrite(line + written, i - written, 1, stream);
- if (stream)
- fwrite(line + i, 1, 1, stream);
+ } else if (ws_rule & WS_TAB_IN_INDENT) {
+ result |= WS_TAB_IN_INDENT;
+ if (stream) {
+ fwrite(line + written, i - written, 1, stream);
+ fputs(ws, stream);
+ fwrite(line + i, 1, 1, stream);
+ fputs(reset, stream);
+ }
+ } else if (stream) {
+ fwrite(line + written, i - written + 1, 1, stream);
+ }
written = i + 1;
}
return 1;
}
-/* Copy the line to the buffer while fixing whitespaces */
-int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *error_count)
+/* Copy the line onto the end of the strbuf while fixing whitespaces */
+void ws_fix_copy(struct strbuf *dst, const char *src, int len, unsigned ws_rule, int *error_count)
{
/*
* len is number of bytes to be copied from src, starting
int last_tab_in_indent = -1;
int last_space_in_indent = -1;
int need_fix_leading_space = 0;
- char *buf;
/*
* Strip trailing whitespace
break;
}
- buf = dst;
if (need_fix_leading_space) {
/* Process indent ourselves */
int consecutive_spaces = 0;
char ch = src[i];
if (ch != ' ') {
consecutive_spaces = 0;
- *dst++ = ch;
+ strbuf_addch(dst, ch);
} else {
consecutive_spaces++;
if (consecutive_spaces == 8) {
- *dst++ = '\t';
+ strbuf_addch(dst, '\t');
consecutive_spaces = 0;
}
}
}
while (0 < consecutive_spaces--)
- *dst++ = ' ';
+ strbuf_addch(dst, ' ');
+ len -= last;
+ src += last;
+ fixed = 1;
+ } else if ((ws_rule & WS_TAB_IN_INDENT) && last_tab_in_indent >= 0) {
+ /* Expand tabs into spaces */
+ int last = last_tab_in_indent + 1;
+ for (i = 0; i < last; i++) {
+ if (src[i] == '\t')
+ do {
+ strbuf_addch(dst, ' ');
+ } while (dst->len % 8);
+ else
+ strbuf_addch(dst, src[i]);
+ }
len -= last;
src += last;
fixed = 1;
}
- memcpy(dst, src, len);
+ strbuf_add(dst, src, len);
if (add_cr_to_tail)
- dst[len++] = '\r';
+ strbuf_addch(dst, '\r');
if (add_nl_to_tail)
- dst[len++] = '\n';
+ strbuf_addch(dst, '\n');
if (fixed && error_count)
(*error_count)++;
- return dst + len - buf;
}
s->index_file = get_index_file();
s->change.strdup_strings = 1;
s->untracked.strdup_strings = 1;
+ s->ignored.strdup_strings = 1;
}
static void wt_status_print_unmerged_header(struct wt_status *s)
color_fprintf_ln(s->fp, c, "#");
}
-static void wt_status_print_untracked_header(struct wt_status *s)
+static void wt_status_print_other_header(struct wt_status *s,
+ const char *what,
+ const char *how)
{
const char *c = color(WT_STATUS_HEADER, s);
- color_fprintf_ln(s->fp, c, "# Untracked files:");
+ color_fprintf_ln(s->fp, c, "# %s files:", what);
if (!advice_status_hints)
return;
- color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
+ color_fprintf_ln(s->fp, c, "# (use \"git %s <file>...\" to include in what will be committed)", how);
color_fprintf_ln(s->fp, c, "#");
}
continue;
if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
continue;
- s->workdir_untracked = 1;
string_list_insert(ent->name, &s->untracked);
+ free(ent);
}
+
+ if (s->show_ignored_files) {
+ dir.nr = 0;
+ dir.flags = DIR_SHOW_IGNORED | DIR_SHOW_OTHER_DIRECTORIES;
+ fill_directory(&dir, s->pathspec);
+ for (i = 0; i < dir.nr; i++) {
+ struct dir_entry *ent = dir.entries[i];
+ if (!cache_name_is_other(ent->name, ent->len))
+ continue;
+ if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL))
+ continue;
+ string_list_insert(ent->name, &s->ignored);
+ free(ent);
+ }
+ }
+
+ free(dir.entries);
}
void wt_status_collect(struct wt_status *s)
run_command(&sm_summary);
}
-static void wt_status_print_untracked(struct wt_status *s)
+static void wt_status_print_other(struct wt_status *s,
+ struct string_list *l,
+ const char *what,
+ const char *how)
{
int i;
struct strbuf buf = STRBUF_INIT;
if (!s->untracked.nr)
return;
- wt_status_print_untracked_header(s);
- for (i = 0; i < s->untracked.nr; i++) {
+ wt_status_print_other_header(s, what, how);
+
+ for (i = 0; i < l->nr; i++) {
struct string_list_item *it;
- it = &(s->untracked.items[i]);
+ it = &(l->items[i]);
color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
quote_path(it->string, strlen(it->string),
wt_status_print_submodule_summary(s, 0); /* staged */
wt_status_print_submodule_summary(s, 1); /* unstaged */
}
- if (s->show_untracked_files)
- wt_status_print_untracked(s);
- else if (s->commitable)
+ if (s->show_untracked_files) {
+ wt_status_print_other(s, &s->untracked, "Untracked", "add");
+ if (s->show_ignored_files)
+ wt_status_print_other(s, &s->ignored, "Ignored", "add -f");
+ } else if (s->commitable)
fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
if (s->verbose)
}
}
-static void wt_shortstatus_untracked(int null_termination, struct string_list_item *it,
- struct wt_status *s)
+static void wt_shortstatus_other(int null_termination, struct string_list_item *it,
+ struct wt_status *s, const char *sign)
{
if (null_termination) {
- fprintf(stdout, "?? %s%c", it->string, 0);
+ fprintf(stdout, "%s %s%c", sign, it->string, 0);
} else {
struct strbuf onebuf = STRBUF_INIT;
const char *one;
one = quote_path(it->string, -1, &onebuf, s->prefix);
- color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "??");
+ color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
printf(" %s\n", one);
strbuf_release(&onebuf);
}
struct string_list_item *it;
it = &(s->untracked.items[i]);
- wt_shortstatus_untracked(null_termination, it, s);
+ wt_shortstatus_other(null_termination, it, s, "??");
+ }
+ for (i = 0; i < s->ignored.nr; i++) {
+ struct string_list_item *it;
+
+ it = &(s->ignored.items[i]);
+ wt_shortstatus_other(null_termination, it, s, "!!");
}
}
int use_color;
int relative_paths;
int submodule_summary;
+ int show_ignored_files;
enum untracked_status_type show_untracked_files;
char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
/* These are computed during processing of the individual sections */
int commitable;
int workdir_dirty;
- int workdir_untracked;
const char *index_file;
FILE *fp;
const char *prefix;
struct string_list change;
struct string_list untracked;
+ struct string_list ignored;
};
void wt_status_prepare(struct wt_status *s);
int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
xdiff_emit_consume_fn fn, void *consume_callback_data,
- xpparam_t const *xpp,
- xdemitconf_t const *xecfg, xdemitcb_t *xecb)
+ xpparam_t const *xpp, xdemitconf_t const *xecfg)
{
int ret;
struct xdiff_emit_state state;
+ xdemitcb_t ecb;
memset(&state, 0, sizeof(state));
state.consume = fn;
state.consume_callback_data = consume_callback_data;
- xecb->outf = xdiff_outf;
- xecb->priv = &state;
+ memset(&ecb, 0, sizeof(ecb));
+ ecb.outf = xdiff_outf;
+ ecb.priv = &state;
strbuf_init(&state.remainder, 0);
- ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
+ ret = xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
strbuf_release(&state.remainder);
return ret;
}
int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
xdiff_emit_consume_fn fn, void *consume_callback_data,
- xpparam_t const *xpp,
- xdemitconf_t const *xecfg, xdemitcb_t *xecb);
+ xpparam_t const *xpp, xdemitconf_t const *xecfg);
int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
xpparam_t const *xpp, xdemitconf_t *xecfg);
int level;
int favor;
int style;
+ const char *ancestor; /* label for orig */
+ const char *file1; /* label for mf1 */
+ const char *file2; /* label for mf2 */
} xmparam_t;
#define DEFAULT_CONFLICT_MARKER_SIZE 7
-int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
- mmfile_t *mf2, const char *name2,
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
xmparam_t const *xmp, mmbuffer_t *result);
#ifdef __cplusplus
static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
xdfenv_t *xe2, const char *name2,
+ const char *name3,
int size, int i, int style,
xdmerge_t *m, char *dest, int marker_size)
{
int marker1_size = (name1 ? strlen(name1) + 1 : 0);
int marker2_size = (name2 ? strlen(name2) + 1 : 0);
- int j;
+ int marker3_size = (name3 ? strlen(name3) + 1 : 0);
if (marker_size <= 0)
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
if (!dest) {
size += marker_size + 1 + marker1_size;
} else {
- for (j = 0; j < marker_size; j++)
- dest[size++] = '<';
+ memset(dest + size, '<', marker_size);
+ size += marker_size;
if (marker1_size) {
dest[size] = ' ';
memcpy(dest + size + 1, name1, marker1_size - 1);
if (style == XDL_MERGE_DIFF3) {
/* Shared preimage */
if (!dest) {
- size += marker_size + 1;
+ size += marker_size + 1 + marker3_size;
} else {
- for (j = 0; j < marker_size; j++)
- dest[size++] = '|';
+ memset(dest + size, '|', marker_size);
+ size += marker_size;
+ if (marker3_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name3, marker3_size - 1);
+ size += marker3_size;
+ }
dest[size++] = '\n';
}
size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
if (!dest) {
size += marker_size + 1;
} else {
- for (j = 0; j < marker_size; j++)
- dest[size++] = '=';
+ memset(dest + size, '=', marker_size);
+ size += marker_size;
dest[size++] = '\n';
}
if (!dest) {
size += marker_size + 1 + marker2_size;
} else {
- for (j = 0; j < marker_size; j++)
- dest[size++] = '>';
+ memset(dest + size, '>', marker_size);
+ size += marker_size;
if (marker2_size) {
dest[size] = ' ';
memcpy(dest + size + 1, name2, marker2_size - 1);
static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
xdfenv_t *xe2, const char *name2,
+ const char *ancestor_name,
int favor,
xdmerge_t *m, char *dest, int style,
int marker_size)
if (m->mode == 0)
size = fill_conflict_hunk(xe1, name1, xe2, name2,
+ ancestor_name,
size, i, style, m, dest,
marker_size);
else if (m->mode & 3) {
*
* returns < 0 on error, == 0 for no conflicts, else number of conflicts
*/
-static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
- xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
- xmparam_t const *xmp, mmbuffer_t *result) {
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
+ xdfenv_t *xe2, xdchange_t *xscr2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
xdmerge_t *changes, *c;
xpparam_t const *xpp = &xmp->xpp;
+ const char *const ancestor_name = xmp->ancestor;
+ const char *const name1 = xmp->file1;
+ const char *const name2 = xmp->file2;
int i0, i1, i2, chg0, chg1, chg2;
int level = xmp->level;
int style = xmp->style;
if (result) {
int marker_size = xmp->marker_size;
int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name,
favor, changes, NULL, style,
marker_size);
result->ptr = xdl_malloc(size);
return -1;
}
result->size = size;
- xdl_fill_merge_buffer(xe1, name1, xe2, name2, favor, changes,
+ xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name, favor, changes,
result->ptr, style, marker_size);
}
return xdl_cleanup_merge(changes);
}
-int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
- mmfile_t *mf2, const char *name2,
- xmparam_t const *xmp, mmbuffer_t *result) {
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
xdchange_t *xscr1, *xscr2;
xdfenv_t xe1, xe2;
int status;
memcpy(result->ptr, mf1->ptr, mf1->size);
result->size = mf1->size;
} else {
- status = xdl_do_merge(&xe1, xscr1, name1,
- &xe2, xscr2, name2,
+ status = xdl_do_merge(&xe1, xscr1,
+ &xe2, xscr2,
xmp, result);
}
xdl_free_script(xscr1);