git-reflog
git-relink
git-remote
+git-remote-curl
git-repack
git-replace
git-repo-config
--- /dev/null
+GIT v1.6.4.3 Release Notes
+==========================
+
+Fixes since v1.6.4.2
+--------------------
+
+* "git clone" from an empty repository gave unnecessary error message,
+ even though it did everything else correctly.
+
+* "git cvsserver" invoked git commands via "git-foo" style, which has long
+ been deprecated.
+
+* "git fetch" and "git clone" had an extra sanity check to verify the
+ presense of the corresponding *.pack file before downloading *.idx
+ file by issuing a HEAD request. Github server however sometimes
+ gave 500 (Internal server error) response to HEAD even if a GET
+ request for *.pack file to the same URL would have succeeded, and broke
+ clone over HTTP from some of their repositories. As a workaround, this
+ verification has been removed (as it is not absolutely necessary).
+
+* "git grep" did not like relative pathname to refer outside the current
+ directory when run from a subdirectory.
+
+* an error message from "git push" was formatted in a very ugly way.
+
+* "git svn" did not quote the subversion user name correctly when
+ running its author-prog helper program.
+
+Other minor documentation updates are included.
GIT v1.6.5 Release Notes
========================
-In git 1.7.0, which is planned to be the release after 1.6.5, "git push"
-into a branch that is currently checked out will be refused by default.
+In git 1.7.0, which was planned to be the release after 1.6.5, "git
+push" into a branch that is currently checked out will be refused by
+default.
You can choose what should happen upon such a push by setting the
configuration variable receive.denyCurrentBranch in the receiving
outperforms the default fallback implementation we borrowed from
Mozzilla.
+ * Unnecessary inefficiency in deepening of a shallow repository has
+ been removed.
+
(usability, bells and whistles)
+ * Human writable date format to various options, e.g. --since=yesterday,
+ master@{2000.09.17}, are taught to infer some omitted input properly.
+
* refs/replace/ hierarchy is designed to be usable as a replacement
of the "grafts" mechanism, with the added advantage that it can be
transferred across repositories.
* "git am" handles input e-mail files that has CRLF line endings sensibly.
+ * "git am" learned "--scissors" option to allow you to discard early part
+ of an incoming e-mail.
+
+ * "git checkout", "git reset" and "git stash" learned to pick and
+ choose to use selected changes you made, similar to "git add -p".
+
+ * "git clone" learned a "-b" option to pick a HEAD to check out
+ different from the remote's default branch.
+
* "git commit --dry-run $args" is a new recommended way to ask "what would
happen if I try to commit with these arguments."
- * "git cvsimport" now supports password-protected pserver access.
+ * "git commit --dry-run" and "git status" shows conflicted paths in a
+ separate section to make them easier to spot during a merge.
+
+ * "git cvsimport" now supports password-protected pserver access even
+ when the password is not taken from ~/.cvspass file.
* "git fast-export" learned --no-data option that can be useful when
reordering commits and trees without touching the contents of
* informational output from "git reset" that lists the locally modified
paths is made consistent with that of "git checkout $another_branch".
- * "git status" gives more descriptive output for unmerged paths.
-
* "git submodule" learned to give submodule name to scripts run with
"foreach" subcommand.
tree vs the commit bound at submodule path, instead of comparing
the index.
+ * "git upload-pack", which is the server side support for "git clone" and
+ "git fetch", can call a new post-upload-pack hook for statistics purposes.
+
(developers)
* With GIT_TEST_OPTS="--root=/p/a/t/h", tests can be run outside the
--
exec >/var/tmp/1
-O=v1.6.4.1-266-g235db15
+O=v1.6.4.2-298-gdf01e7c
echo O=$(git describe master)
git shortlog --no-merges $O..master --not maint
in the appropriate manual page. You will find a description of non-core
porcelain configuration variables in the respective porcelain documentation.
+advice.*::
+ When set to 'true', display the given optional help message.
+ When set to 'false', do not display. The configuration variables
+ are:
++
+--
+ pushNonFastForward::
+ Advice shown when linkgit:git-push[1] refuses
+ non-fast-forward refs. Default: true.
+ statusHints::
+ Directions on how to stage/unstage/add shown in the
+ output of linkgit:git-status[1] and the template shown
+ when writing commit messages. Default: true.
+--
+
core.fileMode::
If false, the executable bit differences between the index and
the working copy are ignored; useful on broken filesystems like FAT.
never-before-seen repository on the site. When more than one
insteadOf strings match a given URL, the longest match is used.
+url.<base>.pushInsteadOf::
+ Any URL that starts with this value will not be pushed to;
+ instead, it will be rewritten to start with <base>, and the
+ resulting URL will be pushed to. In cases where some site serves
+ a large number of repositories, and serves them with multiple
+ access methods, some of which do not allow push, this feature
+ allows people to specify a pull-only URL and have git
+ automatically use an appropriate URL to push, even for a
+ never-before-seen repository on the site. When more than one
+ pushInsteadOf strings match a given URL, the longest match is
+ used. If a remote has an explicit pushurl, git will ignore this
+ setting for that remote.
+
user.email::
Your email address to be recorded in any newly created commits.
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
+ifndef::git-pull[]
-q::
--quiet::
Pass --quiet to git-fetch-pack and silence any other internally
-v::
--verbose::
Be verbose.
+endif::git-pull[]
-a::
--append::
[--3way] [--interactive] [--committer-date-is-author-date]
[--ignore-date] [--ignore-space-change | --ignore-whitespace]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
- [--reject] [-q | --quiet]
+ [--reject] [-q | --quiet] [--scissors | --no-scissors]
[<mbox> | <Maildir>...]
'git am' (--skip | --resolved | --abort)
--keep::
Pass `-k` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).
+-c::
+--scissors::
+ Remove everything in body before a scissors line (see
+ linkgit:git-mailinfo[1]).
+
+---no-scissors::
+ Ignore scissors lines (see linkgit:git-mailinfo[1]).
+
-q::
--quiet::
Be quiet. Only print error messages.
The "Subject: " line is supposed to concisely describe what the
commit is about in one line of text.
-"From: " and "Subject: " lines starting the body (the rest of the
-message after the blank line terminating the RFC2822 headers)
-override the respective commit author name and title values taken
-from the headers.
+"From: " and "Subject: " lines starting the body override the respective
+commit author name and title values taken from the headers.
The commit message is formed by the title taken from the
"Subject: ", a blank line and the body of the message up to
'git checkout' [-q] [-f] [-m] [<branch>]
'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
+'git checkout' --patch [<tree-ish>] [--] [<paths>...]
DESCRIPTION
-----------
branch`. As a convenience, --track without `-b` implies branch
creation; see the description of --track below.
-When <paths> are given, this command does *not* switch
+When <paths> or --patch are given, this command does *not* switch
branches. It updates the named paths in the working tree from
the index file, or from a named <tree-ish> (most often a commit). In
this case, the `-b` and `--track` options are meaningless and giving
"merge" (default) and "diff3" (in addition to what is shown by
"merge" style, shows the original contents).
+-p::
+--patch::
+ Interactively select hunks in the difference between the
+ <tree-ish> (or the index, if unspecified) and the working
+ tree. The chosen hunks are then applied in reverse to the
+ working tree (and if a <tree-ish> was specified, the index).
++
+This means that you can use `git checkout -p` to selectively discard
+edits from your current working tree.
+
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
objects from the source repository into a pack in the cloned repository.
--reference <repository>::
- If the reference repository is on the local machine
+ If the reference repository is on the local machine,
automatically setup .git/objects/info/alternates to
obtain objects from the reference repository. Using
an already existing repository as an alternate will
Instead of using the remote name 'origin' to keep track
of the upstream repository, use <name>.
+--branch <name>::
+-b <name>::
+ Instead of pointing the newly created HEAD to the branch pointed
+ to by the cloned repositoroy's HEAD, point to <name> branch
+ instead. In a non-bare repository, this is the branch that will
+ be checked out.
+
--upload-pack <upload-pack>::
-u <upload-pack>::
When given, and the repository to clone from is accessed
SYNOPSIS
--------
-'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] <msg> <patch>
+'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
DESCRIPTION
-n::
Disable all charset re-coding of the metadata.
+--scissors::
+ Remove everything in body before a scissors line. A line that
+ mainly consists of scissors (either ">8" or "8<") and perforation
+ (dash "-") marks is called a scissors line, and is used to request
+ the reader to cut the message at that line. If such a line
+ appears in the body of the message before the patch, everything
+ before it (including the scissors line itself) is ignored when
+ this option is used.
++
+This is useful if you want to begin your message in a discussion thread
+with comments and suggestions on the message you are responding to, and to
+conclude it with a patch submission, separating the discussion and the
+beginning of the proposed commit log message with a scissors line.
++
+This can enabled by default with the configuration option mailinfo.scissors.
+
+--no-scissors::
+ Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
+
<msg>::
The commit log message extracted from e-mail, usually
except the title line which comes from e-mail Subject.
SYNOPSIS
--------
[verse]
-'git quiltimport' [--dry-run] [--author <author>] [--patches <dir>]
+'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
DESCRIPTION
--- /dev/null
+git-remote-helpers(1)
+=====================
+
+NAME
+----
+git-remote-helpers - Helper programs for interoperation with remote git
+
+SYNOPSIS
+--------
+'git remote-<transport>' <remote>
+
+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.
+
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'capabilities'::
+ Lists the capabilities of the helper, one per line, ending
+ with a blank line.
+
+'list'::
+ Lists the refs, one per line, in the format "<value> <name>
+ [<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.
+
+'fetch' <sha1> <name>::
+ Fetches the given object, writing the necessary objects to the
+ database. Outputs a blank line when the fetch is
+ 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.
+
+If a fatal error occurs, the program writes the error message to
+stderr and exits. The caller should expect that a suitable error
+message has been printed if the child closes the connection without
+completing a valid response for the current command.
+
+Additional commands may be supported, as may be determined from
+capabilities reported by the helper.
+
+CAPABILITIES
+------------
+
+'fetch'::
+ This helper supports the 'fetch' command.
+
+REF LIST ATTRIBUTES
+-------------------
+
+None are defined yet, but the caller must accept any which are supplied.
+
+Documentation
+-------------
+Documentation by Daniel Barkalow.
+
+GIT
+---
+Part of the linkgit:git[1] suite
[verse]
'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
+'git reset' --patch [<commit>] [--] [<paths>...]
DESCRIPTION
-----------
If you want to undo a commit other than the latest on a branch,
linkgit:git-revert[1] is your friend.
-The second form with 'paths' is used to revert selected paths in
-the index from a given commit, without moving HEAD.
+The second and third forms with 'paths' and/or --patch are used to
+revert selected paths in the index from a given commit, without moving
+HEAD.
OPTIONS
and updates the files that are different between the named commit
and the current commit in the working tree.
+-p::
+--patch::
+ Interactively select hunks in the difference between the index
+ and <commit> (defaults to HEAD). The chosen hunks are applied
+ in reverse to the index.
++
+This means that `git reset -p` is the opposite of `git add -p` (see
+linkgit:git-add[1]).
+
-q::
Be quiet, only report errors.
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
-'git stash' [save [--keep-index] [-q|--quiet] [<message>]]
+'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
'git stash' clear
'git stash' create
OPTIONS
-------
-save [--keep-index] [-q|--quiet] [<message>]::
+save [--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::
Save your local modifications to a new 'stash', and run `git reset
- --hard` to revert them. This is the default action when no
- subcommand is given. The <message> part is optional and gives
- the description along with the stashed state.
+ --hard` to revert them. The <message> part is optional and gives
+ the description along with the stashed state. For quickly making
+ a snapshot, you can omit _both_ "save" and <message>, but giving
+ only <message> does not trigger this action to prevent a misspelled
+ subcommand from making an unwanted stash.
+
If the `--keep-index` option is used, all changes already added to the
index are left intact.
++
+With `--patch`, you can interactively select hunks from in the diff
+between HEAD and the working tree to be stashed. The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively. The selected changes are then rolled back
+from your worktree.
++
+The `--patch` option implies `--keep-index`. You can use
+`--no-keep-index` to override this.
list [<options>]::
program pair is meant to be used to pull updates from a remote
repository. For push operations, see 'git-send-pack'.
+After finishing the operation successfully, `post-upload-pack`
+hook is called (see linkgit:githooks[5]).
OPTIONS
-------
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.6.4.2/git.html[documentation for release 1.6.4.2]
+* link:v1.6.4.3/git.html[documentation for release 1.6.4.3]
* release notes for
+ link:RelNotes-1.6.4.3.txt[1.6.4.3],
link:RelNotes-1.6.4.2.txt[1.6.4.2],
link:RelNotes-1.6.4.1.txt[1.6.4.1],
link:RelNotes-1.6.4.txt[1.6.4].
This document describes the currently defined hooks.
+HOOKS
+-----
+
applypatch-msg
---------------
+~~~~~~~~~~~~~~
This hook is invoked by 'git-am' script. It takes a single
parameter, the name of the file that holds the proposed commit
'commit-msg' hook, if the latter is enabled.
pre-applypatch
---------------
+~~~~~~~~~~~~~~
This hook is invoked by 'git-am'. It takes no parameter, and is
invoked after the patch is applied, but before a commit is made.
'pre-commit' hook, if the latter is enabled.
post-applypatch
----------------
+~~~~~~~~~~~~~~~
This hook is invoked by 'git-am'. It takes no parameter,
and is invoked after the patch is applied and a commit is made.
the outcome of 'git-am'.
pre-commit
-----------
+~~~~~~~~~~
This hook is invoked by 'git-commit', and can be bypassed
with `\--no-verify` option. It takes no parameter, and is
to modify the commit message.
prepare-commit-msg
-------------------
+~~~~~~~~~~~~~~~~~~
This hook is invoked by 'git-commit' right after preparing the
default log message, and before the editor is started.
out the `Conflicts:` part of a merge's commit message.
commit-msg
-----------
+~~~~~~~~~~
This hook is invoked by 'git-commit', and can be bypassed
with `\--no-verify` option. It takes a single parameter, the
"Signed-off-by" lines, and aborts the commit if one is found.
post-commit
------------
+~~~~~~~~~~~
This hook is invoked by 'git-commit'. It takes no
parameter, and is invoked after a commit is made.
the outcome of 'git-commit'.
pre-rebase
-----------
+~~~~~~~~~~
This hook is called by 'git-rebase' and can be used to prevent a branch
from getting rebased.
post-checkout
------------
+~~~~~~~~~~~~~
This hook is invoked when a 'git-checkout' is run after having updated the
worktree. The hook is given three parameters: the ref of the previous HEAD,
properties.
post-merge
------------
+~~~~~~~~~~
This hook is invoked by 'git-merge', which happens when a 'git-pull'
is done on a local repository. The hook takes a single parameter, a status
[[pre-receive]]
pre-receive
------------
+~~~~~~~~~~~
This hook is invoked by 'git-receive-pack' on the remote repository,
which happens when a 'git-push' is done on a local repository.
[[update]]
update
-------
+~~~~~~
This hook is invoked by 'git-receive-pack' on the remote repository,
which happens when a 'git-push' is done on a local repository.
[[post-receive]]
post-receive
-------------
+~~~~~~~~~~~~
This hook is invoked by 'git-receive-pack' on the remote repository,
which happens when a 'git-push' is done on a local repository.
[[post-update]]
post-update
------------
+~~~~~~~~~~~
This hook is invoked by 'git-receive-pack' on the remote repository,
which happens when a 'git-push' is done on a local repository.
'git-send-pack' on the other end, so you can simply `echo` messages
for the user.
+post-upload-pack
+----------------
+
+After upload-pack successfully finishes its operation, this hook is called
+for logging purposes.
+
+The hook is passed various pieces of information, one per line, from its
+standard input. Currently the following items can be fed to the hook, but
+more types of information may be added in the future:
+
+want SHA-1::
+ 40-byte hexadecimal object name the client asked to include in the
+ resulting pack. Can occur one or more times in the input.
+
+have SHA-1::
+ 40-byte hexadecimal object name the client asked to exclude from
+ the resulting pack, claiming to have them already. Can occur zero
+ or more times in the input.
+
+time float::
+ Number of seconds spent for creating the packfile.
+
+size decimal::
+ Size of the resulting packfile in bytes.
+
+kind string:
+ Either "clone" (when the client did not give us any "have", and asked
+ for all our refs with "want"), or "fetch" (otherwise).
+
pre-auto-gc
------------
+~~~~~~~~~~~
This hook is invoked by 'git-gc --auto'. It takes no parameter, and
exiting with non-zero status from this script causes the 'git-gc --auto'
a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
+If you want to rewrite URLs for push only, you can create a
+configuration section of the form:
+
+------------
+ [url "<actual url base>"]
+ pushInsteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+ [url "ssh://example.org/"]
+ pushInsteadOf = git://example.org/
+------------
+
+a URL like "git://example.org/path/to/repo.git" will be rewritten to
+"ssh://example.org/path/to/repo.git" for pushes, but pulls will still
+use the original URL.
which are derived from $prefix, so "make all; make prefix=/usr
install" would not work.
+The beginning of the Makefile documents many variables that affect the way
+git is built. You can override them either from the command line, or in a
+config.mak file.
+
Alternatively you can use autoconf generated ./configure script to
set up install paths (via config.mak.autogen), so you can write instead
export GIT_EXEC_PATH PATH GITPERLLIB
- Git is reasonably self-sufficient, but does depend on a few external
- programs and libraries:
+ programs and libraries. Git can be used without most of them by adding
+ the approriate "NO_<LIBRARY>=YesPlease" to the make command line or
+ config.mak file.
- "zlib", the compression library. Git won't build without it.
- - "openssl". Unless you specify otherwise, you'll get the SHA1
- library from here.
+ - "ssh" is used to push and pull over the net.
- If you don't have openssl, you can use one of the SHA1 libraries
- that come with git (git includes the one from Mozilla, and has
- its own PowerPC and ARM optimized ones too - see the Makefile).
+ - A POSIX-compliant shell is required to run many scripts needed
+ for everyday use (e.g. "bisect", "pull").
- - libcurl library; git-http-fetch and git-fetch use them. You
- might also want the "curl" executable for debugging purposes.
- If you do not use http transfer, you are probably OK if you
- do not have them.
+ - "Perl" is needed to use some of the features (e.g. preparing a
+ partial commit using "git add -i/-p", interacting with svn
+ repositories with "git svn"). If you can live without these, use
+ NO_PERL.
- - expat library; git-http-push uses it for remote lock
- management over DAV. Similar to "curl" above, this is optional.
+ - "openssl" library is used by git-imap-send to use IMAP over SSL.
+ If you don't need it, use NO_OPENSSL.
- - "wish", the Tcl/Tk windowing shell is used in gitk to show the
- history graphically, and in git-gui.
+ By default, git uses OpenSSL for SHA1 but it will use it's own
+ library (inspired by Mozilla's) with either NO_OPENSSL or
+ BLK_SHA1. Also included is a version optimized for PowerPC
+ (PPC_SHA1).
+
+ - "libcurl" library is used by git-http-fetch and git-fetch. You
+ might also want the "curl" executable for debugging purposes.
+ If you do not use http:// or https:// repositories, you do not
+ have to have them (use NO_CURL).
- - "ssh" is used to push and pull over the net
+ - "expat" library; git-http-push uses it for remote lock
+ management over DAV. Similar to "curl" above, this is optional
+ (with NO_EXPAT).
- - "perl" and POSIX-compliant shells are needed to use most of
- the bare-bones Porcelainish scripts.
+ - "wish", the Tcl/Tk windowing shell is used in gitk to show the
+ history graphically, and in git-gui. If you don't want gitk or
+ git-gui, you can use NO_TCLTK.
- Some platform specific issues are dealt with Makefile rules,
but depending on your specific installation, you may not
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+# Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
+#
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
#
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
#
BUILT_INS += git-status$X
BUILT_INS += git-whatchanged$X
-# what 'all' will build and 'install' will install, in gitexecdir
+# what 'all' will build and 'install' will install in gitexecdir,
+# excluding programs for built-in commands
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
# what 'all' will build but not install in gitexecdir
LIB_FILE=libgit.a
XDIFF_LIB=xdiff/lib.a
+LIB_H += advice.h
LIB_H += archive.h
LIB_H += attr.h
LIB_H += blob.h
LIB_H += wt-status.h
LIB_OBJS += abspath.o
+LIB_OBJS += advice.o
LIB_OBJS += alias.o
LIB_OBJS += alloc.o
LIB_OBJS += archive.o
LIB_OBJS += tag.o
LIB_OBJS += trace.o
LIB_OBJS += transport.o
+LIB_OBJS += transport-helper.o
LIB_OBJS += tree-diff.o
LIB_OBJS += tree.o
LIB_OBJS += tree-walk.o
TAR = gtar
endif
ifeq ($(uname_S),Darwin)
+ NEEDS_CRYPTO_WITH_SSL = YesPlease
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
else
CURL_LIBCURL = -lcurl
endif
- BUILTIN_OBJS += builtin-http-fetch.o
- EXTLIBS += $(CURL_LIBCURL)
- LIB_OBJS += http.o http-walker.o
+ PROGRAMS += git-remote-curl$X git-http-fetch$X
curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "070908"
ifndef NO_EXPAT
else
OPENSSL_LINK =
endif
+ ifdef NEEDS_CRYPTO_WITH_SSL
+ OPENSSL_LINK += -lcrypto
+ endif
else
BASIC_CFLAGS += -DNO_OPENSSL
BLK_SHA1 = 1
QUIET_LINK = @echo ' ' LINK $@;
QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_LNCP = @echo ' ' LN/CP $@;
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
-http.o http-walker.o http-push.o transport.o: http.h
+http.o http-walker.o http-push.o: http.h
+http.o http-walker.o: $(LIB_H)
+
+git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS) $(CURL_LIBCURL)
git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)) git.o: $(LIB_H) $(wildcard */*.h)
builtin-revert.o wt-status.o: wt-status.h
--- /dev/null
+#include "cache.h"
+
+int advice_push_nonfastforward = 1;
+int advice_status_hints = 1;
+
+static struct {
+ const char *name;
+ int *preference;
+} advice_config[] = {
+ { "pushnonfastforward", &advice_push_nonfastforward },
+ { "statushints", &advice_status_hints },
+};
+
+int git_default_advice_config(const char *var, const char *value)
+{
+ const char *k = skip_prefix(var, "advice.");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
+ if (strcmp(k, advice_config[i].name))
+ continue;
+ *advice_config[i].preference = git_config_bool(var, value);
+ return 0;
+ }
+
+ return 0;
+}
--- /dev/null
+#ifndef ADVICE_H
+#define ADVICE_H
+
+extern int advice_push_nonfastforward;
+extern int advice_status_hints;
+
+int git_default_advice_config(const char *var, const char *value);
+
+#endif /* ADVICE_H */
return pathspec;
}
-int interactive_add(int argc, const char **argv, const char *prefix)
+int run_add_interactive(const char *revision, const char *patch_mode,
+ const char **pathspec)
{
- int status, ac;
+ int status, ac, pc = 0;
const char **args;
- const char **pathspec = NULL;
- if (argc) {
- pathspec = validate_pathspec(argc, argv, prefix);
- if (!pathspec)
- return -1;
- }
+ if (pathspec)
+ while (pathspec[pc])
+ pc++;
- args = xcalloc(sizeof(const char *), (argc + 4));
+ args = xcalloc(sizeof(const char *), (pc + 5));
ac = 0;
args[ac++] = "add--interactive";
- if (patch_interactive)
- args[ac++] = "--patch";
+ if (patch_mode)
+ args[ac++] = patch_mode;
+ if (revision)
+ args[ac++] = revision;
args[ac++] = "--";
- if (argc) {
- memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
- ac += argc;
+ if (pc) {
+ memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
+ ac += pc;
}
args[ac] = NULL;
return status;
}
+int interactive_add(int argc, const char **argv, const char *prefix)
+{
+ const char **pathspec = NULL;
+
+ if (argc) {
+ pathspec = validate_pathspec(argc, argv, prefix);
+ if (!pathspec)
+ return -1;
+ }
+
+ return run_add_interactive(NULL,
+ patch_interactive ? "--patch" : NULL,
+ pathspec);
+}
+
static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
out = open(file, O_CREAT | O_WRONLY, 0644);
if (out < 0)
die ("Could not open '%s' for writing.", file);
- rev.diffopt.file = fdopen(out, "w");
+ rev.diffopt.file = xfdopen(out, "w");
rev.diffopt.close_file = 1;
if (run_diff_files(&rev, 0))
die ("Could not write patch");
size_t imgoff = 0;
size_t preoff = 0;
size_t postlen = postimage->len;
- size_t imglen[preimage->nr];
for (i = 0; i < preimage->nr; i++) {
size_t prelen = preimage->line[i].len;
+ size_t imglen = img->line[try_lno+i].len;
- imglen[i] = img->line[try_lno+i].len;
- if (!fuzzy_matchlines(
- img->buf + try + imgoff, imglen[i],
- preimage->buf + preoff, prelen))
+ if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+ preimage->buf + preoff, prelen))
return 0;
if (preimage->line[i].flag & LINE_COMMON)
- postlen += imglen[i] - prelen;
- imgoff += imglen[i];
+ postlen += imglen - prelen;
+ imgoff += imglen;
preoff += prelen;
}
fixed_buf = xmalloc(imgoff);
memcpy(fixed_buf, img->buf + try, imgoff);
for (i = 0; i < preimage->nr; i++)
- preimage->line[i].len = imglen[i];
+ preimage->line[i].len = img->line[try_lno+i].len;
/*
* Update the preimage buffer and the postimage context lines.
/*
* Now, convert both name and e-mail using mailmap
*/
- if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+ if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
/* Add a trailing '>' to email, since map_user returns plain emails
Note: It already has '<', since we replace from mail+1 */
mailpos = memchr(mail, '\0', mail_len);
return git_xmerge_config(var, value, cb);
}
+static int interactive_checkout(const char *revision, const char **pathspec,
+ struct checkout_opts *opts)
+{
+ return run_add_interactive(revision, "--patch=checkout", pathspec);
+}
+
+
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
struct branch_info new;
struct tree *source_tree = NULL;
char *conflict_style = NULL;
+ int patch_mode = 0;
struct option options[] = {
OPT__QUIET(&opts.quiet),
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
OPT_STRING(0, "conflict", &conflict_style, "style",
"conflict style (merge or diff3)"),
+ OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
OPT_END(),
};
int has_dash_dash;
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ if (patch_mode && (opts.track > 0 || opts.new_branch
+ || opts.new_branch_log || opts.merge || opts.force))
+ die ("--patch is incompatible with all other options");
+
/* --track without -b should DWIM */
if (0 < opts.track && !opts.new_branch) {
const char *argv0 = argv[0];
if (!pathspec)
die("invalid path specification");
+ if (patch_mode)
+ return interactive_checkout(new.name, pathspec, &opts);
+
/* Checkout paths */
if (opts.new_branch) {
if (argc == 1) {
return checkout_paths(source_tree, pathspec, &opts);
}
+ if (patch_mode)
+ return interactive_checkout(new.name, NULL, &opts);
+
if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT;
if (strbuf_check_branch_ref(&buf, opts.new_branch))
static int option_local, option_no_hardlinks, option_shared, option_recursive;
static char *option_template, *option_reference, *option_depth;
static char *option_origin = NULL;
+static char *option_branch = NULL;
static char *option_upload_pack = "git-upload-pack";
static int option_verbose;
"reference repository"),
OPT_STRING('o', "origin", &option_origin, "branch",
"use <branch> instead of 'origin' to track upstream"),
+ OPT_STRING('b', "branch", &option_branch, "branch",
+ "checkout <branch> instead of the remote's HEAD"),
OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
"path to git-upload-pack on the remote"),
OPT_STRING(0, "depth", &option_depth, "depth",
die_errno("failed to create link '%s'", dest->buf);
option_no_hardlinks = 1;
}
- if (copy_file(dest->buf, src->buf, 0666))
+ if (copy_file_with_time(dest->buf, src->buf, 0666))
die_errno("failed to copy file to '%s'", dest->buf);
}
closedir(dir);
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir;
int dest_exists;
- const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
+ const struct ref *refs, *remote_head, *mapped_refs;
+ const struct ref *remote_head_points_at;
+ const struct ref *our_head_points_at;
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
struct transport *transport = NULL;
option_upload_pack);
refs = transport_get_remote_refs(transport);
- if(refs)
+ if (refs)
transport_fetch_refs(transport, refs);
}
mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
remote_head = find_ref_by_name(refs, "HEAD");
- head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+ remote_head_points_at =
+ guess_remote_head(remote_head, mapped_refs, 0);
+
+ if (option_branch) {
+ struct strbuf head = STRBUF_INIT;
+ strbuf_addstr(&head, src_ref_prefix);
+ strbuf_addstr(&head, option_branch);
+ our_head_points_at =
+ find_ref_by_name(mapped_refs, head.buf);
+ strbuf_release(&head);
+
+ if (!our_head_points_at) {
+ warning("Remote branch %s not found in "
+ "upstream %s, using HEAD instead",
+ option_branch, option_origin);
+ our_head_points_at = remote_head_points_at;
+ }
+ }
+ else
+ our_head_points_at = remote_head_points_at;
}
else {
warning("You appear to have cloned an empty repository.");
- head_points_at = NULL;
+ our_head_points_at = NULL;
+ remote_head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
if (!option_bare)
"refs/heads/master");
}
- if (head_points_at) {
- /* Local default branch link */
- create_symref("HEAD", head_points_at->name, NULL);
+ if (remote_head_points_at && !option_bare) {
+ struct strbuf head_ref = STRBUF_INIT;
+ strbuf_addstr(&head_ref, branch_top.buf);
+ strbuf_addstr(&head_ref, "HEAD");
+ create_symref(head_ref.buf,
+ remote_head_points_at->peer_ref->name,
+ reflog_msg.buf);
+ }
+ if (our_head_points_at) {
+ /* Local default branch link */
+ create_symref("HEAD", our_head_points_at->name, NULL);
if (!option_bare) {
- struct strbuf head_ref = STRBUF_INIT;
- const char *head = head_points_at->name;
-
- if (!prefixcmp(head, "refs/heads/"))
- head += 11;
-
- /* Set up the initial local branch */
-
- /* Local branch initial value */
+ const char *head = skip_prefix(our_head_points_at->name,
+ "refs/heads/");
update_ref(reflog_msg.buf, "HEAD",
- head_points_at->old_sha1,
+ our_head_points_at->old_sha1,
NULL, 0, DIE_ON_ERR);
-
- strbuf_addstr(&head_ref, branch_top.buf);
- strbuf_addstr(&head_ref, "HEAD");
-
- /* Remote branch link */
- create_symref(head_ref.buf,
- head_points_at->peer_ref->name,
- reflog_msg.buf);
-
install_branch_config(0, head, option_origin,
- head_points_at->name);
+ our_head_points_at->name);
}
} else if (remote_head) {
/* Source had detached HEAD pointing somewhere. */
- if (!option_bare)
+ if (!option_bare) {
update_ref(reflog_msg.buf, "HEAD",
remote_head->old_sha1,
NULL, REF_NODEREF, DIE_ON_ERR);
+ our_head_points_at = remote_head;
+ }
} else {
/* Nothing to checkout out */
if (!option_no_checkout)
option_no_checkout = 1;
}
- if (transport)
+ if (transport) {
transport_unlock_pack(transport);
+ transport_disconnect(transport);
+ }
if (!option_no_checkout) {
struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
opts.src_index = &the_index;
opts.dst_index = &the_index;
- tree = parse_tree_indirect(remote_head->old_sha1);
+ tree = parse_tree_indirect(our_head_points_at->old_sha1);
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
unpack_trees(1, &t, &opts);
for (ref = ref_map; ref; ref = ref->next) {
if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
- write_in_full(revlist.in, "\n", 1) < 0) {
+ write_str_in_full(revlist.in, "\n") < 0) {
if (errno != EPIPE && errno != EINVAL)
error("failed write to rev-list: %s", strerror(errno));
err = -1;
if (!prefixcmp(name, "refname"))
refname = ref->refname;
- else if(!prefixcmp(name, "upstream")) {
+ else if (!prefixcmp(name, "upstream")) {
struct branch *branch;
/* only local branches may have an upstream */
if (prefixcmp(ref->refname, "refs/heads/"))
#include "parse-options.h"
#include "userdiff.h"
#include "grep.h"
+#include "quote.h"
#ifndef NO_EXTERNAL_GREP
#ifdef __unix__
unsigned long size;
char *data;
enum object_type type;
- char *to_free = NULL;
int hit;
+ struct strbuf pathbuf = STRBUF_INIT;
data = read_sha1_file(sha1, &type, &size);
if (!data) {
return 0;
}
if (opt->relative && opt->prefix_length) {
- static char name_buf[PATH_MAX];
- char *cp;
- int name_len = strlen(name) - opt->prefix_length + 1;
-
- if (!tree_name_len)
- name += opt->prefix_length;
- else {
- if (ARRAY_SIZE(name_buf) <= name_len)
- cp = to_free = xmalloc(name_len);
- else
- cp = name_buf;
- memcpy(cp, name, tree_name_len);
- strcpy(cp + tree_name_len,
- name + tree_name_len + opt->prefix_length);
- name = cp;
- }
+ quote_path_relative(name + tree_name_len, -1, &pathbuf, opt->prefix);
+ strbuf_insert(&pathbuf, 0, name, tree_name_len);
+ name = pathbuf.buf;
}
hit = grep_buffer(opt, name, data, size);
+ strbuf_release(&pathbuf);
free(data);
- free(to_free);
return hit;
}
int i;
char *data;
size_t sz;
+ struct strbuf buf = STRBUF_INIT;
if (lstat(filename, &st) < 0) {
err_ret:
}
close(i);
if (opt->relative && opt->prefix_length)
- filename += opt->prefix_length;
+ filename = quote_path_relative(filename, -1, &buf, opt->prefix);
i = grep_buffer(opt, filename, data, sz);
+ strbuf_release(&buf);
free(data);
return i;
}
hit = external_grep(opt, paths, cached);
if (hit >= 0)
return hit;
+ hit = 0;
}
#endif
};
memset(&opt, 0, sizeof(opt));
+ opt.prefix = prefix;
opt.prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
opt.relative = 1;
opt.pathname = 1;
verify_filename(prefix, argv[j]);
}
- if (i < argc) {
+ if (i < argc)
paths = get_pathspec(prefix, argv + i);
- if (opt.prefix_length && opt.relative) {
- /* Make sure we do not get outside of paths */
- for (i = 0; paths[i]; i++)
- if (strncmp(prefix, paths[i], opt.prefix_length))
- die("git grep: cannot generate relative filenames containing '..'");
- }
- }
else if (prefix) {
paths = xcalloc(2, sizeof(const char *));
paths[0] = prefix;
+++ /dev/null
-#include "cache.h"
-#include "walker.h"
-
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
-{
- struct walker *walker;
- int commits_on_stdin = 0;
- int commits;
- const char **write_ref = NULL;
- char **commit_id;
- const char *url;
- char *rewritten_url = NULL;
- int arg = 1;
- int rc = 0;
- int get_tree = 0;
- int get_history = 0;
- int get_all = 0;
- int get_verbosely = 0;
- int get_recover = 0;
-
- git_config(git_default_config, NULL);
-
- while (arg < argc && argv[arg][0] == '-') {
- if (argv[arg][1] == 't') {
- get_tree = 1;
- } else if (argv[arg][1] == 'c') {
- get_history = 1;
- } else if (argv[arg][1] == 'a') {
- get_all = 1;
- get_tree = 1;
- get_history = 1;
- } else if (argv[arg][1] == 'v') {
- get_verbosely = 1;
- } else if (argv[arg][1] == 'w') {
- write_ref = &argv[arg + 1];
- arg++;
- } else if (!strcmp(argv[arg], "--recover")) {
- get_recover = 1;
- } else if (!strcmp(argv[arg], "--stdin")) {
- commits_on_stdin = 1;
- }
- arg++;
- }
- if (argc < arg + 2 - commits_on_stdin) {
- usage("git http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
- return 1;
- }
- if (commits_on_stdin) {
- commits = walker_targets_stdin(&commit_id, &write_ref);
- } else {
- commit_id = (char **) &argv[arg++];
- commits = 1;
- }
- url = argv[arg];
- if (url && url[strlen(url)-1] != '/') {
- rewritten_url = xmalloc(strlen(url)+2);
- strcpy(rewritten_url, url);
- strcat(rewritten_url, "/");
- url = rewritten_url;
- }
-
- walker = get_http_walker(url, NULL);
- walker->get_tree = get_tree;
- walker->get_history = get_history;
- walker->get_all = get_all;
- walker->get_verbosely = get_verbosely;
- walker->get_recover = get_recover;
-
- rc = walker_fetch(walker, commits, commit_id, write_ref, url);
-
- if (commits_on_stdin)
- walker_targets_free(commits, commit_id, write_ref);
-
- if (walker->corrupt_object_found) {
- fprintf(stderr,
-"Some loose object were found to be corrupt, but they might be just\n"
-"a false '404 Not Found' error message sent with incorrect HTTP\n"
-"status code. Suggest running 'git fsck'.\n");
- }
-
- walker_free(walker);
-
- free(rewritten_url);
-
- return rc;
-}
static struct strbuf charset = STRBUF_INIT;
static int patch_lines;
static struct strbuf **p_hdr_data, **s_hdr_data;
+static int use_scissors;
#define MAX_HDR_PARSED 10
#define MAX_BOUNDARIES 5
return 0;
}
+static int is_scissors_line(const struct strbuf *line)
+{
+ size_t i, len = line->len;
+ int scissors = 0, gap = 0;
+ int first_nonblank = -1;
+ int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+ const char *buf = line->buf;
+
+ for (i = 0; i < len; i++) {
+ if (isspace(buf[i])) {
+ if (in_perforation) {
+ perforation++;
+ gap++;
+ }
+ continue;
+ }
+ last_nonblank = i;
+ if (first_nonblank < 0)
+ first_nonblank = i;
+ if (buf[i] == '-') {
+ in_perforation = 1;
+ perforation++;
+ continue;
+ }
+ if (i + 1 < len &&
+ (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2))) {
+ in_perforation = 1;
+ perforation += 2;
+ scissors += 2;
+ i++;
+ continue;
+ }
+ in_perforation = 0;
+ }
+
+ /*
+ * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+ * Even though there can be arbitrary cruft on the same line
+ * (e.g. "cut here"), in order to avoid misidentification, the
+ * perforation must occupy more than a third of the visible
+ * width of the line, and dashes and scissors must occupy more
+ * than half of the perforation.
+ */
+
+ visible = last_nonblank - first_nonblank + 1;
+ return (scissors && 8 <= visible &&
+ visible < perforation * 3 &&
+ gap * 2 < perforation);
+}
+
static int handle_commit_msg(struct strbuf *line)
{
static int still_looking = 1;
strbuf_ltrim(line);
if (!line->len)
return 0;
- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
+ still_looking = check_header(line, s_hdr_data, 0);
+ if (still_looking)
return 0;
}
if (metainfo_charset)
convert_to_utf8(line, charset.buf);
+ if (use_scissors && is_scissors_line(line)) {
+ int i;
+ rewind(cmitmsg);
+ ftruncate(fileno(cmitmsg), 0);
+ still_looking = 1;
+
+ /*
+ * We may have already read "secondary headers"; purge
+ * them to give ourselves a clean restart.
+ */
+ for (i = 0; header[i]; i++) {
+ if (s_hdr_data[i])
+ strbuf_release(s_hdr_data[i]);
+ s_hdr_data[i] = NULL;
+ }
+ return 0;
+ }
+
if (patchbreak(line)) {
fclose(cmitmsg);
cmitmsg = NULL;
fprintf(fout, "\n");
}
-static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
- const char *msg, const char *patch)
+static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
{
int peek;
- keep_subject = ks;
- metainfo_charset = encoding;
fin = in;
fout = out;
return 0;
}
+static int git_mailinfo_config(const char *var, const char *value, void *unused)
+{
+ if (prefixcmp(var, "mailinfo."))
+ return git_default_config(var, value, unused);
+ if (!strcmp(var, "mailinfo.scissors")) {
+ use_scissors = git_config_bool(var, value);
+ return 0;
+ }
+ /* perhaps others here */
+ return 0;
+}
+
static const char mailinfo_usage[] =
- "git mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
+ "git mailinfo [-k] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
int cmd_mailinfo(int argc, const char **argv, const char *prefix)
{
/* NEEDSWORK: might want to do the optional .git/ directory
* discovery
*/
- git_config(git_default_config, NULL);
+ git_config(git_mailinfo_config, NULL);
def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8");
metainfo_charset = def_charset;
metainfo_charset = NULL;
else if (!prefixcmp(argv[1], "--encoding="))
metainfo_charset = argv[1] + 11;
+ else if (!strcmp(argv[1], "--scissors"))
+ use_scissors = 1;
+ else if (!strcmp(argv[1], "--no-scissors"))
+ use_scissors = 0;
else
usage(mailinfo_usage);
argc--; argv++;
if (argc != 3)
usage(mailinfo_usage);
- return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
+ return !!mailinfo(stdin, stdout, argv[1], argv[2]);
}
fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0)
die_errno("cannot open output file '%s'", name);
- output = fdopen(fd, "w");
+ output = xfdopen(fd, "w");
/* Copy it out, while searching for a line that begins with
* "From " and having something that looks like a date format.
it->pcache.tree_size = size;
}
+static void cleanup_preferred_base(void)
+{
+ struct pbase_tree *it;
+ unsigned i;
+
+ it = pbase_tree;
+ pbase_tree = NULL;
+ while (it) {
+ struct pbase_tree *this = it;
+ it = this->next;
+ free(this->pcache.tree_data);
+ free(this);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
+ if (!pbase_tree_cache[i])
+ continue;
+ free(pbase_tree_cache[i]->tree_data);
+ free(pbase_tree_cache[i]);
+ pbase_tree_cache[i] = NULL;
+ }
+
+ free(done_pbase_paths);
+ done_pbase_paths = NULL;
+ done_pbase_paths_num = done_pbase_paths_alloc = 0;
+}
+
static void check_object(struct object_entry *entry)
{
if (entry->in_pack) {
static void ll_find_deltas(struct object_entry **list, unsigned list_size,
int window, int depth, unsigned *processed)
{
- struct thread_params p[delta_search_threads];
+ struct thread_params *p;
int i, ret, active_threads = 0;
if (delta_search_threads <= 1) {
if (progress > pack_to_stdout)
fprintf(stderr, "Delta compression using up to %d threads.\n",
delta_search_threads);
+ p = xcalloc(delta_search_threads, sizeof(*p));
/* Partition the work amongst work threads. */
for (i = 0; i < delta_search_threads; i++) {
active_threads--;
}
}
+ free(p);
}
#else
static int git_pack_config(const char *k, const char *v, void *cb)
{
- if(!strcmp(k, "pack.window")) {
+ if (!strcmp(k, "pack.window")) {
window = git_config_int(k, v);
return 0;
}
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
+ cleanup_preferred_base();
if (include_tag && nr_result)
for_each_ref(add_ref_tag, NULL);
stop_progress(&progress_state);
continue;
error("failed to push some refs to '%s'", url[i]);
- if (nonfastforward) {
- printf("To prevent you from losing history, non-fast-forward updates were rejected.\n"
- "Merge the remote changes before pushing again.\n"
- "See 'non-fast forward' section of 'git push --help' for details.\n");
+ if (nonfastforward && advice_push_nonfastforward) {
+ printf("To prevent you from losing history, non-fast-forward updates were rejected\n"
+ "Merge the remote changes before pushing again. See the 'non-fast forward'\n"
+ "section of 'git push --help' for details.\n");
}
errs++;
}
} else if (cmd->updateref &&
(write_in_full(lock->lock_fd,
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
- write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+ write_str_in_full(lock->lock_fd, "\n") != 1 ||
close_ref(lock) < 0)) {
status |= error("Couldn't write %s",
lock->lk->filename);
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
- for(ref = matches; ref; ref = ref->next)
+ for (ref = matches; ref; ref = ref->next)
string_list_append(abbrev_branch(ref->name), &states->heads);
free_refs(fetch_map);
const char *symref;
strbuf_addf(&buf, "refs/remotes/%s", rename->old);
- if(!prefixcmp(refname, buf.buf)) {
+ if (!prefixcmp(refname, buf.buf)) {
item = string_list_append(xstrdup(refname), rename->remote_branches);
symref = resolve_ref(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
}
}
+static int interactive_reset(const char *revision, const char **argv,
+ const char *prefix)
+{
+ const char **pathspec = NULL;
+
+ if (*argv)
+ pathspec = get_pathspec(prefix, argv);
+
+ return run_add_interactive(revision, "--patch=reset", pathspec);
+}
+
static int read_from_tree(const char *prefix, const char **argv,
unsigned char *tree_sha1, int refresh_flags)
{
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
+ int patch_mode = 0;
const char *rev = "HEAD";
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
"reset HEAD, index and working tree", MERGE),
OPT_BOOLEAN('q', NULL, &quiet,
"disable showing new HEAD in hard reset and progress message"),
+ OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
OPT_END()
};
die("Could not parse object '%s'.", rev);
hashcpy(sha1, commit->object.sha1);
+ if (patch_mode) {
+ if (reset_type != NONE)
+ die("--patch is incompatible with --{hard,mixed,soft}");
+ return interactive_reset(rev, argv + i, prefix);
+ }
+
/* git reset tree [--] paths... can be used to
* load chosen paths from the tree into the index without
* affecting the working tree nor HEAD. */
/* copy author name to namebuf, to support matching on both name and email */
memcpy(namebuf, author, boemail - author);
len = boemail - author;
- while(len > 0 && isspace(namebuf[len-1]))
+ while (len > 0 && isspace(namebuf[len-1]))
len--;
namebuf[len] = 0;
rls.git_cmd = 1;
if (start_command(&rls))
return -1;
- rls_fout = fdopen(rls.out, "r");
+ rls_fout = xfdopen(rls.out, "r");
while (fgets(buffer, sizeof(buffer), rls_fout)) {
unsigned char sha1[20];
if (buffer[0] == '-') {
#include "git-compat-util.h"
#include "strbuf.h"
#include "hash.h"
+#include "advice.h"
#include SHA1_HEADER
#ifndef git_SHA_CTX
};
const char *show_date(unsigned long time, int timezone, enum date_mode mode);
+const char *show_date_relative(unsigned long time, int tz,
+ const struct timeval *now,
+ char *timebuf,
+ size_t timebuf_size);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
+unsigned long approxidate_relative(const char *date, const struct timeval *now);
enum date_mode parse_date_format(const char *format);
#define IDENT_WARN_ON_NO_NAME 1
extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode);
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+extern int copy_file_with_time(const char *dst, const char *src, int mode);
extern void write_or_die(int fd, const void *buf, size_t count);
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
extern void fsync_or_die(int fd, const char *);
+extern ssize_t read_in_full(int fd, void *buf, size_t count);
+extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+static inline ssize_t write_str_in_full(int fd, const char *str)
+{
+ return write_in_full(fd, str, strlen(str));
+}
+
/* pager.c */
extern void setup_pager(void);
extern const char *pager_program;
else {
if (write_in_full(fd, hex, 40) != 40)
break;
- if (write_in_full(fd, "\n", 1) != 1)
+ if (write_str_in_full(fd, "\n") != 1)
break;
}
}
int in_merge_bases(struct commit *, struct commit **, int);
extern int interactive_add(int argc, const char **argv, const char *prefix);
+extern int run_add_interactive(const char *revision, const char *patch_mode,
+ const char **pathspec);
static inline int single_parent(struct commit *commit)
{
free_path_split(path);
}
-char **copy_environ()
+static char **copy_environ(void)
{
char **env;
int i = 0;
/*
* If name contains '=', then sets the variable, otherwise it unsets it
*/
-char **env_setenv(char **env, const char *name)
+static char **env_setenv(char **env, const char *name)
{
char *eq = strchrnul(name, '=');
int i = lookup_env(env, name, eq-name);
return env;
}
+/*
+ * Copies global environ and adjusts variables as specified by vars.
+ */
+char **make_augmented_environ(const char *const *vars)
+{
+ char **env = copy_environ();
+
+ while (*vars)
+ env = env_setenv(env, *vars++);
+ return env;
+}
+
/* this is the first function to call into WS_32; initialize it */
#undef gethostbyname
struct hostent *mingw_gethostbyname(const char *host)
* helpers
*/
-char **copy_environ(void);
+char **make_augmented_environ(const char *const *vars);
void free_environ(char **env);
-char **env_setenv(char **env, const char *name);
/*
* A replacement of main() that ensures that argv[0] has a path
if (!prefixcmp(var, "mailmap."))
return git_default_mailmap_config(var, value);
+ if (!prefixcmp(var, "advice."))
+ return git_default_advice_config(var, value);
+
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
pager_use_color = git_config_bool(var,value);
return 0;
copy_end - copy_begin)
goto write_err_out;
if (new_line &&
- write_in_full(fd, "\n", 1) != 1)
+ write_str_in_full(fd, "\n") != 1)
goto write_err_out;
}
copy_begin = store.offset[i];
signal(SIGCHLD, SIG_DFL);
host = strstr(url, "://");
- if(host) {
+ if (host) {
*host = '\0';
protocol = get_protocol(url);
host += 3;
url.*.*)
local pfx="${cur%.*}."
cur="${cur##*.}"
- __gitcomp "insteadof" "$pfx" "$cur"
+ __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
return
;;
esac
(defun git-add-file ()
"Add marked file(s) to the index cache."
(interactive)
- (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
+ (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored 'unmerged))))
;; FIXME: add support for directories
(unless files
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
(when buffer (with-current-buffer buffer (revert-buffer t t t)))))
(git-success-message "Reverted" names))))))
-(defun git-resolve-file ()
- "Resolve conflicts in marked file(s)."
- (interactive)
- (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
- (when files
- (when (apply 'git-call-process-display-error "update-index" "--" files)
- (git-update-status-files files)
- (git-success-message "Resolved" files)))))
-
(defun git-remove-handled ()
"Remove handled files from the status list."
(interactive)
(define-key map "P" 'git-prev-unmerged-file)
(define-key map "q" 'git-status-quit)
(define-key map "r" 'git-remove-file)
- (define-key map "R" 'git-resolve-file)
(define-key map "t" toggle-map)
(define-key map "T" 'git-toggle-all-marks)
(define-key map "u" 'git-unmark-file)
("Merge"
["Next Unmerged File" git-next-unmerged-file t]
["Prev Unmerged File" git-prev-unmerged-file t]
- ["Mark as Resolved" git-resolve-file t]
["Interactive Merge File" git-find-file-imerge t]
["Diff Against Common Base File" git-diff-file-base t]
["Diff Combined" git-diff-file-combined t]
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
#
-import optparse, sys, os, marshal, popen2, subprocess, shelve
-import tempfile, getopt, sha, os.path, time, platform
+import optparse, sys, os, marshal, subprocess, shelve
+import tempfile, getopt, os.path, time, platform
import re
-from sets import Set;
-
verbose = False
self.usage += " //depot/path[@revRange]"
self.silent = False
- self.createdBranches = Set()
- self.committedChanges = Set()
+ self.createdBranches = set()
+ self.committedChanges = set()
self.branch = ""
self.detectBranches = False
self.detectLabels = False
if len(self.changesFile) > 0:
output = open(self.changesFile).readlines()
- changeSet = Set()
+ changeSet = set()
for line in output:
changeSet.add(int(line))
return 0;
}
+static int copy_times(const char *dst, const char *src)
+{
+ struct stat st;
+ struct utimbuf times;
+ if (stat(src, &st) < 0)
+ return -1;
+ times.actime = st.st_atime;
+ times.modtime = st.st_mtime;
+ if (utime(dst, ×) < 0)
+ return -1;
+ return 0;
+}
+
int copy_file(const char *dst, const char *src, int mode)
{
int fdi, fdo, status;
return status;
}
+
+int copy_file_with_time(const char *dst, const char *src, int mode)
+{
+ int status = copy_file(dst, src, mode);
+ if (!status)
+ return copy_times(dst, src);
+ return status;
+}
return -1;
if (month < 2 || (year + 2) % 4)
day--;
+ if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
+ return -1;
return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
}
return offset * eastwest;
}
+const char *show_date_relative(unsigned long time, int tz,
+ const struct timeval *now,
+ char *timebuf,
+ size_t timebuf_size)
+{
+ unsigned long diff;
+ if (now->tv_sec < time)
+ return "in the future";
+ diff = now->tv_sec - time;
+ if (diff < 90) {
+ snprintf(timebuf, timebuf_size, "%lu seconds ago", diff);
+ return timebuf;
+ }
+ /* Turn it into minutes */
+ diff = (diff + 30) / 60;
+ if (diff < 90) {
+ snprintf(timebuf, timebuf_size, "%lu minutes ago", diff);
+ return timebuf;
+ }
+ /* Turn it into hours */
+ diff = (diff + 30) / 60;
+ if (diff < 36) {
+ snprintf(timebuf, timebuf_size, "%lu hours ago", diff);
+ return timebuf;
+ }
+ /* We deal with number of days from here on */
+ diff = (diff + 12) / 24;
+ if (diff < 14) {
+ snprintf(timebuf, timebuf_size, "%lu days ago", diff);
+ return timebuf;
+ }
+ /* Say weeks for the past 10 weeks or so */
+ if (diff < 70) {
+ snprintf(timebuf, timebuf_size, "%lu weeks ago", (diff + 3) / 7);
+ return timebuf;
+ }
+ /* Say months for the past 12 months or so */
+ if (diff < 360) {
+ snprintf(timebuf, timebuf_size, "%lu months ago", (diff + 15) / 30);
+ return timebuf;
+ }
+ /* Give years and months for 5 years or so */
+ if (diff < 1825) {
+ unsigned long years = diff / 365;
+ unsigned long months = (diff % 365 + 15) / 30;
+ int n;
+ n = snprintf(timebuf, timebuf_size, "%lu year%s",
+ years, (years > 1 ? "s" : ""));
+ if (months)
+ snprintf(timebuf + n, timebuf_size - n,
+ ", %lu month%s ago",
+ months, (months > 1 ? "s" : ""));
+ else
+ snprintf(timebuf + n, timebuf_size - n, " ago");
+ return timebuf;
+ }
+ /* Otherwise, just years. Centuries is probably overkill. */
+ snprintf(timebuf, timebuf_size, "%lu years ago", (diff + 183) / 365);
+ return timebuf;
+}
+
const char *show_date(unsigned long time, int tz, enum date_mode mode)
{
struct tm *tm;
}
if (mode == DATE_RELATIVE) {
- unsigned long diff;
struct timeval now;
gettimeofday(&now, NULL);
- if (now.tv_sec < time)
- return "in the future";
- diff = now.tv_sec - time;
- if (diff < 90) {
- snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff);
- return timebuf;
- }
- /* Turn it into minutes */
- diff = (diff + 30) / 60;
- if (diff < 90) {
- snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff);
- return timebuf;
- }
- /* Turn it into hours */
- diff = (diff + 30) / 60;
- if (diff < 36) {
- snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff);
- return timebuf;
- }
- /* We deal with number of days from here on */
- diff = (diff + 12) / 24;
- if (diff < 14) {
- snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff);
- return timebuf;
- }
- /* Say weeks for the past 10 weeks or so */
- if (diff < 70) {
- snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7);
- return timebuf;
- }
- /* Say months for the past 12 months or so */
- if (diff < 360) {
- snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
- return timebuf;
- }
- /* Give years and months for 5 years or so */
- if (diff < 1825) {
- unsigned long years = diff / 365;
- unsigned long months = (diff % 365 + 15) / 30;
- int n;
- n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
- years, (years > 1 ? "s" : ""));
- if (months)
- snprintf(timebuf + n, sizeof(timebuf) - n,
- ", %lu month%s ago",
- months, (months > 1 ? "s" : ""));
- else
- snprintf(timebuf + n, sizeof(timebuf) - n,
- " ago");
- return timebuf;
- }
- /* Otherwise, just years. Centuries is probably overkill. */
- snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365);
- return timebuf;
+ return show_date_relative(time, tz, &now,
+ timebuf, sizeof(timebuf));
}
if (mode == DATE_LOCAL)
return end - date;
}
-/* Have we filled in any part of the time/date yet? */
+/*
+ * Have we filled in any part of the time/date yet?
+ * We just do a binary 'and' to see if the sign bit
+ * is set in all the values.
+ */
static inline int nodate(struct tm *tm)
{
- return tm->tm_year < 0 &&
- tm->tm_mon < 0 &&
- tm->tm_mday < 0 &&
- !(tm->tm_hour | tm->tm_min | tm->tm_sec);
+ return (tm->tm_year &
+ tm->tm_mon &
+ tm->tm_mday &
+ tm->tm_hour &
+ tm->tm_min &
+ tm->tm_sec) < 0;
}
/*
}
}
- if (num > 0 && num < 32) {
- tm->tm_mday = num;
- } else if (num > 0 && num < 13) {
+ if (num > 0 && num < 13 && tm->tm_mon < 0)
tm->tm_mon = num-1;
- }
return n;
}
tm.tm_mon = -1;
tm.tm_mday = -1;
tm.tm_isdst = -1;
+ tm.tm_hour = -1;
+ tm.tm_min = -1;
+ tm.tm_sec = -1;
offset = -1;
tm_gmt = 0;
date_string(now, offset, buf, bufsize);
}
-static void update_tm(struct tm *tm, unsigned long sec)
+/*
+ * Relative time update (eg "2 days ago"). If we haven't set the time
+ * yet, we need to set it from current time.
+ */
+static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
{
- time_t n = mktime(tm) - sec;
+ time_t n;
+
+ if (tm->tm_mday < 0)
+ tm->tm_mday = now->tm_mday;
+ if (tm->tm_mon < 0)
+ tm->tm_mon = now->tm_mon;
+ if (tm->tm_year < 0) {
+ tm->tm_year = now->tm_year;
+ if (tm->tm_mon > now->tm_mon)
+ tm->tm_year--;
+ }
+
+ n = mktime(tm) - sec;
localtime_r(&n, tm);
+ return n;
}
-static void date_yesterday(struct tm *tm, int *num)
+static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
- update_tm(tm, 24*60*60);
+ update_tm(tm, now, 24*60*60);
}
-static void date_time(struct tm *tm, int hour)
+static void date_time(struct tm *tm, struct tm *now, int hour)
{
if (tm->tm_hour < hour)
- date_yesterday(tm, NULL);
+ date_yesterday(tm, now, NULL);
tm->tm_hour = hour;
tm->tm_min = 0;
tm->tm_sec = 0;
}
-static void date_midnight(struct tm *tm, int *num)
+static void date_midnight(struct tm *tm, struct tm *now, int *num)
{
- date_time(tm, 0);
+ date_time(tm, now, 0);
}
-static void date_noon(struct tm *tm, int *num)
+static void date_noon(struct tm *tm, struct tm *now, int *num)
{
- date_time(tm, 12);
+ date_time(tm, now, 12);
}
-static void date_tea(struct tm *tm, int *num)
+static void date_tea(struct tm *tm, struct tm *now, int *num)
{
- date_time(tm, 17);
+ date_time(tm, now, 17);
}
-static void date_pm(struct tm *tm, int *num)
+static void date_pm(struct tm *tm, struct tm *now, int *num)
{
int hour, n = *num;
*num = 0;
tm->tm_hour = (hour % 12) + 12;
}
-static void date_am(struct tm *tm, int *num)
+static void date_am(struct tm *tm, struct tm *now, int *num)
{
int hour, n = *num;
*num = 0;
tm->tm_hour = (hour % 12);
}
-static void date_never(struct tm *tm, int *num)
+static void date_never(struct tm *tm, struct tm *now, int *num)
{
time_t n = 0;
localtime_r(&n, tm);
static const struct special {
const char *name;
- void (*fn)(struct tm *, int *);
+ void (*fn)(struct tm *, struct tm *, int *);
} special[] = {
{ "yesterday", date_yesterday },
{ "noon", date_noon },
{ NULL }
};
-static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
{
const struct typelen *tl;
const struct special *s;
for (s = special; s->name; s++) {
int len = strlen(s->name);
if (match_string(date, s->name) == len) {
- s->fn(tm, num);
+ s->fn(tm, now, num);
return end;
}
}
while (tl->type) {
int len = strlen(tl->type);
if (match_string(date, tl->type) >= len-1) {
- update_tm(tm, tl->length * *num);
+ update_tm(tm, now, tl->length * *num);
*num = 0;
return end;
}
n++;
diff += 7*n;
- update_tm(tm, diff * 24 * 60 * 60);
+ update_tm(tm, now, diff * 24 * 60 * 60);
return end;
}
}
if (match_string(date, "months") >= 5) {
- int n = tm->tm_mon - *num;
+ int n;
+ update_tm(tm, now, 0); /* fill in date fields if needed */
+ n = tm->tm_mon - *num;
*num = 0;
while (n < 0) {
n += 12;
}
if (match_string(date, "years") >= 4) {
+ update_tm(tm, now, 0); /* fill in date fields if needed */
tm->tm_year -= *num;
*num = 0;
return end;
return end;
}
-unsigned long approxidate(const char *date)
+/*
+ * Do we have a pending number at the end, or when
+ * we see a new one? Let's assume it's a month day,
+ * as in "Dec 6, 1992"
+ */
+static void pending_number(struct tm *tm, int *num)
+{
+ int number = *num;
+
+ if (number) {
+ *num = 0;
+ if (tm->tm_mday < 0 && number < 32)
+ tm->tm_mday = number;
+ else if (tm->tm_mon < 0 && number < 13)
+ tm->tm_mon = number-1;
+ else if (tm->tm_year < 0) {
+ if (number > 1969 && number < 2100)
+ tm->tm_year = number - 1900;
+ else if (number > 69 && number < 100)
+ tm->tm_year = number;
+ else if (number < 38)
+ tm->tm_year = 100 + number;
+ /* We screw up for number = 00 ? */
+ }
+ }
+}
+
+static unsigned long approxidate_str(const char *date, const struct timeval *tv)
{
int number = 0;
struct tm tm, now;
- struct timeval tv;
time_t time_sec;
- char buffer[50];
-
- if (parse_date(date, buffer, sizeof(buffer)) > 0)
- return strtoul(buffer, NULL, 10);
- gettimeofday(&tv, NULL);
- time_sec = tv.tv_sec;
+ time_sec = tv->tv_sec;
localtime_r(&time_sec, &tm);
now = tm;
+
+ tm.tm_year = -1;
+ tm.tm_mon = -1;
+ tm.tm_mday = -1;
+
for (;;) {
unsigned char c = *date;
if (!c)
break;
date++;
if (isdigit(c)) {
+ pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
continue;
}
if (isalpha(c))
- date = approxidate_alpha(date-1, &tm, &number);
+ date = approxidate_alpha(date-1, &tm, &now, &number);
}
- if (number > 0 && number < 32)
- tm.tm_mday = number;
- if (tm.tm_mon > now.tm_mon && tm.tm_year == now.tm_year)
- tm.tm_year--;
- return mktime(&tm);
+ pending_number(&tm, &number);
+ return update_tm(&tm, &now, 0);
+}
+
+unsigned long approxidate_relative(const char *date, const struct timeval *tv)
+{
+ char buffer[50];
+
+ if (parse_date(date, buffer, sizeof(buffer)) > 0)
+ return strtoul(buffer, NULL, 0);
+
+ return approxidate_str(date, tv);
+}
+
+unsigned long approxidate(const char *date)
+{
+ struct timeval tv;
+ char buffer[50];
+
+ if (parse_date(date, buffer, sizeof(buffer)) > 0)
+ return strtoul(buffer, NULL, 0);
+
+ gettimeofday(&tv, NULL);
+ return approxidate_str(date, &tv);
}
num = 0;
scale = 1;
dot = 0;
- for(;;) {
+ for (;;) {
ch = *cp;
if ( !dot && ch == '.' ) {
scale = 1;
# command line options
my $patch_mode;
+my $patch_mode_revision;
+
+sub apply_patch;
+sub apply_patch_for_checkout_commit;
+sub apply_patch_for_stash;
+
+my %patch_modes = (
+ 'stage' => {
+ DIFF => 'diff-files -p',
+ APPLY => sub { apply_patch 'apply --cached', @_; },
+ APPLY_CHECK => 'apply --cached',
+ VERB => 'Stage',
+ TARGET => '',
+ PARTICIPLE => 'staging',
+ FILTER => 'file-only',
+ },
+ 'stash' => {
+ DIFF => 'diff-index -p HEAD',
+ APPLY => sub { apply_patch 'apply --cached', @_; },
+ APPLY_CHECK => 'apply --cached',
+ VERB => 'Stash',
+ TARGET => '',
+ PARTICIPLE => 'stashing',
+ FILTER => undef,
+ },
+ 'reset_head' => {
+ DIFF => 'diff-index -p --cached',
+ APPLY => sub { apply_patch 'apply -R --cached', @_; },
+ APPLY_CHECK => 'apply -R --cached',
+ VERB => 'Unstage',
+ TARGET => '',
+ PARTICIPLE => 'unstaging',
+ FILTER => 'index-only',
+ },
+ 'reset_nothead' => {
+ DIFF => 'diff-index -R -p --cached',
+ APPLY => sub { apply_patch 'apply --cached', @_; },
+ APPLY_CHECK => 'apply --cached',
+ VERB => 'Apply',
+ TARGET => ' to index',
+ PARTICIPLE => 'applying',
+ FILTER => 'index-only',
+ },
+ 'checkout_index' => {
+ DIFF => 'diff-files -p',
+ APPLY => sub { apply_patch 'apply -R', @_; },
+ APPLY_CHECK => 'apply -R',
+ VERB => 'Discard',
+ TARGET => ' from worktree',
+ PARTICIPLE => 'discarding',
+ FILTER => 'file-only',
+ },
+ 'checkout_head' => {
+ DIFF => 'diff-index -p',
+ APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
+ APPLY_CHECK => 'apply -R',
+ VERB => 'Discard',
+ TARGET => ' from index and worktree',
+ PARTICIPLE => 'discarding',
+ FILTER => undef,
+ },
+ 'checkout_nothead' => {
+ DIFF => 'diff-index -R -p',
+ APPLY => sub { apply_patch_for_checkout_commit '', @_ },
+ APPLY_CHECK => 'apply',
+ VERB => 'Apply',
+ TARGET => ' to index and worktree',
+ PARTICIPLE => 'applying',
+ FILTER => undef,
+ },
+);
+
+my %patch_mode_flavour = %{$patch_modes{stage}};
sub run_cmd_pipe {
if ($^O eq 'MSWin32' || $^O eq 'msys') {
return if (!@tracked);
}
- my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+ my $reference;
+ if (defined $patch_mode_revision and $patch_mode_revision ne 'HEAD') {
+ $reference = $patch_mode_revision;
+ } elsif (is_initial_commit()) {
+ $reference = get_empty_tree();
+ } else {
+ $reference = 'HEAD';
+ }
for (run_cmd_pipe(qw(git diff-index --cached
--numstat --summary), $reference,
'--', @tracked)) {
print "\n";
}
+sub run_git_apply {
+ my $cmd = shift;
+ my $fh;
+ open $fh, '| git ' . $cmd;
+ print $fh @_;
+ return close $fh;
+}
+
sub parse_diff {
my ($path) = @_;
- my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
+ my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+ if (defined $patch_mode_revision) {
+ push @diff_cmd, $patch_mode_revision;
+ }
+ my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
my @colored = ();
if ($diff_use_color) {
- @colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
+ @colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
}
my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
or die "failed to open hunk edit file for writing: " . $!;
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
+ my $participle = $patch_mode_flavour{PARTICIPLE};
print $fh <<EOF;
# ---
# To remove '-' lines, make them ' ' lines (context).
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
-# marked for staging. If it does not apply cleanly, you will be given
+# marked for $participle. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.
EOF
sub diff_applies {
my $fh;
- open $fh, '| git apply --recount --cached --check';
- for my $h (@_) {
- print $fh @{$h->{TEXT}};
- }
- return close $fh;
+ return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --recount --check',
+ map { @{$_->{TEXT}} } @_);
}
sub _restore_terminal_and_die {
}
sub help_patch_cmd {
- print colored $help_color, <<\EOF ;
-y - stage this hunk
-n - do not stage this hunk
-q - quit, do not stage this hunk nor any of the remaining ones
-a - stage this and all the remaining hunks in the file
-d - do not stage this hunk nor any of the remaining hunks in the file
+ my $verb = lc $patch_mode_flavour{VERB};
+ my $target = $patch_mode_flavour{TARGET};
+ print colored $help_color, <<EOF ;
+y - $verb this hunk$target
+n - do not $verb this hunk$target
+q - quit, do not $verb this hunk nor any of the remaining ones
+a - $verb this and all the remaining hunks in the file
+d - do not $verb this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
EOF
}
+sub apply_patch {
+ my $cmd = shift;
+ my $ret = run_git_apply $cmd . ' --recount', @_;
+ if (!$ret) {
+ print STDERR @_;
+ }
+ return $ret;
+}
+
+sub apply_patch_for_checkout_commit {
+ my $reverse = shift;
+ my $applies_index = run_git_apply 'apply '.$reverse.' --cached --recount --check', @_;
+ my $applies_worktree = run_git_apply 'apply '.$reverse.' --recount --check', @_;
+
+ if ($applies_worktree && $applies_index) {
+ run_git_apply 'apply '.$reverse.' --cached --recount', @_;
+ run_git_apply 'apply '.$reverse.' --recount', @_;
+ return 1;
+ } elsif (!$applies_index) {
+ print colored $error_color, "The selected hunks do not apply to the index!\n";
+ if (prompt_yesno "Apply them to the worktree anyway? ") {
+ return run_git_apply 'apply '.$reverse.' --recount', @_;
+ } else {
+ print colored $error_color, "Nothing was applied.\n";
+ return 0;
+ }
+ } else {
+ print STDERR @_;
+ return 0;
+ }
+}
+
sub patch_update_cmd {
- my @all_mods = list_modified('file-only');
+ my @all_mods = list_modified($patch_mode_flavour{FILTER});
my @mods = grep { !($_->{BINARY}) } @all_mods;
my @them;
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
- print colored $prompt_color, 'Stage ',
- ($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'),
+ print colored $prompt_color, $patch_mode_flavour{VERB},
+ ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : ' this hunk'),
+ $patch_mode_flavour{TARGET},
" [y,n,q,a,d,/$other,?]? ";
my $line = prompt_single_character;
if ($line) {
if (@result) {
my $fh;
-
- open $fh, '| git apply --cached --recount';
- for (@{$head->{TEXT}}, @result) {
- print $fh $_;
- }
- if (!close $fh) {
- for (@{$head->{TEXT}}, @result) {
- print STDERR $_;
- }
- }
+ my @patch = (@{$head->{TEXT}}, @result);
+ my $apply_routine = $patch_mode_flavour{APPLY};
+ &$apply_routine(@patch);
refresh();
}
sub process_args {
return unless @ARGV;
my $arg = shift @ARGV;
- if ($arg eq "--patch") {
- $patch_mode = 1;
- $arg = shift @ARGV or die "missing --";
+ if ($arg =~ /--patch(?:=(.*))?/) {
+ if (defined $1) {
+ if ($1 eq 'reset') {
+ $patch_mode = 'reset_head';
+ $patch_mode_revision = 'HEAD';
+ $arg = shift @ARGV or die "missing --";
+ if ($arg ne '--') {
+ $patch_mode_revision = $arg;
+ $patch_mode = ($arg eq 'HEAD' ?
+ 'reset_head' : 'reset_nothead');
+ $arg = shift @ARGV or die "missing --";
+ }
+ } elsif ($1 eq 'checkout') {
+ $arg = shift @ARGV or die "missing --";
+ if ($arg eq '--') {
+ $patch_mode = 'checkout_index';
+ } else {
+ $patch_mode_revision = $arg;
+ $patch_mode = ($arg eq 'HEAD' ?
+ 'checkout_head' : 'checkout_nothead');
+ $arg = shift @ARGV or die "missing --";
+ }
+ } elsif ($1 eq 'stage' or $1 eq 'stash') {
+ $patch_mode = $1;
+ $arg = shift @ARGV or die "missing --";
+ } else {
+ die "unknown --patch mode: $1";
+ }
+ } else {
+ $patch_mode = 'stage';
+ $arg = shift @ARGV or die "missing --";
+ }
die "invalid argument $arg, expecting --"
unless $arg eq "--";
+ %patch_mode_flavour = %{$patch_modes{$patch_mode}};
}
elsif ($arg ne "--") {
die "invalid argument $arg, expecting --";
s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
k,keep pass -k flag to git-mailinfo
+c,scissors strip everything before a scissors line
whitespace= pass it through git-apply
ignore-space-change pass it through git-apply
ignore-whitespace pass it through git-apply
prec=4
dotest="$GIT_DIR/rebase-apply"
sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume=
+resolvemsg= resume= scissors=
git_apply_opt=
committer_date_is_author_date=
ignore_date=
utf8= ;;
-k|--keep)
keep=t ;;
+ -c|--scissors)
+ scissors=t ;;
+ --no-scissors)
+ scissors=f ;;
-r|--resolved)
resolved=t ;;
--skip)
--abort)
abort=t ;;
--rebasing)
- rebasing=t threeway=t keep=t ;;
+ rebasing=t threeway=t keep=t scissors=f ;;
-d|--dotest)
die "-d option is no longer supported. Do not use."
;;
split_patches "$@"
- # -s, -u, -k, --whitespace, -3, -C, -q and -p flags are kept
- # for the resuming session after a patch failure.
- # -i can and must be given when resuming.
+ # -i can and must be given when resuming; everything
+ # else is kept
echo " $git_apply_opt" >"$dotest/apply-opt"
echo "$threeway" >"$dotest/threeway"
echo "$sign" >"$dotest/sign"
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
+ echo "$scissors" >"$dotest/scissors"
echo "$GIT_QUIET" >"$dotest/quiet"
echo 1 >"$dotest/next"
if test -n "$rebasing"
then
keep=-k
fi
+case "$(cat "$dotest/scissors")" in
+t)
+ scissors=--scissors ;;
+f)
+ scissors=--no-scissors ;;
+esac
if test "$(cat "$dotest/quiet")" = t
then
GIT_QUIET=t
# by the user, or the user can tell us to do so by --resolved flag.
case "$resume" in
'')
- git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+ git mailinfo $keep $scissors $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
}
my $rr = ":pserver:$user\@$serv:$port$repo";
- unless ($pass) {
+ if ($pass) {
+ $pass = $self->_scramble($pass);
+ } else {
open(H,$ENV{'HOME'}."/.cvspass") and do {
# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
while (<H>) {
};
}
- $pass = $self->_scramble($pass);
-
my ($s, $rep);
if ($proxyhost) {
return 0;
}
- my @gitvars = `git-config -l`;
+ my @gitvars = `git config -l`;
if ($?) {
print "E problems executing git-config on the server -- this is not a git repository or the PATH is not set correctly.\n";
print "E \n";
# Save the file data in $state
$state->{entries}{$state->{directory}.$data}{modified_filename} = $filename;
$state->{entries}{$state->{directory}.$data}{modified_mode} = $mode;
- $state->{entries}{$state->{directory}.$data}{modified_hash} = `git-hash-object $filename`;
+ $state->{entries}{$state->{directory}.$data}{modified_hash} = `git hash-object $filename`;
$state->{entries}{$state->{directory}.$data}{modified_hash} =~ s/\s.*$//s;
#$log->debug("req_Modified : file=$data mode=$mode size=$size");
# do a checkout of the file if it is part of this tree
if ($wrev) {
- system('git-checkout-index', '-f', '-u', $filename);
+ system('git', 'checkout-index', '-f', '-u', $filename);
unless ($? == 0) {
die "Error running git-checkout-index -f -u $filename : $!";
}
{
$log->info("Removing file '$filename'");
unlink($filename);
- system("git-update-index", "--remove", $filename);
+ system("git", "update-index", "--remove", $filename);
}
elsif ( $addflag )
{
$log->info("Adding file '$filename'");
- system("git-update-index", "--add", $filename);
+ system("git", "update-index", "--add", $filename);
} else {
$log->info("Updating file '$filename'");
- system("git-update-index", $filename);
+ system("git", "update-index", $filename);
}
}
return;
}
- my $treehash = `git-write-tree`;
+ my $treehash = `git write-tree`;
chomp $treehash;
$log->debug("Treehash : $treehash, Parenthash : $parenthash");
}
close $msg_fh;
- my $commithash = `git-commit-tree $treehash -p $parenthash < $msg_filename`;
+ my $commithash = `git commit-tree $treehash -p $parenthash < $msg_filename`;
chomp($commithash);
$log->info("Commit hash : $commithash");
# TODO: if we got a revision from the client, use that instead
# to look up the commithash in sqlite (still good to default to
# the current head as we do now)
- system("git-read-tree", $lastseenin);
+ system("git", "read-tree", $lastseenin);
unless ($? == 0)
{
print "E error running git-read-tree $lastseenin $ENV{GIT_INDEX_FILE} $!\n";
$log->info("Created index '$ENV{GIT_INDEX_FILE}' with commit $lastseenin - exit status $?");
# do a checkout of the file
- system('git-checkout-index', '-f', '-u', $filename);
+ system('git', 'checkout-index', '-f', '-u', $filename);
unless ($? == 0) {
print "E error running git-checkout-index -f -u $filename : $!\n";
return;
close ANNOTATEHINTS
or (print "E failed to write $a_hints: $!\n"), return;
- my @cmd = (qw(git-annotate -l -S), $a_hints, $filename);
+ my @cmd = (qw(git annotate -l -S), $a_hints, $filename);
if (!open(ANNOTATE, "-|", @cmd)) {
print "E error invoking ". join(' ',@cmd) .": $!\n";
return;
die "Need filehash" unless ( defined ( $filehash ) and $filehash =~ /^[a-zA-Z0-9]{40}$/ );
- my $type = `git-cat-file -t $filehash`;
+ my $type = `git cat-file -t $filehash`;
chomp $type;
die ( "Invalid type '$type' (expected 'blob')" ) unless ( defined ( $type ) and $type eq "blob" );
- my $size = `git-cat-file -s $filehash`;
+ my $size = `git cat-file -s $filehash`;
chomp $size;
$log->debug("transmitfile($filehash) size=$size, type=$type");
- if ( open my $fh, '-|', "git-cat-file", "blob", $filehash )
+ if ( open my $fh, '-|', "git", "cat-file", "blob", $filehash )
{
if ( defined ( $options->{targetfile} ) )
{
push @git_log_params, $self->{module};
}
# git-rev-list is the backend / plumbing version of git-log
- open(GITLOG, '-|', 'git-rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
+ open(GITLOG, '-|', 'git', 'rev-list', @git_log_params) or die "Cannot call git-rev-list: $!";
my @commits;
next;
}
my $base = eval {
- safe_pipe_capture('git-merge-base',
+ safe_pipe_capture('git', 'merge-base',
$lastpicked, $parent);
};
# The two branches may not be related at all,
if ($base) {
my @merged;
# print "want to log between $base $parent \n";
- open(GITLOG, '-|', 'git-log', '--pretty=medium', "$base..$parent")
+ open(GITLOG, '-|', 'git', 'log', '--pretty=medium', "$base..$parent")
or die "Cannot call git-log: $!";
my $mergedhash;
while (<GITLOG>) {
if ( defined ( $lastpicked ) )
{
- my $filepipe = open(FILELIST, '-|', 'git-diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
+ my $filepipe = open(FILELIST, '-|', 'git', 'diff-tree', '-z', '-r', $lastpicked, $commit->{hash}) or die("Cannot call git-diff-tree : $!");
local ($/) = "\0";
while ( <FILELIST> )
{
# this is used to detect files removed from the repo
my $seen_files = {};
- my $filepipe = open(FILELIST, '-|', 'git-ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
+ my $filepipe = open(FILELIST, '-|', 'git', 'ls-tree', '-z', '-r', $commit->{hash}) or die("Cannot call git-ls-tree : $!");
local $/ = "\0";
while ( <FILELIST> )
{
return $message;
}
- my @lines = safe_pipe_capture("git-cat-file", "commit", $commithash);
+ my @lines = safe_pipe_capture("git", "cat-file", "commit", $commithash);
shift @lines while ( $lines[0] =~ /\S/ );
$message = join("",@lines);
$message .= " " if ( $message =~ /\n$/ );
# The tree must be really really clean.
if ! git update-index --ignore-submodules --refresh > /dev/null; then
echo >&2 "cannot rebase: you have unstaged changes"
- git diff --name-status -r --ignore-submodules -- >&2
+ git diff-files --name-status -r --ignore-submodules -- >&2
exit 1
fi
diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
or: $dashless drop [-q|--quiet] [<stash>]
or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
or: $dashless branch <branchname> [<stash>]
- or: $dashless [save [--keep-index] [-q|--quiet] [<message>]]
+ or: $dashless [save [-k|--keep-index] [-q|--quiet] [<message>]]
or: $dashless clear"
SUBDIRECTORY_OK=Yes
ref_stash=refs/stash
+if git config --get-colorbool color.interactive; then
+ help_color="$(git config --get-color color.interactive.help 'red bold')"
+ reset_color="$(git config --get-color '' reset)"
+else
+ help_color=
+ reset_color=
+fi
+
no_changes () {
git diff-index --quiet --cached HEAD --ignore-submodules -- &&
git diff-files --quiet --ignore-submodules
git commit-tree $i_tree -p $b_commit) ||
die "Cannot save the current index state"
- # state of the working tree
- w_tree=$( (
+ if test -z "$patch_mode"
+ then
+
+ # state of the working tree
+ w_tree=$( (
+ rm -f "$TMP-index" &&
+ cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
+ GIT_INDEX_FILE="$TMP-index" &&
+ export GIT_INDEX_FILE &&
+ git read-tree -m $i_tree &&
+ git add -u &&
+ git write-tree &&
+ rm -f "$TMP-index"
+ ) ) ||
+ die "Cannot save the current worktree state"
+
+ else
+
rm -f "$TMP-index" &&
- cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
- GIT_INDEX_FILE="$TMP-index" &&
- export GIT_INDEX_FILE &&
- git read-tree -m $i_tree &&
- git add -u &&
- git write-tree &&
- rm -f "$TMP-index"
- ) ) ||
+ GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
+
+ # find out what the user wants
+ GIT_INDEX_FILE="$TMP-index" \
+ git add--interactive --patch=stash -- &&
+
+ # state of the working tree
+ w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
die "Cannot save the current worktree state"
+ git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
+ test -s "$TMP-patch" ||
+ die "No changes selected"
+
+ rm -f "$TMP-index" ||
+ die "Cannot remove temporary index (can't happen)"
+
+ fi
+
# create the stash
if test -z "$stash_msg"
then
save_stash () {
keep_index=
+ patch_mode=
while test $# != 0
do
case "$1" in
- --keep-index)
+ -k|--keep-index)
+ keep_index=t
+ ;;
+ --no-keep-index)
+ keep_index=
+ ;;
+ -p|--patch)
+ patch_mode=t
keep_index=t
;;
-q|--quiet)
GIT_QUIET=t
;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "error: unknown option for 'stash save': $1"
+ usage
+ ;;
*)
break
;;
die "Cannot save the current status"
say Saved working directory and index state "$stash_msg"
- git reset --hard ${GIT_QUIET:+-q}
-
- if test -n "$keep_index" && test -n $i_tree
+ if test -z "$patch_mode"
then
- git read-tree --reset -u $i_tree
+ git reset --hard ${GIT_QUIET:+-q}
+
+ if test -n "$keep_index" && test -n $i_tree
+ then
+ git read-tree --reset -u $i_tree
+ fi
+ else
+ git apply -R < "$TMP-patch" ||
+ die "Cannot remove worktree changes"
+
+ if test -z "$keep_index"
+ then
+ git reset
+ fi
fi
}
drop_stash $stash
}
+# The default command is "save" if nothing but options are given
+seen_non_option=
+for opt
+do
+ case "$opt" in
+ -*) ;;
+ *) seen_non_option=t; break ;;
+ esac
+done
+
+test -n "$seen_non_option" || set "save" "$@"
+
# Main command set
case "$1" in
list)
apply_to_branch "$@"
;;
*)
- if test $# -eq 0
- then
+ case $# in
+ 0)
save_stash &&
say '(To restore them type "git stash apply")'
- else
+ ;;
+ *)
usage
- fi
+ esac
;;
esac
sub call_authors_prog {
my ($orig_author) = @_;
+ $orig_author = command_oneline('rev-parse', '--sq-quote', $orig_author);
my $author = `$::_authors_prog $orig_author`;
if ($? != 0) {
die "$::_authors_prog failed with exit code $?\n"
#include "run-command.h"
const char git_usage_string[] =
- "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
+ "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
+ " [-p|--paginate|--no-pager]\n"
+ " [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+ " [--help] COMMAND [ARGS]";
const char git_more_info_string[] =
"See 'git help COMMAND' for more information on a specific command.";
{ "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
{ "help", cmd_help },
-#ifndef NO_CURL
- { "http-fetch", cmd_http_fetch, RUN_SETUP },
-#endif
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },
{ "log", cmd_log, RUN_SETUP | USE_PAGER },
die_error(400, "Invalid snapshot format parameter");
} elsif (!exists($known_snapshot_formats{$format})) {
die_error(400, "Unknown snapshot format");
- } elsif (!grep($_ eq $format, @snapshot_fmts)) {
- die_error(403, "Unsupported snapshot format");
} elsif ($known_snapshot_formats{$format}{'disabled'}) {
die_error(403, "Snapshot format not allowed");
+ } elsif (!grep($_ eq $format, @snapshot_fmts)) {
+ die_error(403, "Unsupported snapshot format");
}
if (!defined $hash) {
struct grep_pat *pattern_list;
struct grep_pat **pattern_tail;
struct grep_expr *pattern_expression;
+ const char *prefix;
int prefix_length;
regex_t regexp;
int linenum;
--- /dev/null
+#include "cache.h"
+#include "walker.h"
+
+int main(int argc, const char **argv)
+{
+ const char *prefix;
+ struct walker *walker;
+ int commits_on_stdin = 0;
+ int commits;
+ const char **write_ref = NULL;
+ char **commit_id;
+ const char *url;
+ char *rewritten_url = NULL;
+ int arg = 1;
+ int rc = 0;
+ int get_tree = 0;
+ int get_history = 0;
+ int get_all = 0;
+ int get_verbosely = 0;
+ int get_recover = 0;
+
+ prefix = setup_git_directory();
+
+ git_config(git_default_config, NULL);
+
+ while (arg < argc && argv[arg][0] == '-') {
+ if (argv[arg][1] == 't') {
+ get_tree = 1;
+ } else if (argv[arg][1] == 'c') {
+ get_history = 1;
+ } else if (argv[arg][1] == 'a') {
+ get_all = 1;
+ get_tree = 1;
+ get_history = 1;
+ } else if (argv[arg][1] == 'v') {
+ get_verbosely = 1;
+ } else if (argv[arg][1] == 'w') {
+ write_ref = &argv[arg + 1];
+ arg++;
+ } else if (!strcmp(argv[arg], "--recover")) {
+ get_recover = 1;
+ } else if (!strcmp(argv[arg], "--stdin")) {
+ commits_on_stdin = 1;
+ }
+ arg++;
+ }
+ if (argc < arg + 2 - commits_on_stdin) {
+ usage("git http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
+ return 1;
+ }
+ if (commits_on_stdin) {
+ commits = walker_targets_stdin(&commit_id, &write_ref);
+ } else {
+ commit_id = (char **) &argv[arg++];
+ commits = 1;
+ }
+ url = argv[arg];
+ if (url && url[strlen(url)-1] != '/') {
+ rewritten_url = xmalloc(strlen(url)+2);
+ strcpy(rewritten_url, url);
+ strcat(rewritten_url, "/");
+ url = rewritten_url;
+ }
+
+ walker = get_http_walker(url, NULL);
+ walker->get_tree = get_tree;
+ walker->get_history = get_history;
+ walker->get_all = get_all;
+ walker->get_verbosely = get_verbosely;
+ walker->get_recover = get_recover;
+
+ rc = walker_fetch(walker, commits, commit_id, write_ref, url);
+
+ if (commits_on_stdin)
+ walker_targets_free(commits, commit_id, write_ref);
+
+ if (walker->corrupt_object_found) {
+ fprintf(stderr,
+"Some loose object were found to be corrupt, but they might be just\n"
+"a false '404 Not Found' error message sent with incorrect HTTP\n"
+"status code. Suggest running 'git fsck'.\n");
+ }
+
+ walker_free(walker);
+
+ free(rewritten_url);
+
+ return rc;
+}
char *url;
struct strbuf buf = STRBUF_INIT;
- /* Don't use the index if the pack isn't there */
- end_url_with_slash(&buf, base_url);
- strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
- url = strbuf_detach(&buf, 0);
-
- if (http_get_strbuf(url, NULL, 0)) {
- ret = error("Unable to verify pack %s is available",
- hex);
- goto cleanup;
- }
-
if (has_pack_index(sha1)) {
ret = 0;
goto cleanup;
int rc;
struct tree_desc t[3];
struct unpack_trees_options opts;
+ static const struct unpack_trees_error_msgs msgs = {
+ /* would_overwrite */
+ "Your local changes to '%s' would be overwritten by merge. Aborting.",
+ /* not_uptodate_file */
+ "Your local changes to '%s' would be overwritten by merge. Aborting.",
+ /* not_uptodate_dir */
+ "Updating '%s' would lose untracked files in it. Aborting.",
+ /* would_lose_untracked */
+ "Untracked working tree file '%s' would be %s by merge. Aborting",
+ /* bind_overlap -- will not happen here */
+ NULL,
+ };
memset(&opts, 0, sizeof(opts));
if (index_only)
opts.fn = threeway_merge;
opts.src_index = &the_index;
opts.dst_index = &the_index;
+ opts.msgs = msgs;
init_tree_desc_from_tree(t+0, common);
init_tree_desc_from_tree(t+1, head);
code = git_merge_trees(o->call_depth, common, head, merge);
- if (code != 0)
- die("merging of trees %s and %s failed",
- sha1_to_hex(head->object.sha1),
- sha1_to_hex(merge->object.sha1));
+ if (code != 0) {
+ if (show(o, 4) || o->call_depth)
+ die("merging of trees %s and %s failed",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+ else
+ exit(128);
+ }
if (unmerged_cache()) {
struct string_list *entries, *re_head, *re_merge;
} else {
int i = 1;
new = xmalloc(sizeof(struct llist_item) * BLKSIZE);
- for(;i < BLKSIZE; i++) {
+ for (; i < BLKSIZE; i++)
llist_item_put(&new[i]);
- }
}
return new;
}
static void llist_free(struct llist *list)
{
- while((list->back = list->front)) {
+ while ((list->back = list->front)) {
list->front = list->front->next;
llist_item_put(list->back);
}
if (cmp > 0) { /* we insert before this entry */
return llist_insert(list, prev, sha1);
}
- if(!cmp) { /* already exists */
+ if (!cmp) { /* already exists */
return l;
}
prev = l;
int cmp = hashcmp(l->sha1, sha1);
if (cmp > 0) /* not in list, since sorted */
return prev;
- if(!cmp) { /* found */
+ if (!cmp) { /* found */
if (prev == NULL) {
if (hint != NULL && hint != list->front) {
/* we don't know the previous element */
static inline size_t pack_list_size(struct pack_list *pl)
{
size_t ret = 0;
- while(pl) {
+ while (pl) {
ret++;
pl = pl->next;
}
return 0;
while ((subset = pl->next)) {
- while(subset) {
+ while (subset) {
ret += sizeof_union(pl->pack, subset->pack);
subset = subset->next;
}
pl = local_packs;
while (pl) {
- if(pl->unique_objects->size)
+ if (pl->unique_objects->size)
pack_list_insert(&unique, pl);
else
pack_list_insert(&non_unique, pl);
*min = min_perm;
/* add the unique packs to the list */
pl = unique;
- while(pl) {
+ while (pl) {
pack_list_insert(min, pl);
pl = pl->next;
}
struct pack_list *subset, *pl = local_packs;
while ((subset = pl)) {
- while((subset = subset->next))
+ while ((subset = subset->next))
cmp_two_packs(pl, subset);
pl = pl->next;
}
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
- if(!strcmp(arg, "--")) {
+ if (!strcmp(arg, "--")) {
i++;
break;
}
- if(!strcmp(arg, "--all")) {
+ if (!strcmp(arg, "--all")) {
load_all_packs = 1;
continue;
}
- if(!strcmp(arg, "--verbose")) {
+ if (!strcmp(arg, "--verbose")) {
verbose = 1;
continue;
}
- if(!strcmp(arg, "--alt-odb")) {
+ if (!strcmp(arg, "--alt-odb")) {
alt_odb = 1;
continue;
}
- if(*arg == '-')
+ if (*arg == '-')
usage(pack_redundant_usage);
else
break;
FD_ZERO(&in);
FD_SET(0, &in);
select(1, &in, NULL, &in, NULL);
-
- setenv("LESS", "FRSX", 0);
}
#endif
pager_argv[2] = pager;
pager_process.argv = pager_argv;
pager_process.in = -1;
+ if (!getenv("LESS")) {
+ static const char *env[] = { "LESS=FRSX", NULL };
+ pager_process.env = env;
+ }
#ifndef __MINGW32__
pager_process.preexec_cb = pager_preexec;
#endif
--- /dev/null
+#include "cache.h"
+#include "remote.h"
+#include "strbuf.h"
+#include "walker.h"
+#include "http.h"
+
+static struct ref *get_refs(struct walker *walker, const char *url)
+{
+ struct strbuf buffer = STRBUF_INIT;
+ char *data, *start, *mid;
+ char *ref_name;
+ char *refs_url;
+ int i = 0;
+ int http_ret;
+
+ struct ref *refs = NULL;
+ struct ref *ref = NULL;
+ struct ref *last_ref = NULL;
+
+ refs_url = xmalloc(strlen(url) + 11);
+ sprintf(refs_url, "%s/info/refs", url);
+
+ http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
+ switch (http_ret) {
+ case HTTP_OK:
+ break;
+ case HTTP_MISSING_TARGET:
+ die("%s not found: did you run git update-server-info on the"
+ " server?", refs_url);
+ default:
+ http_error(refs_url, http_ret);
+ die("HTTP request failed");
+ }
+
+ data = buffer.buf;
+ start = NULL;
+ mid = data;
+ while (i < buffer.len) {
+ if (!start) {
+ start = &data[i];
+ }
+ if (data[i] == '\t')
+ mid = &data[i];
+ if (data[i] == '\n') {
+ data[i] = 0;
+ ref_name = mid + 1;
+ ref = xmalloc(sizeof(struct ref) +
+ strlen(ref_name) + 1);
+ memset(ref, 0, sizeof(struct ref));
+ strcpy(ref->name, ref_name);
+ get_sha1_hex(start, ref->old_sha1);
+ if (!refs)
+ refs = ref;
+ if (last_ref)
+ last_ref->next = ref;
+ last_ref = ref;
+ start = NULL;
+ }
+ i++;
+ }
+
+ strbuf_release(&buffer);
+
+ ref = alloc_ref("HEAD");
+ if (!walker->fetch_ref(walker, ref) &&
+ !resolve_remote_symref(ref, refs)) {
+ ref->next = refs;
+ refs = ref;
+ } else {
+ free(ref);
+ }
+
+ strbuf_release(&buffer);
+ free(refs_url);
+ return refs;
+}
+
+int main(int argc, const char **argv)
+{
+ struct remote *remote;
+ struct strbuf buf = STRBUF_INIT;
+ const char *url;
+ struct walker *walker = NULL;
+
+ setup_git_directory();
+ if (argc < 2) {
+ fprintf(stderr, "Remote needed\n");
+ return 1;
+ }
+
+ remote = remote_get(argv[1]);
+
+ if (argc > 2) {
+ url = argv[2];
+ } else {
+ url = remote->url[0];
+ }
+
+ do {
+ if (strbuf_getline(&buf, stdin, '\n') == EOF)
+ break;
+ if (!prefixcmp(buf.buf, "fetch ")) {
+ char *obj = buf.buf + strlen("fetch ");
+ if (!walker)
+ walker = get_http_walker(url, remote);
+ walker->get_all = 1;
+ walker->get_tree = 1;
+ walker->get_history = 1;
+ walker->get_verbosely = 0;
+ walker->get_recover = 0;
+ if (walker_fetch(walker, 1, &obj, NULL, NULL))
+ die("Fetch failed.");
+ printf("\n");
+ fflush(stdout);
+ } else if (!strcmp(buf.buf, "list")) {
+ struct ref *refs;
+ struct ref *posn;
+ if (!walker)
+ walker = get_http_walker(url, remote);
+ refs = get_refs(walker, url);
+ for (posn = refs; posn; posn = posn->next) {
+ if (posn->symref)
+ printf("@%s %s\n", posn->symref, posn->name);
+ else
+ printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+ }
+ printf("\n");
+ fflush(stdout);
+ } else if (!strcmp(buf.buf, "capabilities")) {
+ printf("fetch\n");
+ printf("\n");
+ fflush(stdout);
+ } else {
+ return 1;
+ }
+ strbuf_reset(&buf);
+ } while (1);
+ return 0;
+}
int instead_of_nr;
int instead_of_alloc;
};
+struct rewrites {
+ struct rewrite **rewrite;
+ int rewrite_alloc;
+ int rewrite_nr;
+};
static struct remote **remotes;
static int remotes_alloc;
static const char *default_remote_name;
static int explicit_default_remote_name;
-static struct rewrite **rewrite;
-static int rewrite_alloc;
-static int rewrite_nr;
+static struct rewrites rewrites;
+static struct rewrites rewrites_push;
#define BUF_SIZE (2048)
static char buffer[BUF_SIZE];
-static const char *alias_url(const char *url)
+static const char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
char *ret;
longest = NULL;
longest_i = -1;
- for (i = 0; i < rewrite_nr; i++) {
- if (!rewrite[i])
+ for (i = 0; i < r->rewrite_nr; i++) {
+ if (!r->rewrite[i])
continue;
- for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
- if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+ for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
+ if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
(!longest ||
- longest->len < rewrite[i]->instead_of[j].len)) {
- longest = &(rewrite[i]->instead_of[j]);
+ longest->len < r->rewrite[i]->instead_of[j].len)) {
+ longest = &(r->rewrite[i]->instead_of[j]);
longest_i = i;
}
}
if (!longest)
return url;
- ret = xmalloc(rewrite[longest_i]->baselen +
+ ret = xmalloc(r->rewrite[longest_i]->baselen +
(strlen(url) - longest->len) + 1);
- strcpy(ret, rewrite[longest_i]->base);
- strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+ strcpy(ret, r->rewrite[longest_i]->base);
+ strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
return ret;
}
remote->url[remote->url_nr++] = url;
}
-static void add_url_alias(struct remote *remote, const char *url)
-{
- add_url(remote, alias_url(url));
-}
-
static void add_pushurl(struct remote *remote, const char *pushurl)
{
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
remote->pushurl[remote->pushurl_nr++] = pushurl;
}
+static void add_pushurl_alias(struct remote *remote, const char *url)
+{
+ const char *pushurl = alias_url(url, &rewrites_push);
+ if (pushurl != url)
+ add_pushurl(remote, pushurl);
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+ add_url(remote, alias_url(url, &rewrites));
+ add_pushurl_alias(remote, url);
+}
+
static struct remote *make_remote(const char *name, int len)
{
struct remote *ret;
return ret;
}
-static struct rewrite *make_rewrite(const char *base, int len)
+static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
{
struct rewrite *ret;
int i;
- for (i = 0; i < rewrite_nr; i++) {
+ for (i = 0; i < r->rewrite_nr; i++) {
if (len
- ? (len == rewrite[i]->baselen &&
- !strncmp(base, rewrite[i]->base, len))
- : !strcmp(base, rewrite[i]->base))
- return rewrite[i];
+ ? (len == r->rewrite[i]->baselen &&
+ !strncmp(base, r->rewrite[i]->base, len))
+ : !strcmp(base, r->rewrite[i]->base))
+ return r->rewrite[i];
}
- ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+ ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
ret = xcalloc(1, sizeof(struct rewrite));
- rewrite[rewrite_nr++] = ret;
+ r->rewrite[r->rewrite_nr++] = ret;
if (len) {
ret->base = xstrndup(base, len);
ret->baselen = len;
subkey = strrchr(name, '.');
if (!subkey)
return 0;
- rewrite = make_rewrite(name, subkey - name);
if (!strcmp(subkey, ".insteadof")) {
+ rewrite = make_rewrite(&rewrites, name, subkey - name);
+ if (!value)
+ return config_error_nonbool(key);
+ add_instead_of(rewrite, xstrdup(value));
+ } else if (!strcmp(subkey, ".pushinsteadof")) {
+ rewrite = make_rewrite(&rewrites_push, name, subkey - name);
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
{
int i, j;
for (i = 0; i < remotes_nr; i++) {
+ int add_pushurl_aliases;
if (!remotes[i])
continue;
- for (j = 0; j < remotes[i]->url_nr; j++) {
- remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
- }
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
- remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j]);
+ remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
+ }
+ add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
+ for (j = 0; j < remotes[i]->url_nr; j++) {
+ if (add_pushurl_aliases)
+ add_pushurl_alias(remotes[i], remotes[i]->url[j]);
+ remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
}
}
}
case 0:
if (!memcmp(dst_value, "refs/", 5))
matched_dst = make_linked_ref(dst_value, dst_tail);
- else if((dst_guess = guess_ref(dst_value, matched_src)))
+ else if ((dst_guess = guess_ref(dst_value, matched_src)))
matched_dst = make_linked_ref(dst_guess, dst_tail);
else
error("unable to push to unqualified destination: %s\n"
path = rr->items[i].string;
length = strlen(path) + 1;
if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
- write_in_full(out_fd, "\t", 1) != 1 ||
+ write_str_in_full(out_fd, "\t") != 1 ||
write_in_full(out_fd, path, length) != length)
die("unable to write rerere record");
}
if (cmd->dir)
die("chdir in start_command() not implemented");
- if (cmd->env) {
- env = copy_environ();
- for (; *cmd->env; cmd->env++)
- env = env_setenv(env, *cmd->env);
- }
+ if (cmd->env)
+ env = make_augmented_environ(cmd->env);
if (cmd->git_cmd) {
cmd->argv = prepare_git_cmd(cmd->argv);
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+gitweb_init () {
+ safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+ cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = 'current';
+our \$GIT = 'git';
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = 'projects';
+our \$site_name = '[localhost]';
+our \$site_header = '';
+our \$site_footer = '';
+our \$home_text = 'indextext.html';
+our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/gitweb.css');
+our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/git-logo.png';
+our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/git-favicon.png';
+our \$projects_list = '';
+our \$export_ok = '';
+our \$strict_export = '';
+
+EOF
+
+ cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+ GATEWAY_INTERFACE='CGI/1.1'
+ HTTP_ACCEPT='*/*'
+ REQUEST_METHOD='GET'
+ SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
+ QUERY_STRING=""$1""
+ PATH_INFO=""$2""
+ export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
+ SCRIPT_NAME QUERY_STRING PATH_INFO
+
+ GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+ export GITWEB_CONFIG
+
+ # some of git commands write to STDERR on error, but this is not
+ # written to web server logs, so we are not interested in that:
+ # we are interested only in properly formatted errors/warnings
+ rm -f gitweb.log &&
+ perl -- "$SCRIPT_NAME" \
+ >gitweb.output 2>gitweb.log &&
+ if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
+
+ # gitweb.log is left for debugging
+ # gitweb.output is used to parse http output
+}
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ say 'skipping gitweb tests, perl not available'
+ test_done
+fi
+
+perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ say 'skipping gitweb tests, perl version is too old'
+ test_done
+}
+
+gitweb_init
--- /dev/null
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ say 'skipping --patch tests, perl not available'
+ test_done
+fi
+
+set_state () {
+ echo "$3" > "$1" &&
+ git add "$1" &&
+ echo "$2" > "$1"
+}
+
+save_state () {
+ noslash="$(echo "$1" | tr / _)" &&
+ cat "$1" > _worktree_"$noslash" &&
+ git show :"$1" > _index_"$noslash"
+}
+
+set_and_save_state () {
+ set_state "$@" &&
+ save_state "$1"
+}
+
+verify_state () {
+ test "$(cat "$1")" = "$2" &&
+ test "$(git show :"$1")" = "$3"
+}
+
+verify_saved_state () {
+ noslash="$(echo "$1" | tr / _)" &&
+ verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
+}
+
+save_head () {
+ git rev-parse HEAD > _head
+}
+
+verify_saved_head () {
+ test "$(cat _head)" = "$(git rev-parse HEAD)"
+}
--- /dev/null
+#!/bin/sh
+
+test_description='test date parsing and printing'
+. ./test-lib.sh
+
+# arbitrary reference time: 2009-08-30 19:20:00
+TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
+
+check_show() {
+ t=$(($TEST_DATE_NOW - $1))
+ echo "$t -> $2" >expect
+ test_expect_${3:-success} "relative date ($2)" "
+ test-date show $t >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_show 5 '5 seconds ago'
+check_show 300 '5 minutes ago'
+check_show 18000 '5 hours ago'
+check_show 432000 '5 days ago'
+check_show 1728000 '3 weeks ago'
+check_show 13000000 '5 months ago'
+check_show 37500000 '1 year, 2 months ago'
+check_show 55188000 '1 year, 9 months ago'
+check_show 630000000 '20 years ago'
+
+check_parse() {
+ echo "$1 -> $2" >expect
+ test_expect_${3:-success} "parse date ($1)" "
+ test-date parse '$1' >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_parse 2008 bad
+check_parse 2008-02 bad
+check_parse 2008-02-14 bad
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+
+check_approxidate() {
+ echo "$1 -> $2 +0000" >expect
+ test_expect_${3:-success} "parse approxidate ($1)" "
+ test-date approxidate '$1' >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_approxidate now '2009-08-30 19:20:00'
+check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
+check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
+check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
+check_approxidate yesterday '2009-08-29 19:20:00'
+check_approxidate 3.days.ago '2009-08-27 19:20:00'
+check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
+check_approxidate 3.months.ago '2009-05-30 19:20:00'
+check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
+
+check_approxidate '6am yesterday' '2009-08-29 06:00:00'
+check_approxidate '6pm yesterday' '2009-08-29 18:00:00'
+check_approxidate '3:00' '2009-08-30 03:00:00'
+check_approxidate '15:00' '2009-08-30 15:00:00'
+check_approxidate 'noon today' '2009-08-30 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+
+check_approxidate 'last tuesday' '2009-08-25 19:20:00'
+check_approxidate 'July 5th' '2009-07-05 19:20:00'
+check_approxidate '06/05/2009' '2009-06-05 19:20:00'
+check_approxidate '06.05.2009' '2009-05-06 19:20:00'
+
+check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
+check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
+check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git checkout --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_and_save_state dir/foo work head &&
+ (echo n; echo n) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p' '
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p with staged changes' '
+ set_state dir/foo work index
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo index index
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
+ set_and_save_state dir/foo work head &&
+ (echo n; echo y; echo n) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
+ (echo n; echo y; echo y) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD with change already staged' '
+ set_state dir/foo index index
+ # the third n is to get out in case it mistakenly does not apply
+ (echo n; echo y; echo n) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD^' '
+ # the third n is to get out in case it mistakenly does not apply
+ (echo n; echo y; echo n) | git checkout -p HEAD^ &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent parent
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'path limiting works: dir' '
+ set_state dir/foo work head &&
+ (echo y; echo n) | git checkout -p dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: -- dir' '
+ set_state dir/foo work head &&
+ (echo y; echo n) | git checkout -p -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: HEAD^ -- dir' '
+ # the third n is to get out in case it mistakenly does not apply
+ (echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent parent
+'
+
+test_expect_success 'path limiting works: foo inside dir' '
+ set_state dir/foo work head &&
+ # the third n is to get out in case it mistakenly does not apply
+ (echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done
test ! -s output.out
'
+test_expect_success 'stash -k' '
+ echo bar3 > file &&
+ echo bar4 > file2 &&
+ git add file2 &&
+ git stash -k &&
+ test bar,bar4 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash --invalid-option' '
+ echo bar5 > file &&
+ echo bar6 > file2 &&
+ git add file2 &&
+ test_must_fail git stash --invalid-option &&
+ test_must_fail git stash save --invalid-option &&
+ test bar5,bar6 = $(cat file),$(cat file2) &&
+ git stash -- -message-starting-with-dash &&
+ test bar,bar2 = $(cat file),$(cat file2)
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git checkout --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ echo index > dir/foo &&
+ git add dir/foo &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before dir, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_state dir/foo work index
+ (echo n; echo n) | test_must_fail git stash save -p &&
+ verify_state dir/foo work index &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git stash -p' '
+ (echo n; echo y) | git stash save -p &&
+ verify_state dir/foo head index &&
+ verify_saved_state bar &&
+ git reset --hard &&
+ git stash apply &&
+ verify_state dir/foo work head &&
+ verify_state bar dummy dummy
+'
+
+test_expect_success 'git stash -p --no-keep-index' '
+ set_state dir/foo work index &&
+ set_state bar bar_work bar_index &&
+ (echo n; echo y) | git stash save -p --no-keep-index &&
+ verify_state dir/foo head head &&
+ verify_state bar bar_work dummy &&
+ git reset --hard &&
+ git stash apply --index &&
+ verify_state dir/foo work index &&
+ verify_state bar dummy bar_index
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done
'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
last=`cat last` &&
echo total is $last &&
- test `cat last` = 13'
+ test `cat last` = 14'
+
+check_mailinfo () {
+ mail=$1 opt=$2
+ mo="$mail$opt"
+ git mailinfo -u $opt msg$mo patch$mo <$mail >info$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/msg$mo msg$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/patch$mo patch$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/info$mo info$mo
+}
+
for mail in `echo 00*`
do
test_expect_success "mailinfo $mail" '
- git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
- echo msg &&
- test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
- echo patch &&
- test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
- echo info &&
- test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+ check_mailinfo $mail "" &&
+ if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
+ then
+ check_mailinfo $mail --scissors
+ fi
'
done
--- /dev/null
+Author: Junio Hamano
+Email: junkio@cox.net
+Subject: BLAH ONE
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
--- /dev/null
+Author: Junio C Hamano
+Email: gitster@pobox.com
+Subject: Teach mailinfo to ignore everything before -- >8 -- mark
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
--- /dev/null
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
--- /dev/null
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
--- /dev/null
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
--- /dev/null
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
Date: Fri, 9 Jun 2006 00:44:16 -0700
Subject: [PATCH] a patch
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio Hamano <junkio@cox.net>
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+Subject: Why doesn't git-am does not like >8 scissors mark?
+
+Subject: [PATCH] BLAH ONE
+
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
--- /dev/null
+#!/bin/sh
+
+test_description='post upload-hook'
+
+. ./test-lib.sh
+
+LOGFILE=".git/post-upload-pack-log"
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B &&
+ git reset --hard A &&
+ test_commit C &&
+ git branch prev B &&
+ mkdir -p .git/hooks &&
+ {
+ echo "#!$SHELL_PATH" &&
+ echo "cat >post-upload-pack-log"
+ } >".git/hooks/post-upload-pack" &&
+ chmod +x .git/hooks/post-upload-pack
+'
+
+test_expect_success initial '
+ rm -fr sub &&
+ git init sub &&
+ (
+ cd sub &&
+ git fetch --no-tags .. prev
+ ) &&
+ want=$(sed -n "s/^want //p" "$LOGFILE") &&
+ test "$want" = "$(git rev-parse --verify B)" &&
+ ! grep "^have " "$LOGFILE" &&
+ kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+ test "$kind" = fetch
+'
+
+test_expect_success second '
+ rm -fr sub &&
+ git init sub &&
+ (
+ cd sub &&
+ git fetch --no-tags .. prev:refs/remotes/prev &&
+ git fetch --no-tags .. master
+ ) &&
+ want=$(sed -n "s/^want //p" "$LOGFILE") &&
+ test "$want" = "$(git rev-parse --verify C)" &&
+ have=$(sed -n "s/^have //p" "$LOGFILE") &&
+ test "$have" = "$(git rev-parse --verify B)" &&
+ kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+ test "$kind" = fetch
+'
+
+test_expect_success all '
+ rm -fr sub &&
+ HERE=$(pwd) &&
+ git init sub &&
+ (
+ cd sub &&
+ git clone "file://$HERE/.git" new
+ ) &&
+ sed -n "s/^want //p" "$LOGFILE" | sort >actual &&
+ git rev-parse A B C | sort >expect &&
+ test_cmp expect actual &&
+ ! grep "^have " "$LOGFILE" &&
+ kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+ test "$kind" = clone
+'
+
+test_done
)
'
+test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
+ mk_empty &&
+ (
+ TRASH=$(pwd)/ &&
+ cd testrepo &&
+ git config "url.trash/.pushInsteadOf" "$TRASH" &&
+ git config remote.up.url "$TRASH." &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
test_expect_success 'push without wildcard' '
mk_empty &&
)
'
+test_expect_success 'push with pushInsteadOf' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.$TRASH.pushInsteadOf" trash/ &&
+ git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.trash2/.pushInsteadOf" trash/ &&
+ git config remote.r.url trash/wrong &&
+ git config remote.r.pushurl "$TRASH/testrepo" &&
+ git push r refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
test_expect_success 'push with matching heads' '
mk_test heads/master &&
(
cd src-0 && git init
) &&
- git clone src-0 target-6 &&
+ git clone "file://$(pwd)/src-0" target-6 2>err-6 &&
+ ! grep "fatal:" err-6 &&
(
cd src-0 && test_commit A
) &&
- git clone src-0 target-7 &&
+ git clone "file://$(pwd)/src-0" target-7 2>err-7 &&
+ ! grep "fatal:" err-7 &&
# There is no reason to insist they are bit-for-bit
# identical, but this test should suffice for now.
test_cmp target-6/.git/config target-7/.git/config
--- /dev/null
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+ echo refs/heads/"$1" >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+}
+
+check_file() {
+ echo "$1" >expect &&
+ test_cmp expect file
+}
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent && git init &&
+ echo one >file && git add file && git commit -m one &&
+ git checkout -b two &&
+ echo two >file && git add file && git commit -m two &&
+ git checkout master)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+ git clone parent clone &&
+ (cd clone &&
+ check_HEAD master &&
+ check_file one
+ )
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+ git clone -b two parent clone-two &&
+ (cd clone-two &&
+ check_HEAD two &&
+ check_file two
+ )
+'
+
+test_expect_success 'clone -b sets up tracking' '
+ (cd clone-two &&
+ echo origin >expect &&
+ git config branch.two.remote >actual &&
+ echo refs/heads/two >>expect &&
+ git config branch.two.merge >>actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+ (cd clone-two &&
+ echo refs/remotes/origin/master >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'clone -b with bogus branch chooses HEAD' '
+ git clone -b bogus parent clone-bogus &&
+ (cd clone-bogus &&
+ check_HEAD master &&
+ check_file one
+ )
+'
+
+test_done
test_cmp expected actual
'
+test_expect_success 'grep from a subdirectory to search wider area (1)' '
+ mkdir -p s &&
+ (
+ cd s && git grep "x x x" ..
+ )
+'
+
+test_expect_success 'grep from a subdirectory to search wider area (2)' '
+ mkdir -p s &&
+ (
+ cd s || exit 1
+ ( git grep xxyyzz .. >out ; echo $? >status )
+ ! test -s out &&
+ test 1 = $(cat status)
+ )
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git reset --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add dir &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_and_save_state dir/foo work work
+ (echo n; echo n) | git reset -p &&
+ verify_saved_state dir/foo &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p' '
+ (echo n; echo y) | git reset -p &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^' '
+ (echo n; echo y) | git reset -p HEAD^ &&
+ verify_state dir/foo work parent &&
+ verify_saved_state bar
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'git reset -p dir' '
+ set_state dir/foo work work
+ (echo y; echo n) | git reset -p dir &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p -- foo (inside dir)' '
+ set_state dir/foo work work
+ (echo y; echo n) | (cd dir && git reset -p -- foo) &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^ -- dir' '
+ (echo y; echo n) | git reset -p HEAD^ -- dir &&
+ verify_state dir/foo work parent &&
+ verify_saved_state bar
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+
+test_done
)
'
+git --git-dir=x/.git config --unset svn.authorsfile
+git --git-dir=x/.git config --unset svn.authorsprog
+
+test_expect_success 'authors-prog handled special characters in username' '
+ svn mkdir -m bad --username "xyz; touch evil" "$svnrepo"/bad &&
+ (
+ cd x &&
+ git svn --authors-prog=../svn-authors-prog fetch &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn |
+ grep "^author xyz; touch evil <xyz; touch evil@example\.com> " &&
+ ! test -f evil
+ )
+'
+
test_done
commandline, and checks that it would not write any errors
or warnings to log.'
-gitweb_init () {
- safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
- cat >gitweb_config.perl <<EOF
-#!/usr/bin/perl
-
-# gitweb configuration for tests
-
-our \$version = "current";
-our \$GIT = "git";
-our \$projectroot = "$safe_pwd";
-our \$project_maxdepth = 8;
-our \$home_link_str = "projects";
-our \$site_name = "[localhost]";
-our \$site_header = "";
-our \$site_footer = "";
-our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
-our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
-our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
-our \$projects_list = "";
-our \$export_ok = "";
-our \$strict_export = "";
-EOF
-
- cat >.git/description <<EOF
-$0 test repository
-EOF
-}
-
-gitweb_run () {
- GATEWAY_INTERFACE="CGI/1.1"
- HTTP_ACCEPT="*/*"
- REQUEST_METHOD="GET"
- SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
- QUERY_STRING=""$1""
- PATH_INFO=""$2""
- export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
- SCRIPT_NAME QUERY_STRING PATH_INFO
-
- GITWEB_CONFIG=$(pwd)/gitweb_config.perl
- export GITWEB_CONFIG
-
- # some of git commands write to STDERR on error, but this is not
- # written to web server logs, so we are not interested in that:
- # we are interested only in properly formatted errors/warnings
- rm -f gitweb.log &&
- perl -- "$SCRIPT_NAME" \
- >/dev/null 2>gitweb.log &&
- if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi
-
- # gitweb.log is left for debugging
-}
-
-. ./test-lib.sh
-
-if ! test_have_prereq PERL; then
- say 'skipping gitweb tests, perl not available'
- test_done
-fi
-
-perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
- say 'skipping gitweb tests, perl version is too old'
- test_done
-}
-
-gitweb_init
+. ./gitweb-lib.sh
# ----------------------------------------------------------------------
# no commits (empty, just initialized repository)
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (http status tests).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it returns the expected HTTP status
+code and message.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot settings
+
+test_commit \
+ 'SnapshotTests' \
+ 'i can has snapshot?'
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'override'} = 0;
+EOF
+
+test_expect_success \
+ 'snapshots: tgz only default format enabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+ grep "403 - Unsupported snapshot format" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+ grep "403 - Snapshot format not allowed" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "403 - Unsupported snapshot format" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'default'} = ['tgz','tbz2','txz','zip'];
+EOF
+
+test_expect_success \
+ 'snapshots: all enabled in default, use default disabled value' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+ grep "403 - Snapshot format not allowed" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'zip'}{'disabled'} = 1;
+EOF
+
+test_expect_success \
+ 'snapshots: zip explicitly disabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "403 - Snapshot format not allowed" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'tgz'}{'disabled'} = 0;
+EOF
+
+test_expect_success \
+ 'snapshots: tgz explicitly enabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+test_done
#include "cache.h"
-int main(int argc, char **argv)
+static const char *usage_msg = "\n"
+" test-date show [time_t]...\n"
+" test-date parse [date]...\n"
+" test-date approxidate [date]...\n";
+
+static void show_dates(char **argv, struct timeval *now)
{
- int i;
+ char buf[128];
+
+ for (; *argv; argv++) {
+ time_t t = atoi(*argv);
+ show_date_relative(t, 0, now, buf, sizeof(buf));
+ printf("%s -> %s\n", *argv, buf);
+ }
+}
- for (i = 1; i < argc; i++) {
+static void parse_dates(char **argv, struct timeval *now)
+{
+ for (; *argv; argv++) {
char result[100];
time_t t;
- memcpy(result, "bad", 4);
- parse_date(argv[i], result, sizeof(result));
+ result[0] = 0;
+ parse_date(*argv, result, sizeof(result));
t = strtoul(result, NULL, 0);
- printf("%s -> %s -> %s", argv[i], result, ctime(&t));
+ printf("%s -> %s\n", *argv,
+ t ? show_date(t, 0, DATE_ISO8601) : "bad");
+ }
+}
- t = approxidate(argv[i]);
- printf("%s -> %s\n", argv[i], ctime(&t));
+static void parse_approxidate(char **argv, struct timeval *now)
+{
+ for (; *argv; argv++) {
+ time_t t;
+ t = approxidate_relative(*argv, now);
+ printf("%s -> %s\n", *argv, show_date(t, 0, DATE_ISO8601));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct timeval now;
+ const char *x;
+
+ x = getenv("TEST_DATE_NOW");
+ if (x) {
+ now.tv_sec = atoi(x);
+ now.tv_usec = 0;
}
+ else
+ gettimeofday(&now, NULL);
+
+ argv++;
+ if (!*argv)
+ usage(usage_msg);
+ if (!strcmp(*argv, "show"))
+ show_dates(argv+1, &now);
+ else if (!strcmp(*argv, "parse"))
+ parse_dates(argv+1, &now);
+ else if (!strcmp(*argv, "approxidate"))
+ parse_approxidate(argv+1, &now);
+ else
+ usage(usage_msg);
return 0;
}
--- /dev/null
+#include "cache.h"
+#include "transport.h"
+
+#include "run-command.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+struct helper_data
+{
+ const char *name;
+ struct child_process *helper;
+ unsigned fetch : 1;
+};
+
+static struct child_process *get_helper(struct transport *transport)
+{
+ struct helper_data *data = transport->data;
+ struct strbuf buf = STRBUF_INIT;
+ struct child_process *helper;
+ FILE *file;
+
+ if (data->helper)
+ return data->helper;
+
+ helper = xcalloc(1, sizeof(*helper));
+ helper->in = -1;
+ helper->out = -1;
+ helper->err = 0;
+ helper->argv = xcalloc(4, sizeof(*helper->argv));
+ strbuf_addf(&buf, "remote-%s", data->name);
+ helper->argv[0] = strbuf_detach(&buf, NULL);
+ helper->argv[1] = transport->remote->name;
+ helper->argv[2] = transport->url;
+ helper->git_cmd = 1;
+ if (start_command(helper))
+ die("Unable to run helper: git %s", helper->argv[0]);
+ data->helper = helper;
+
+ write_str_in_full(helper->in, "capabilities\n");
+
+ file = xfdopen(helper->out, "r");
+ while (1) {
+ if (strbuf_getline(&buf, file, '\n') == EOF)
+ exit(128); /* child died, message supplied already */
+
+ if (!*buf.buf)
+ break;
+ if (!strcmp(buf.buf, "fetch"))
+ data->fetch = 1;
+ }
+ return data->helper;
+}
+
+static int disconnect_helper(struct transport *transport)
+{
+ struct helper_data *data = transport->data;
+ if (data->helper) {
+ write_str_in_full(data->helper->in, "\n");
+ close(data->helper->in);
+ finish_command(data->helper);
+ free((char *)data->helper->argv[0]);
+ free(data->helper->argv);
+ free(data->helper);
+ data->helper = NULL;
+ }
+ return 0;
+}
+
+static int fetch_with_fetch(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
+{
+ struct child_process *helper = get_helper(transport);
+ FILE *file = xfdopen(helper->out, "r");
+ int i;
+ struct strbuf buf = STRBUF_INIT;
+
+ for (i = 0; i < nr_heads; i++) {
+ const struct ref *posn = to_fetch[i];
+ if (posn->status & REF_STATUS_UPTODATE)
+ continue;
+
+ strbuf_addf(&buf, "fetch %s %s\n",
+ sha1_to_hex(posn->old_sha1), posn->name);
+ write_in_full(helper->in, buf.buf, buf.len);
+ strbuf_reset(&buf);
+
+ if (strbuf_getline(&buf, file, '\n') == EOF)
+ exit(128); /* child died, message supplied already */
+ }
+ return 0;
+}
+
+static int fetch(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
+{
+ struct helper_data *data = transport->data;
+ int i, count;
+
+ count = 0;
+ for (i = 0; i < nr_heads; i++)
+ if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
+ count++;
+
+ if (!count)
+ return 0;
+
+ if (data->fetch)
+ return fetch_with_fetch(transport, nr_heads, to_fetch);
+
+ return -1;
+}
+
+static struct ref *get_refs_list(struct transport *transport, int for_push)
+{
+ struct child_process *helper;
+ struct ref *ret = NULL;
+ struct ref **tail = &ret;
+ struct ref *posn;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *file;
+
+ helper = get_helper(transport);
+
+ write_str_in_full(helper->in, "list\n");
+
+ file = xfdopen(helper->out, "r");
+ while (1) {
+ char *eov, *eon;
+ if (strbuf_getline(&buf, file, '\n') == EOF)
+ exit(128); /* child died, message supplied already */
+
+ if (!*buf.buf)
+ break;
+
+ eov = strchr(buf.buf, ' ');
+ if (!eov)
+ die("Malformed response in ref list: %s", buf.buf);
+ eon = strchr(eov + 1, ' ');
+ *eov = '\0';
+ if (eon)
+ *eon = '\0';
+ *tail = alloc_ref(eov + 1);
+ if (buf.buf[0] == '@')
+ (*tail)->symref = xstrdup(buf.buf + 1);
+ else if (buf.buf[0] != '?')
+ get_sha1_hex(buf.buf, (*tail)->old_sha1);
+ tail = &((*tail)->next);
+ }
+ strbuf_release(&buf);
+
+ for (posn = ret; posn; posn = posn->next)
+ resolve_remote_symref(posn, ret);
+
+ return ret;
+}
+
+int transport_helper_init(struct transport *transport, const char *name)
+{
+ struct helper_data *data = xcalloc(sizeof(*data), 1);
+ data->name = name;
+
+ transport->data = data;
+ transport->get_refs_list = get_refs_list;
+ transport->fetch = fetch;
+ transport->disconnect = disconnect_helper;
+ return 0;
+}
#include "cache.h"
#include "transport.h"
#include "run-command.h"
-#ifndef NO_CURL
-#include "http.h"
-#endif
#include "pkt-line.h"
#include "fetch-pack.h"
#include "send-pack.h"
return result;
}
-/* Generic functions for using commit walkers */
-
-#ifndef NO_CURL /* http fetch is the only user */
-static int fetch_objs_via_walker(struct transport *transport,
- int nr_objs, const struct ref **to_fetch)
-{
- char *dest = xstrdup(transport->url);
- struct walker *walker = transport->data;
- char **objs = xmalloc(nr_objs * sizeof(*objs));
- int i;
-
- walker->get_all = 1;
- walker->get_tree = 1;
- walker->get_history = 1;
- walker->get_verbosely = transport->verbose >= 0;
- walker->get_recover = 0;
-
- for (i = 0; i < nr_objs; i++)
- objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
-
- if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
- die("Fetch failed.");
-
- for (i = 0; i < nr_objs; i++)
- free(objs[i]);
- free(objs);
- free(dest);
- return 0;
-}
-#endif /* NO_CURL */
-
-static int disconnect_walker(struct transport *transport)
-{
- struct walker *walker = transport->data;
- if (walker)
- walker_free(walker);
- return 0;
-}
-
#ifndef NO_CURL
static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
{
return !!run_command_v_opt(argv, RUN_GIT_CMD);
}
-static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
-{
- struct strbuf buffer = STRBUF_INIT;
- char *data, *start, *mid;
- char *ref_name;
- char *refs_url;
- int i = 0;
- int http_ret;
-
- struct ref *refs = NULL;
- struct ref *ref = NULL;
- struct ref *last_ref = NULL;
-
- struct walker *walker;
-
- if (for_push)
- return NULL;
-
- if (!transport->data)
- transport->data = get_http_walker(transport->url,
- transport->remote);
-
- walker = transport->data;
-
- refs_url = xmalloc(strlen(transport->url) + 11);
- sprintf(refs_url, "%s/info/refs", transport->url);
-
- http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
- switch (http_ret) {
- case HTTP_OK:
- break;
- case HTTP_MISSING_TARGET:
- die("%s not found: did you run git update-server-info on the"
- " server?", refs_url);
- default:
- http_error(refs_url, http_ret);
- die("HTTP request failed");
- }
-
- data = buffer.buf;
- start = NULL;
- mid = data;
- while (i < buffer.len) {
- if (!start)
- start = &data[i];
- if (data[i] == '\t')
- mid = &data[i];
- if (data[i] == '\n') {
- data[i] = 0;
- ref_name = mid + 1;
- ref = xmalloc(sizeof(struct ref) +
- strlen(ref_name) + 1);
- memset(ref, 0, sizeof(struct ref));
- strcpy(ref->name, ref_name);
- get_sha1_hex(start, ref->old_sha1);
- if (!refs)
- refs = ref;
- if (last_ref)
- last_ref->next = ref;
- last_ref = ref;
- start = NULL;
- }
- i++;
- }
-
- strbuf_release(&buffer);
-
- ref = alloc_ref("HEAD");
- if (!walker->fetch_ref(walker, ref) &&
- !resolve_remote_symref(ref, refs)) {
- ref->next = refs;
- refs = ref;
- } else {
- free(ref);
- }
-
- strbuf_release(&buffer);
- free(refs_url);
- return refs;
-}
-
-static int fetch_objs_via_curl(struct transport *transport,
- int nr_objs, const struct ref **to_fetch)
-{
- if (!transport->data)
- transport->data = get_http_walker(transport->url,
- transport->remote);
- return fetch_objs_via_walker(transport, nr_objs, to_fetch);
-}
-
#endif
struct bundle_transport_data {
} else if (!prefixcmp(url, "http://")
|| !prefixcmp(url, "https://")
|| !prefixcmp(url, "ftp://")) {
+ transport_helper_init(ret, "curl");
#ifdef NO_CURL
error("git was compiled without libcurl support.");
#else
- ret->get_refs_list = get_refs_via_curl;
- ret->fetch = fetch_objs_via_curl;
ret->push = curl_transport_push;
#endif
- ret->disconnect = disconnect_walker;
} else if (is_local(url) && is_file(url)) {
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
update_tracking_ref(transport->remote, ref, verbose);
}
- if (!ret && !refs_pushed(remote_refs))
+ if (!quiet && !ret && !refs_pushed(remote_refs))
fprintf(stderr, "Everything up-to-date\n");
return ret;
}
int transport_disconnect(struct transport *transport);
char *transport_anonymize_url(const char *url);
+/* Transport methods defined outside transport.c */
+int transport_helper_init(struct transport *transport, const char *name);
+
#endif
static int shallow_nr;
static struct object_array have_obj;
static struct object_array want_obj;
+static struct object_array extra_edge_obj;
static unsigned int timeout;
/* 0 for no sideband,
* otherwise maximum packet size (up to 65520 bytes).
int i;
struct rev_info revs;
- pack_pipe = fdopen(fd, "w");
+ pack_pipe = xfdopen(fd, "w");
init_revisions(&revs, NULL);
revs.tag_objects = 1;
revs.tree_objects = 1;
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits, &revs, show_edge);
+ if (use_thin_pack)
+ for (i = 0; i < extra_edge_obj.nr; i++)
+ fprintf(pack_pipe, "-%s\n", sha1_to_hex(
+ extra_edge_obj.objects[i].item->sha1));
traverse_commit_list(&revs, show_commit, show_object, NULL);
fflush(pack_pipe);
fclose(pack_pipe);
return 0;
}
+static int feed_msg_to_hook(int fd, const char *fmt, ...)
+{
+ int cnt;
+ char buf[1024];
+ va_list params;
+
+ va_start(params, fmt);
+ cnt = vsprintf(buf, fmt, params);
+ va_end(params);
+ return write_in_full(fd, buf, cnt) != cnt;
+}
+
+static int feed_obj_to_hook(const char *label, struct object_array *oa, int i, int fd)
+{
+ return feed_msg_to_hook(fd, "%s %s\n", label,
+ sha1_to_hex(oa->objects[i].item->sha1));
+}
+
+static int run_post_upload_pack_hook(size_t total, struct timeval *tv)
+{
+ const char *argv[2];
+ struct child_process proc;
+ int err, i;
+
+ argv[0] = "hooks/post-upload-pack";
+ argv[1] = NULL;
+
+ if (access(argv[0], X_OK) < 0)
+ return 0;
+
+ memset(&proc, 0, sizeof(proc));
+ proc.argv = argv;
+ proc.in = -1;
+ proc.stdout_to_stderr = 1;
+ err = start_command(&proc);
+ if (err)
+ return err;
+ for (i = 0; !err && i < want_obj.nr; i++)
+ err |= feed_obj_to_hook("want", &want_obj, i, proc.in);
+ for (i = 0; !err && i < have_obj.nr; i++)
+ err |= feed_obj_to_hook("have", &have_obj, i, proc.in);
+ if (!err)
+ err |= feed_msg_to_hook(proc.in, "time %ld.%06ld\n",
+ (long)tv->tv_sec, (long)tv->tv_usec);
+ if (!err)
+ err |= feed_msg_to_hook(proc.in, "size %ld\n", (long)total);
+ if (!err)
+ err |= feed_msg_to_hook(proc.in, "kind %s\n",
+ (nr_our_refs == want_obj.nr && !have_obj.nr)
+ ? "clone" : "fetch");
+ if (close(proc.in))
+ err = 1;
+ if (finish_command(&proc))
+ err = 1;
+ return err;
+}
+
static void create_pack_file(void)
{
+ struct timeval start_tv, tv;
struct async rev_list;
struct child_process pack_objects;
int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
int buffered = -1;
- ssize_t sz;
+ ssize_t sz, total_sz;
const char *argv[10];
int arg = 0;
+ gettimeofday(&start_tv, NULL);
+ total_sz = 0;
if (shallow_nr) {
rev_list.proc = do_rev_list;
rev_list.data = 0;
/* pass on revisions we (don't) want */
if (!shallow_nr) {
- FILE *pipe_fd = fdopen(pack_objects.in, "w");
+ FILE *pipe_fd = xfdopen(pack_objects.in, "w");
if (!create_full_pack) {
int i;
for (i = 0; i < want_obj.nr; i++)
sz = xread(pack_objects.out, cp,
sizeof(data) - outsz);
if (0 < sz)
- ;
+ total_sz += sz;
else if (sz == 0) {
close(pack_objects.out);
pack_objects.out = -1;
}
if (use_sideband)
packet_flush(1);
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec -= start_tv.tv_sec;
+ if (tv.tv_usec < start_tv.tv_usec) {
+ tv.tv_sec--;
+ tv.tv_usec += 1000000;
+ }
+ tv.tv_usec -= start_tv.tv_usec;
+ if (run_post_upload_pack_hook(total_sz, &tv))
+ warning("post-upload-hook failed");
return;
fail:
save_commit_buffer = 0;
- for(;;) {
+ for (;;) {
int len = packet_read_line(0, line, sizeof(line));
reset_timeout();
shallow_nr = 0;
if (debug_fd)
- write_in_full(debug_fd, "#S\n", 3);
+ write_str_in_full(debug_fd, "#S\n");
for (;;) {
struct object *o;
unsigned char sha1_buf[20];
if (!prefixcmp(line, "shallow ")) {
unsigned char sha1[20];
struct object *object;
- use_thin_pack = 0;
if (get_sha1(line + 8, sha1))
die("invalid shallow line: %s", line);
object = parse_object(sha1);
}
if (!prefixcmp(line, "deepen ")) {
char *end;
- use_thin_pack = 0;
depth = strtol(line + 7, &end, 0);
if (end == line + 7 || depth <= 0)
die("Invalid deepen: %s", line);
}
}
if (debug_fd)
- write_in_full(debug_fd, "#E\n", 3);
+ write_str_in_full(debug_fd, "#E\n");
if (!use_sideband && daemon_mode)
no_progress = 1;
NULL, &want_obj);
parents = parents->next;
}
+ add_object_array(object, NULL, &extra_edge_obj);
}
/* make sure commit traversal conforms to client */
register_shallow(object->sha1);
static void list_vars(void)
{
struct git_var *ptr;
- for(ptr = git_vars; ptr->read; ptr++) {
+ for (ptr = git_vars; ptr->read; ptr++)
printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
- }
}
static const char *read_var(const char *var)
struct git_var *ptr;
const char *val;
val = NULL;
- for(ptr = git_vars; ptr->read; ptr++) {
+ for (ptr = git_vars; ptr->read; ptr++) {
if (strcmp(var, ptr->name) == 0) {
val = ptr->read(IDENT_ERROR_ON_NO_NAME);
break;
{
const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+ if (!advice_status_hints)
+ return;
if (!s->is_initial)
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
else
{
const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+ if (!advice_status_hints)
+ return;
if (!s->is_initial) {
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
} else {
{
const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+ if (!advice_status_hints)
+ return;
if (!has_deleted)
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)");
else
{
const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Untracked files:");
+ if (!advice_status_hints)
+ return;
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
color_fprintf_ln(s->fp, c, "#");
}
setup_standard_excludes(&dir);
fill_directory(&dir, NULL);
- for(i = 0; i < dir.nr; i++) {
+ for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
if (!cache_name_is_other(ent->name, ent->len))
continue;
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
- wt_status_print_unmerged(s);
wt_status_print_updated(s);
+ wt_status_print_unmerged(s);
wt_status_print_changed(s);
if (s->submodule_summary)
wt_status_print_submodule_summary(s);
{
int i1, i2;
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return s1 == s2 && !memcmp(l1, l2, s1);
+
+ i1 = 0;
+ i2 = 0;
+
+ /*
+ * -w matches everything that matches with -b, and -b in turn
+ * matches everything that matches with --ignore-space-at-eol.
+ *
+ * Each flavor of ignoring needs different logic to skip whitespaces
+ * while we have both sides to compare.
+ */
if (flags & XDF_IGNORE_WHITESPACE) {
- for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
- if (isspace(l1[i1]))
- while (isspace(l1[i1]) && i1 < s1)
- i1++;
- if (isspace(l2[i2]))
- while (isspace(l2[i2]) && i2 < s2)
- i2++;
- if (i1 < s1 && i2 < s2 && l1[i1++] != l2[i2++])
+ goto skip_ws;
+ while (i1 < s1 && i2 < s2) {
+ if (l1[i1++] != l2[i2++])
return 0;
+ skip_ws:
+ while (i1 < s1 && isspace(l1[i1]))
+ i1++;
+ while (i2 < s2 && isspace(l2[i2]))
+ i2++;
}
- return (i1 >= s1 && i2 >= s2);
} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
- for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
- if (isspace(l1[i1])) {
- if (!isspace(l2[i2]))
- return 0;
- while (isspace(l1[i1]) && i1 < s1)
- i1++;
- while (isspace(l2[i2]) && i2 < s2)
- i2++;
- } else if (l1[i1++] != l2[i2++])
- return 0;
- }
- return (i1 >= s1 && i2 >= s2);
- } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
- for (i1 = i2 = 0; i1 < s1 && i2 < s2; ) {
- if (l1[i1] != l2[i2]) {
+ while (i1 < s1 && i2 < s2) {
+ if (isspace(l1[i1]) && isspace(l2[i2])) {
+ /* Skip matching spaces and try again */
while (i1 < s1 && isspace(l1[i1]))
i1++;
while (i2 < s2 && isspace(l2[i2]))
i2++;
- if (i1 < s1 || i2 < s2)
- return 0;
- return 1;
+ continue;
}
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
+ while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
+ ; /* keep going */
+ }
+
+ /*
+ * After running out of one side, the remaining side must have
+ * nothing but whitespace for the lines to match. Note that
+ * ignore-whitespace-at-eol case may break out of the loop
+ * while there still are characters remaining on both lines.
+ */
+ if (i1 < s1) {
+ while (i1 < s1 && isspace(l1[i1]))
i1++;
+ if (s1 != i1)
+ return 0;
+ }
+ if (i2 < s2) {
+ while (i2 < s2 && isspace(l2[i2]))
i2++;
- }
- return i1 >= s1 && i2 >= s2;
- } else
- return s1 == s2 && !memcmp(l1, l2, s1);
+ return (s2 == i2);
+ }
+ return 1;
}
static unsigned long xdl_hash_record_with_whitespace(char const **data,
for (; ptr < top && *ptr != '\n'; ptr++) {
if (isspace(*ptr)) {
const char *ptr2 = ptr;
+ int at_eol;
while (ptr + 1 < top && isspace(ptr[1])
&& ptr[1] != '\n')
ptr++;
+ at_eol = (top <= ptr + 1 || ptr[1] == '\n');
if (flags & XDF_IGNORE_WHITESPACE)
; /* already handled */
else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
- && ptr[1] != '\n') {
+ && !at_eol) {
ha += (ha << 5);
ha ^= (unsigned long) ' ';
}
else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
- && ptr[1] != '\n') {
+ && !at_eol) {
while (ptr2 != ptr + 1) {
ha += (ha << 5);
ha ^= (unsigned long) *ptr2;