/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.
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
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
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::
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
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.
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>::
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
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
-------------
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],
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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
`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
LF='
'
# 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
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 $@+ $@
-e '}' \
-e 's|^import sys.*|&; \\\
import os; \\\
- sys.path[0] = os.environ.has_key("GITPYTHONLIB") and \\\
- os.environ["GITPYTHONLIB"] or \\\
- "@@INSTLIBDIR@@"|' \
+ sys.path.insert(0, os.getenv("GITPYTHONLIB",\
+ "@@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
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) {
* 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);
}
}
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,
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
#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);
}
#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()
};
/* 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
{
{
int i;
int decoration_style = 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);
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);
}
} 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 "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>...";
*/
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)
{
#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 */
}
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);
}
#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 */
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
-#!/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;
/* special case: only removal */
/* 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 (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
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;
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;
+}
# else
# define _XOPEN_SOURCE 500
# endif
-#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
+#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
+ !defined(_M_UNIX) && !defined(sgi) && !defined(__DragonFly__)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#endif
#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 char *xstrdup(const char *str);
* 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)
--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.
my $have_mail_address = eval { require Mail::Address; 1 };
my $smtp;
my $auth;
+my $mail_domain_default = "localhost.localdomain";
+my $mail_domain;
sub unique_email_list(@);
sub cleanup_compose_files();
# 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, $_);
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 = (
"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" => \$mail_domain,
"identity=s" => \$identity,
"annotate" => \$annotate,
"compose" => \$compose,
}
+# 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 maildomain_net
+{
+ my $maildomain;
+
+ if (eval { require Net::Domain; 1 }) {
+ my $domain = Net::Domain::domainname();
+ $maildomain = $domain
+ unless $^O eq 'darwin' && $domain =~ /\.local$/;
+ }
+
+ 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
+ unless $^O eq 'darwin' && $domain =~ /\.local$/;
+
+ last if $maildomain;
+ }
+ }
+ }
+
+ return $maildomain;
+}
+
+sub maildomain
+{
+ return maildomain_net() || maildomain_mta() || $mail_domain_default;
+}
+
# Returns 1 if the message was sent, and 0 otherwise.
# In actuality, the whole program dies when there
# is an error sending a message.
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);
+ $mail_domain ||= maildomain();
+ $smtp ||= Net::SMTP::SSL->new($smtp_server,
+ Hello => $mail_domain,
+ Port => $smtp_server_port);
}
else {
require Net::SMTP;
+ $mail_domain ||= maildomain();
$smtp ||= Net::SMTP->new((defined $smtp_server_port)
? "$smtp_server:$smtp_server_port"
- : $smtp_server);
+ : $smtp_server,
+ Hello => $mail_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 ",
+ "maildomain=$mail_domain",
+ defined $smtp_server_port ? "port=$smtp_server_port" : "";
}
if (defined $smtp_authuser) {
}
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=
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)
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
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)
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;
}
/*
#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. */
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",
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");
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))
}
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];
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_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_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 &&
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
--- /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 '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
--- /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_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 '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' '
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;
. ./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 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
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
'
#
# 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 () {
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):
{
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);
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;
}
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);