--- /dev/null
+GIT v1.6.4.2 Release Notes
+==========================
+
+Fixes since v1.6.4.1
+--------------------
+
+* --date=relative output between 1 and 5 years ago rounded the number of
+ years when saying X years Y months ago, instead of rounding it down.
+
+* "git add -p" did not handle changes in executable bits correctly
+ (a regression around 1.6.3).
+
+* "git apply" did not honor GNU diff's convention to mark the creation/deletion
+ event with UNIX epoch timestamp on missing side.
+
+* "git checkout" incorrectly removed files in a directory pointed by a
+ symbolic link during a branch switch that replaces a directory with
+ a symbolic link.
+
+* "git clean -d -f" happily descended into a subdirectory that is managed by a
+ separate git repository. It now requires two -f options for safety.
+
+* "git fetch/push" over http transports had two rather grave bugs.
+
+* "git format-patch --cover-letter" did not prepare the cover letter file
+ for use with non-ASCII strings when there are the series contributors with
+ non-ASCII names.
+
+* "git pull origin branch" and "git fetch origin && git merge origin/branch"
+ left different merge messages in the resulting commit.
+
+Other minor documentation updates are included.
(subsystems)
+ * various updates to git-svn and gitweb.
+
(portability)
+ * more improvements on mingw port.
+
(performance)
+ * On major platforms, the system can be compiled to use with Linus's
+ block-sha1 implementation of the SHA-1 hash algorithm, which
+ outperforms the default fallback implementation we borrowed from
+ Mozzilla.
+
(usability, bells and whistles)
+ * 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" learned to optionally ignore whitespace differences.
+
+ * "git am" handles input e-mail files that has CRLF line endings sensibly.
+
+ * "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 fast-export" learned --no-data option that can be useful when
+ reordering commits and trees without touching the contents of
+ blobs.
+
+ * "git init" learned to mkdir/chdir into a directory when given an
+ extra argument (i.e. "git init this").
+
+ * "git instaweb" optionally can use mongoose as the web server.
+
+ * "git log --decorate" can optionally be told with --decorate=full to
+ give the reference name in full.
+
+ * "git push" can be told to be --quiet.
+
+ * 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.
+
+ * various subcommands to "git submodule" learned --recursive option.
+
+ * "git submodule summary" learned --files option to compare the work
+ tree vs the commit bound at submodule path, instead of comparing
+ the index.
+
(developers)
+ * With GIT_TEST_OPTS="--root=/p/a/t/h", tests can be run outside the
+ source directory; using tmpfs may give faster turnaround.
+
+
Fixes since v1.6.4
------------------
# Here are fixes that this release has, but have not been backported to
# v1.6.4.X series.
-
+--
+exec >/var/tmp/1
+O=v1.6.4.1-266-g235db15
+echo O=$(git describe master)
+git shortlog --no-merges $O..master --not maint
SYNOPSIS
--------
[verse]
-'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
[--output=<file>] [--worktree-attributes]
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
[path...]
based sha1 expressions such as "<branchname>@\{yesterday}".
-f::
+--force::
Reset <branchname> to <startpoint> if <branchname> exists
already. Without `-f` 'git-branch' refuses to change an existing branch.
- `--no-merged` is used to find branches which are candidates for merging
into HEAD, since those branches are not fully contained by HEAD.
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1],
+linkgit:git-fetch[1],
+linkgit:git-remote[1].
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <gitster@pobox.com>
'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
OPTIONS
-------
-q::
+--quiet::
Quiet, suppress feedback messages.
-f::
+--force::
When switching branches, proceed even if the index or the
working tree differs from HEAD. This is used to throw away
local changes.
"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
if you really want to remove such a directory.
-f::
+--force::
If the git configuration specifies clean.requireForce as true,
'git-clean' will refuse to run unless given -f or -n.
'git clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
- [--depth <depth>] [--] <repository> [<directory>]
+ [--depth <depth>] [--recursive] [--] <repository> [<directory>]
DESCRIPTION
-----------
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
with a long history, and would want to send in fixes
as patches.
+--recursive::
+ After the clone is created, initialize all submodules within,
+ using their default settings. This is equivalent to running
+ 'git submodule update --init --recursive' immediately after
+ the clone is finished. This option is ignored if the cloned
+ repository does not have a worktree/checkout (i.e. if any of
+ `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+
<repository>::
The (possibly remote) repository to clone from. See the
<<URLS,URLS>> section below for more information on specifying
SYNOPSIS
--------
[verse]
-'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend]
- [(-c | -C) <commit>] [-F <file> | -m <msg>]
+'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
+ [(-c | -C) <commit>] [-F <file> | -m <msg>] [--dry-run]
[--allow-empty] [--no-verify] [-e] [--author=<author>]
[--cleanup=<mode>] [--] [[-i | -o ]<file>...]
by one which files should be part of the commit, before finalizing the
operation. Currently, this is done by invoking 'git-add --interactive'.
-The 'git-status' command can be used to obtain a
+The `--dry-run` option can be used to obtain a
summary of what is included by any of the above for the next
-commit by giving the same set of parameters you would give to
-this command.
+commit by giving the same set of parameters (options and paths).
If you make a commit and then find a mistake immediately after
that, you can recover from it with 'git-reset'.
Like '-C', but with '-c' the editor is invoked, so that
the user can further edit the commit message.
+--dry-run::
+ Do not actually make a commit, but show the list of paths
+ with updates in the index, paths with changes in the work tree,
+ and paths that are untracked, similar to the one that is given
+ in the commit log editor.
+
-F <file>::
--file=<file>::
Take the commit message from the given file. Use '-' to
--quiet::
Suppress commit summary message.
+--dry-run::
+ Do not create a commit, but show a list of paths that are
+ to be committed, paths with local changes that will be left
+ uncommitted and paths that are untracked.
+
\--::
Do not interpret any more arguments as options.
--prune=<date>::
Prune loose objects older than date (default is 2 weeks ago,
- overrideable by the config variable `gc.pruneExpire`). This
+ overridable by the config variable `gc.pruneExpire`). This
option is on by default.
--no-prune::
The HTTP daemon command-line that will be executed.
Command-line options may be specified here, and the
configuration file will be added at the end of the command-line.
- Currently lighttpd, apache2 and webrick are supported.
+ Currently apache2, lighttpd, mongoose and webrick are supported.
(Default: lighttpd)
-m::
and <until>, see "SPECIFYING REVISIONS" section in
linkgit:git-rev-parse[1].
---decorate::
- Print out the ref names of any commits that are shown.
+--decorate[=short|full]::
+ Print out the ref names of any commits that are shown. If 'short' is
+ specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
+ 'refs/remotes/' will not be printed. If 'full' is specified, the
+ full ref name (including prefix) will be printed. The default option
+ is 'short'.
--source::
Print out the ref name given on the command line by which each
OPTIONS
-------
-f::
+--force::
Force renaming or moving of a file even if the target exists
-k::
Skip move or rename actions which would lead to an error
[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>]::
[verse]
'git submodule' [--quiet] add [-b branch]
[--reference <repository>] [--] <repository> <path>
-'git submodule' [--quiet] status [--cached] [--] [<path>...]
+'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
- [--reference <repository>] [--merge] [--] [<path>...]
-'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
-'git submodule' [--quiet] foreach <command>
+ [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach [--recursive] <command>
'git submodule' [--quiet] sync [--] [<path>...]
initialized and `+` if the currently checked out submodule commit
does not match the SHA-1 found in the index of the containing
repository. This command is the default command for 'git-submodule'.
++
+If '--recursive' is specified, this command will recurse into nested
+submodules, and show their status as well.
init::
Initialize the submodules, i.e. register each submodule name
If the submodule is not yet initialized, and you just want to use the
setting as stored in .gitmodules, you can automatically initialize the
submodule with the --init option.
++
+If '--recursive' is specified, this command will recurse into the
+registered submodules, and update any nested submodules within.
summary::
Show commit summary between the given commit (defaults to HEAD) and
working tree/index. For a submodule in question, a series of commits
in the submodule between the given super project commit and the
- index or working tree (switched by --cached) are shown.
+ index or working tree (switched by --cached) are shown. If the option
+ --files is given, show the series of commits in the submodule between
+ the index of the super project and the working tree of the submodule
+ (this option doesn't allow to use the --cached option or to provide an
+ explicit commit).
foreach::
Evaluates an arbitrary shell command in each checked out submodule.
- The command has access to the variables $path and $sha1:
+ The command has access to the variables $name, $path and $sha1:
+ $name is the name of the relevant submodule section in .gitmodules,
$path is the name of the submodule directory relative to the
superproject, and $sha1 is the commit as recorded in the superproject.
Any submodules defined in the superproject but not checked out are
ignored by this command. Unless given --quiet, foreach prints the name
of each submodule before evaluating the command.
+ If --recursive is given, submodules are traversed recursively (i.e.
+ the given shell command is evaluated in nested submodules as well).
A non-zero return from the command in any submodule causes
the processing to terminate. This can be overridden by adding '|| :'
to the end of the command.
commands typically use the commit found in the submodule HEAD, but
with this option, the commit stored in the index is used instead.
+--files::
+ This option is only valid for the summary command. This command
+ compares the commit in the index with that in the submodule HEAD
+ when this option is used.
+
-n::
--summary-limit::
This option is only valid for the summary command.
*NOTE*: Do *not* use this option unless you have read the note
for linkgit:git-clone[1]'s --reference and --shared options carefully.
+--recursive::
+ This option is only valid for foreach, update and status commands.
+ Traverse submodules recursively. The operation is performed not
+ only in the submodules of the current repo, but also
+ in any nested submodules inside those submodules (and so on).
+
<path>...::
Paths to submodule(s). When specified this will restrict the command
to only operate on the submodules found at the specified paths.
--------
[verse]
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
- <name> [<commit> | <object>]
-'git tag' -d <name>...
+ <tagname> [<commit> | <object>]
+'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
-'git tag' -v <name>...
+'git tag' -v <tagname>...
DESCRIPTION
-----------
-Adds a 'tag' reference in `.git/refs/tags/`. The tag <name> must pass
-linkgit:git-check-ref-format[1] which basicly means that control characters,
-space, ~, ^, :, ?, *, [ and \ are prohibited.
+Adds a tag reference in `.git/refs/tags/`.
Unless `-f` is given, the tag must not yet exist in
`.git/refs/tags/` directory.
Make a GPG-signed tag, using the given key
-f::
+--force::
Replace an existing tag with the given name (instead of failing)
-d::
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+<tagname>::
+ The name of the tag to create, delete, or describe.
+ The new tag name must pass all checks defined by
+ linkgit:git-check-ref-format[1]. Some of these checks
+ may restrict the characters allowed in a tag name.
+
CONFIGURATION
-------------
By default, 'git-tag' in sign-with-default mode (-s) will use your
------------
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1].
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>,
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
-------
-v::
--verbose::
After verifying the pack, show list of objects contained
- in the pack.
+ in the pack and a histogram of delta chain length.
+
+-s::
+--stat-only::
+ Do not verify the pack contents; only show the histogram of delta
+ chain length. With `--verbose`, list of objects is also shown.
+
\--::
Do not interpret any more arguments as options.
DESCRIPTION
-----------
-Creates a tree object using the current index.
+Creates a tree object using the current index. The name of the new
+tree object is printed to standard output.
The index must be in a fully merged state.
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.6.4.1/git.html[documentation for release 1.6.4.1]
+* link:v1.6.4.2/git.html[documentation for release 1.6.4.2]
* release notes for
+ 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].
'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
-----------
# when attempting to read from an fopen'ed directory.
#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
#
# Define NO_CURL if you do not have libcurl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
#
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
PROGRAMS += git-shell$X
PROGRAMS += git-show-index$X
PROGRAMS += git-unpack-file$X
-PROGRAMS += git-update-server-info$X
PROGRAMS += git-upload-pack$X
PROGRAMS += git-var$X
BUILTIN_OBJS += builtin-unpack-objects.o
BUILTIN_OBJS += builtin-update-index.o
BUILTIN_OBJS += builtin-update-ref.o
+BUILTIN_OBJS += builtin-update-server-info.o
BUILTIN_OBJS += builtin-upload-archive.o
BUILTIN_OBJS += builtin-verify-pack.o
BUILTIN_OBJS += builtin-verify-tag.o
NO_C99_FORMAT = YesPlease
NO_STRTOUMAX = YesPlease
endif
- ifdef NO_IPV6
- NEEDS_RESOLV = YesPlease
- endif
INSTALL = /usr/ucb/install
TAR = gtar
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
NO_PTHREADS = YesPlease
endif
endif
-ifneq (,$(findstring arm,$(uname_M)))
- ARM_SHA1 = YesPlease
- NO_MKSTEMPS = YesPlease
-endif
-include config.mak.autogen
-include config.mak
endif
else
BASIC_CFLAGS += -DNO_OPENSSL
- MOZILLA_SHA1 = 1
+ BLK_SHA1 = 1
OPENSSL_LIBSSL =
endif
ifdef NEEDS_SSL_WITH_CRYPTO
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
-else
-ifdef ARM_SHA1
- SHA1_HEADER = "arm/sha1.h"
- LIB_OBJS += arm/sha1.o arm/sha1_arm.o
-else
-ifdef MOZILLA_SHA1
- SHA1_HEADER = "mozilla-sha1/sha1.h"
- LIB_OBJS += mozilla-sha1/sha1.o
else
SHA1_HEADER = <openssl/sha.h>
EXTLIBS += $(LIB_4_CRYPTO)
endif
endif
-endif
-endif
ifdef NO_PERL_MAKEMAKER
export NO_PERL_MAKEMAKER
endif
{
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
char cwd[1024] = "";
- int buf_index = 1, len;
+ int buf_index = 1;
int depth = MAXDEPTH;
char *last_elem = NULL;
die_errno ("Could not get current working directory");
if (last_elem) {
- int len = strlen(buf);
+ size_t len = strlen(buf);
if (len + strlen(last_elem) + 2 > PATH_MAX)
die ("Too long path name: '%s/%s'",
buf, last_elem);
}
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
- len = readlink(buf, next_buf, PATH_MAX);
+ ssize_t len = readlink(buf, next_buf, PATH_MAX);
if (len < 0)
die_errno ("Invalid symlink '%s'", buf);
if (PATH_MAX <= len)
+++ /dev/null
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- */
-
-#include <string.h>
-#include "sha1.h"
-
-extern void arm_sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
-
-void arm_SHA1_Init(arm_SHA_CTX *c)
-{
- c->len = 0;
- c->hash[0] = 0x67452301;
- c->hash[1] = 0xefcdab89;
- c->hash[2] = 0x98badcfe;
- c->hash[3] = 0x10325476;
- c->hash[4] = 0xc3d2e1f0;
-}
-
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n)
-{
- uint32_t workspace[80];
- unsigned int partial;
- unsigned long done;
-
- partial = c->len & 0x3f;
- c->len += n;
- if ((partial + n) >= 64) {
- if (partial) {
- done = 64 - partial;
- memcpy(c->buffer + partial, p, done);
- arm_sha_transform(c->hash, c->buffer, workspace);
- partial = 0;
- } else
- done = 0;
- while (n >= done + 64) {
- arm_sha_transform(c->hash, p + done, workspace);
- done += 64;
- }
- } else
- done = 0;
- if (n - done)
- memcpy(c->buffer + partial, p + done, n - done);
-}
-
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c)
-{
- uint64_t bitlen;
- uint32_t bitlen_hi, bitlen_lo;
- unsigned int i, offset, padlen;
- unsigned char bits[8];
- static const unsigned char padding[64] = { 0x80, };
-
- bitlen = c->len << 3;
- offset = c->len & 0x3f;
- padlen = ((offset < 56) ? 56 : (64 + 56)) - offset;
- arm_SHA1_Update(c, padding, padlen);
-
- bitlen_hi = bitlen >> 32;
- bitlen_lo = bitlen & 0xffffffff;
- bits[0] = bitlen_hi >> 24;
- bits[1] = bitlen_hi >> 16;
- bits[2] = bitlen_hi >> 8;
- bits[3] = bitlen_hi;
- bits[4] = bitlen_lo >> 24;
- bits[5] = bitlen_lo >> 16;
- bits[6] = bitlen_lo >> 8;
- bits[7] = bitlen_lo;
- arm_SHA1_Update(c, bits, 8);
-
- for (i = 0; i < 5; i++) {
- uint32_t v = c->hash[i];
- hash[0] = v >> 24;
- hash[1] = v >> 16;
- hash[2] = v >> 8;
- hash[3] = v;
- hash += 4;
- }
-}
+++ /dev/null
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- */
-
-#include <stdint.h>
-
-typedef struct {
- uint64_t len;
- uint32_t hash[5];
- unsigned char buffer[64];
-} arm_SHA_CTX;
-
-void arm_SHA1_Init(arm_SHA_CTX *c);
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n);
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c);
-
-#define git_SHA_CTX arm_SHA_CTX
-#define git_SHA1_Init arm_SHA1_Init
-#define git_SHA1_Update arm_SHA1_Update
-#define git_SHA1_Final arm_SHA1_Final
+++ /dev/null
-/*
- * SHA transform optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
- .text
- .globl arm_sha_transform
-
-/*
- * void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
- *
- * note: the "data" pointer may be unaligned.
- */
-
-arm_sha_transform:
-
- stmfd sp!, {r4 - r8, lr}
-
- @ for (i = 0; i < 16; i++)
- @ W[i] = ntohl(((uint32_t *)data)[i]);
-
-#ifdef __ARMEB__
- mov r4, r0
- mov r0, r2
- mov r2, #64
- bl memcpy
- mov r2, r0
- mov r0, r4
-#else
- mov r3, r2
- mov lr, #16
-1: ldrb r4, [r1], #1
- ldrb r5, [r1], #1
- ldrb r6, [r1], #1
- ldrb r7, [r1], #1
- subs lr, lr, #1
- orr r5, r5, r4, lsl #8
- orr r6, r6, r5, lsl #8
- orr r7, r7, r6, lsl #8
- str r7, [r3], #4
- bne 1b
-#endif
-
- @ for (i = 0; i < 64; i++)
- @ W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31);
-
- sub r3, r2, #4
- mov lr, #64
-2: ldr r4, [r3, #4]!
- subs lr, lr, #1
- ldr r5, [r3, #8]
- ldr r6, [r3, #32]
- ldr r7, [r3, #52]
- eor r4, r4, r5
- eor r4, r4, r6
- eor r4, r4, r7
- mov r4, r4, ror #31
- str r4, [r3, #64]
- bne 2b
-
- /*
- * The SHA functions are:
- *
- * f1(B,C,D) = (D ^ (B & (C ^ D)))
- * f2(B,C,D) = (B ^ C ^ D)
- * f3(B,C,D) = ((B & C) | (D & (B | C)))
- *
- * Then the sub-blocks are processed as follows:
- *
- * A' = ror(A, 27) + f(B,C,D) + E + K + *W++
- * B' = A
- * C' = ror(B, 2)
- * D' = C
- * E' = D
- *
- * We therefore unroll each loop 5 times to avoid register shuffling.
- * Also the ror for C (and also D and E which are successivelyderived
- * from it) is applied in place to cut on an additional mov insn for
- * each round.
- */
-
- .macro sha_f1, A, B, C, D, E
- ldr r3, [r2], #4
- eor ip, \C, \D
- add \E, r1, \E, ror #2
- and ip, \B, ip, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f2, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- eor ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f3, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- orr ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- and ip, ip, \D, ror #2
- add \E, \E, r3
- and r3, \B, \C, ror #2
- orr ip, ip, r3
- add \E, \E, ip
- .endm
-
- ldmia r0, {r4 - r8}
-
- mov lr, #4
- ldr r1, .L_sha_K + 0
-
- /* adjust initial values */
- mov r6, r6, ror #30
- mov r7, r7, ror #30
- mov r8, r8, ror #30
-
-3: subs lr, lr, #1
- sha_f1 r4, r5, r6, r7, r8
- sha_f1 r8, r4, r5, r6, r7
- sha_f1 r7, r8, r4, r5, r6
- sha_f1 r6, r7, r8, r4, r5
- sha_f1 r5, r6, r7, r8, r4
- bne 3b
-
- ldr r1, .L_sha_K + 4
- mov lr, #4
-
-4: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 4b
-
- ldr r1, .L_sha_K + 8
- mov lr, #4
-
-5: subs lr, lr, #1
- sha_f3 r4, r5, r6, r7, r8
- sha_f3 r8, r4, r5, r6, r7
- sha_f3 r7, r8, r4, r5, r6
- sha_f3 r6, r7, r8, r4, r5
- sha_f3 r5, r6, r7, r8, r4
- bne 5b
-
- ldr r1, .L_sha_K + 12
- mov lr, #4
-
-6: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 6b
-
- ldmia r0, {r1, r2, r3, ip, lr}
- add r4, r1, r4
- add r5, r2, r5
- add r6, r3, r6, ror #2
- add r7, ip, r7, ror #2
- add r8, lr, r8, ror #2
- stmia r0, {r4 - r8}
-
- ldmfd sp!, {r4 - r8, pc}
-
-.L_sha_K:
- .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
if (!hashcmp(bisect_rev, current_bad_sha1)) {
exit_if_skipped_commits(tried, current_bad_sha1);
- printf("%s is first bad commit\n", bisect_rev_hex);
+ printf("%s is the first bad commit\n", bisect_rev_hex);
show_diff_tree(prefix, revs.commits->item);
/* This means the bisection process succeeded. */
exit(10);
/*
- * Based on the Mozilla SHA1 (see mozilla-sha1/sha1.c),
- * optimized to do word accesses rather than byte accesses,
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
* and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
*/
-#include <string.h>
-#include <arpa/inet.h>
+/* this is only to get definitions for memcpy(), ntohl() and htonl() */
+#include "../git-compat-util.h"
#include "sha1.h"
-#if defined(__i386__) || defined(__x86_64__)
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
/*
* Force usage of rol or ror by selecting the one with the smaller constant.
#if defined(__i386__) || defined(__x86_64__)
#define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
-#elif defined(__arm__)
+#elif defined(__GNUC__) && defined(__arm__)
#define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
#else
#define setW(x, val) (W(x) = (val))
/*
- * Based on the Mozilla SHA1 (see mozilla-sha1/sha1.h),
- * optimized to do word accesses rather than byte accesses,
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
* and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
*/
typedef struct {
+ unsigned long long size;
unsigned int H[5];
unsigned int W[16];
- unsigned long long size;
} blk_SHA_CTX;
void blk_SHA1_Init(blk_SHA_CTX *ctx);
for (specs = 0; pathspec[specs]; specs++)
/* nothing */;
seen = xcalloc(specs, 1);
- refresh_index(&the_index, verbose ? REFRESH_SAY_CHANGED : REFRESH_QUIET,
- pathspec, seen);
+ refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
+ pathspec, seen, "Unstaged changes after refreshing the index:");
for (i = 0; i < specs; i++) {
if (!seen[i])
die("pathspec '%s' did not match any files", pathspec[i]);
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"));
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);
OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
- OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+ OPT_BOOLEAN('f', "force", &force_create, "force creation (when already exists)"),
{
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
"commit", "print only not merged branches",
topts.dir = xcalloc(1, sizeof(*topts.dir));
topts.dir->flags |= DIR_SHOW_IGNORED;
topts.dir->exclude_per_dir = ".gitignore";
- tree = parse_tree_indirect(old->commit->object.sha1);
+ tree = parse_tree_indirect(old->commit ?
+ old->commit->object.sha1 :
+ (unsigned char *)EMPTY_TREE_SHA1_BIN);
init_tree_desc(&trees[0], tree->buffer, tree->size);
tree = parse_tree_indirect(new->commit->object.sha1);
init_tree_desc(&trees[1], tree->buffer, tree->size);
parse_commit(new->commit);
}
- if (!old.commit && !opts->force) {
- if (!opts->quiet) {
- warning("You appear to be on a branch yet to be born.");
- warning("Forcing checkout of %s.", new->name);
- }
- opts->force = 1;
- }
-
ret = merge_working_tree(opts, &old, new);
if (ret)
return ret;
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"),
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
3),
- OPT_BOOLEAN('f', NULL, &opts.force, "force"),
+ OPT_BOOLEAN('f', "force", &opts.force, "force"),
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))
struct option options[] = {
OPT__QUIET(&quiet),
OPT__DRY_RUN(&show_only),
- OPT_BOOLEAN('f', NULL, &force, "force"),
+ OPT_BOOLEAN('f', "force", &force, "force"),
OPT_BOOLEAN('d', NULL, &remove_directories,
"remove whole directories"),
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
};
static int option_quiet, option_no_checkout, option_bare, option_mirror;
-static int option_local, option_no_hardlinks, option_shared;
+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;
"don't use local hardlinks, always copy"),
OPT_BOOLEAN('s', "shared", &option_shared,
"setup as shared repository"),
+ OPT_BOOLEAN(0, "recursive", &option_recursive,
+ "setup as shared repository"),
OPT_STRING(0, "template", &option_template, "path",
"path the template repository"),
OPT_STRING(0, "reference", &option_reference, "repo",
"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",
OPT_END()
};
+static const char *argv_submodule[] = {
+ "submodule", "update", "--init", "--recursive", NULL
+};
+
static char *get_repo_path(const char *repo, int *is_bundle)
{
static char *suffix[] = { "/.git", ".git", "" };
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);
err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(remote_head->old_sha1), "1", NULL);
+
+ if (!err && option_recursive)
+ err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
}
strbuf_release(&reflog_msg);
static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, no_verify, allow_empty;
+static int quiet, verbose, no_verify, allow_empty, dry_run;
static char *untracked_files_arg;
/*
* The default commit message cleanup mode will remove the lines
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+ OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
exit(128); /* We've already reported the error, finish dying */
}
-static char *prepare_index(int argc, const char **argv, const char *prefix)
+static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
{
int fd;
struct string_list partial;
const char **pathspec = NULL;
+ int refresh_flags = REFRESH_QUIET;
+ if (is_status)
+ refresh_flags |= REFRESH_UNMERGED;
if (interactive) {
if (interactive_add(argc, argv, prefix) != 0)
die("interactive add failed");
if (all || (also && pathspec && *pathspec)) {
int fd = hold_locked_index(&index_lock, 1);
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
- refresh_cache(REFRESH_QUIET);
+ refresh_cache(refresh_flags);
if (write_cache(fd, active_cache, active_nr) ||
close_lock_file(&index_lock))
die("unable to write new_index file");
*/
if (!pathspec || !*pathspec) {
fd = hold_locked_index(&index_lock, 1);
- refresh_cache(REFRESH_QUIET);
+ refresh_cache(refresh_flags);
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(&index_lock))
die("unable to write new_index file");
return false_lock.filename;
}
-static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn)
+static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
+ struct wt_status *s)
{
- struct wt_status s;
-
- wt_status_prepare(&s);
- if (wt_status_relative_paths)
- s.prefix = prefix;
+ if (s->relative_paths)
+ s->prefix = prefix;
if (amend) {
- s.amend = 1;
- s.reference = "HEAD^1";
+ s->amend = 1;
+ s->reference = "HEAD^1";
}
- s.verbose = verbose;
- s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
- s.index_file = index_file;
- s.fp = fp;
- s.nowarn = nowarn;
+ s->verbose = verbose;
+ s->index_file = index_file;
+ s->fp = fp;
+ s->nowarn = nowarn;
- wt_status_print(&s);
+ wt_status_print(s);
- return s.commitable;
+ return s->commitable;
}
static int is_a_merge(const unsigned char *sha1)
author_date = date;
}
-static int prepare_to_commit(const char *index_file, const char *prefix)
+static int prepare_to_commit(const char *index_file, const char *prefix,
+ struct wt_status *s)
{
struct stat statbuf;
int commitable, saved_color_setting;
if (ident_shown)
fprintf(fp, "#\n");
- saved_color_setting = wt_status_use_color;
- wt_status_use_color = 0;
- commitable = run_status(fp, index_file, prefix, 1);
- wt_status_use_color = saved_color_setting;
+ saved_color_setting = s->use_color;
+ s->use_color = 0;
+ commitable = run_status(fp, index_file, prefix, 1, s);
+ s->use_color = saved_color_setting;
} else {
unsigned char sha1[20];
const char *parent = "HEAD";
if (!commitable && !in_merge && !allow_empty &&
!(amend && is_a_merge(head_sha1))) {
- run_status(stdout, index_file, prefix, 0);
+ run_status(stdout, index_file, prefix, 0, s);
return 0;
}
static int parse_and_validate_options(int argc, const char *argv[],
const char * const usage[],
- const char *prefix)
+ const char *prefix,
+ struct wt_status *s)
{
int f = 0;
if (!untracked_files_arg)
; /* default already initialized */
else if (!strcmp(untracked_files_arg, "no"))
- show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+ s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
else if (!strcmp(untracked_files_arg, "normal"))
- show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
else if (!strcmp(untracked_files_arg, "all"))
- show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+ s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
else
die("Invalid untracked files mode '%s'", untracked_files_arg);
return argc;
}
-int cmd_status(int argc, const char **argv, const char *prefix)
+static int dry_run_commit(int argc, const char **argv, const char *prefix,
+ struct wt_status *s)
{
- const char *index_file;
int commitable;
+ const char *index_file;
- git_config(git_status_config, NULL);
+ index_file = prepare_index(argc, argv, prefix, 1);
+ commitable = run_status(stdout, index_file, prefix, 0, s);
+ rollback_index_files();
- if (wt_status_use_color == -1)
- wt_status_use_color = git_use_color_default;
+ return commitable ? 0 : 1;
+}
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
+static int parse_status_slot(const char *var, int offset)
+{
+ if (!strcasecmp(var+offset, "header"))
+ return WT_STATUS_HEADER;
+ if (!strcasecmp(var+offset, "updated")
+ || !strcasecmp(var+offset, "added"))
+ return WT_STATUS_UPDATED;
+ if (!strcasecmp(var+offset, "changed"))
+ return WT_STATUS_CHANGED;
+ if (!strcasecmp(var+offset, "untracked"))
+ return WT_STATUS_UNTRACKED;
+ if (!strcasecmp(var+offset, "nobranch"))
+ return WT_STATUS_NOBRANCH;
+ if (!strcasecmp(var+offset, "unmerged"))
+ return WT_STATUS_UNMERGED;
+ die("bad config variable '%s'", var);
+}
- argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
+static int git_status_config(const char *k, const char *v, void *cb)
+{
+ struct wt_status *s = cb;
- index_file = prepare_index(argc, argv, prefix);
+ if (!strcmp(k, "status.submodulesummary")) {
+ int is_bool;
+ s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+ if (is_bool && s->submodule_summary)
+ s->submodule_summary = -1;
+ return 0;
+ }
+ if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
+ s->use_color = git_config_colorbool(k, v, -1);
+ return 0;
+ }
+ if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+ int slot = parse_status_slot(k, 13);
+ if (!v)
+ return config_error_nonbool(k);
+ color_parse(v, k, s->color_palette[slot]);
+ return 0;
+ }
+ if (!strcmp(k, "status.relativepaths")) {
+ s->relative_paths = git_config_bool(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "status.showuntrackedfiles")) {
+ if (!v)
+ return config_error_nonbool(k);
+ else if (!strcmp(v, "no"))
+ s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+ else if (!strcmp(v, "normal"))
+ s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ else if (!strcmp(v, "all"))
+ s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+ else
+ return error("Invalid untracked files mode '%s'", v);
+ return 0;
+ }
+ return git_diff_ui_config(k, v, NULL);
+}
- commitable = run_status(stdout, index_file, prefix, 0);
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+ struct wt_status s;
- rollback_index_files();
+ wt_status_prepare(&s);
+ git_config(git_status_config, &s);
+ if (s.use_color == -1)
+ s.use_color = git_use_color_default;
+ if (diff_use_color_default == -1)
+ diff_use_color_default = git_use_color_default;
- return commitable ? 0 : 1;
+ argc = parse_and_validate_options(argc, argv, builtin_status_usage,
+ prefix, &s);
+ return dry_run_commit(argc, argv, prefix, &s);
}
static void print_summary(const char *prefix, const unsigned char *sha1)
static int git_commit_config(const char *k, const char *v, void *cb)
{
+ struct wt_status *s = cb;
+
if (!strcmp(k, "commit.template"))
return git_config_string(&template_file, k, v);
- return git_status_config(k, v, cb);
+ return git_status_config(k, v, s);
}
int cmd_commit(int argc, const char **argv, const char *prefix)
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
int allow_fast_forward = 1;
+ struct wt_status s;
- git_config(git_commit_config, NULL);
-
- if (wt_status_use_color == -1)
- wt_status_use_color = git_use_color_default;
+ wt_status_prepare(&s);
+ git_config(git_commit_config, &s);
- argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
+ if (s.use_color == -1)
+ s.use_color = git_use_color_default;
- index_file = prepare_index(argc, argv, prefix);
+ argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+ prefix, &s);
+ if (dry_run) {
+ if (diff_use_color_default == -1)
+ diff_use_color_default = git_use_color_default;
+ return dry_run_commit(argc, argv, prefix, &s);
+ }
+ index_file = prepare_index(argc, argv, prefix, 0);
/* Set up everything for writing the commit object. This includes
running hooks, writing the trees, and interacting with the user. */
- if (!prepare_to_commit(index_file, prefix)) {
+ if (!prepare_to_commit(index_file, prefix, &s)) {
rollback_index_files();
return 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/"))
struct rev_info *rev)
{
int i;
+ int decoration_style = 0;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--decorate")) {
- load_ref_decorations();
- rev->show_decorations = 1;
+ decoration_style = DECORATE_SHORT_REFS;
+ } else if (!prefixcmp(arg, "--decorate=")) {
+ const char *v = skip_prefix(arg, "--decorate=");
+ if (!strcmp(v, "full"))
+ decoration_style = DECORATE_FULL_REFS;
+ else if (!strcmp(v, "short"))
+ decoration_style = DECORATE_SHORT_REFS;
+ else
+ die("invalid --decorate option: %s", arg);
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
} else if (!strcmp(arg, "-h")) {
} else
die("unrecognized argument: %s", arg);
}
+ if (decoration_style) {
+ rev->show_decorations = 1;
+ load_ref_decorations(decoration_style);
+ }
}
/*
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
struct option builtin_mv_options[] = {
OPT__DRY_RUN(&show_only),
- OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
+ OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"),
OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
OPT_END(),
};
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);
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");
+ 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++;
}
argc = parse_options(argc, argv, unused_prefix, read_tree_options,
read_tree_usage, 0);
- if (read_cache_unmerged() && (opts.prefix || opts.merge))
- die("You need to resolve your current index first");
-
prefix_set = opts.prefix ? 1 : 0;
if (1 < opts.merge + opts.reset + prefix_set)
die("Which one? -m, --reset, or --prefix?");
- stage = opts.merge = (opts.reset || opts.merge || prefix_set);
+
+ if (opts.reset || opts.merge || opts.prefix) {
+ if (read_cache_unmerged() && (opts.prefix || opts.merge))
+ die("You need to resolve your current index first");
+ stage = opts.merge = 1;
+ }
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
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)
if (read_cache() < 0)
return error("Could not read index");
- result = refresh_cache(flags) ? 1 : 0;
+ result = refresh_index(&the_index, (flags), NULL, NULL,
+ "Unstaged changes after reset:") ? 1 : 0;
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(index_lock))
return error ("Could not refresh index");
}
}
+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. */
die("Cannot do %s reset with paths.",
reset_type_names[reset_type]);
return read_from_tree(prefix, argv + i, sha1,
- quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
+ quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
}
if (reset_type == NONE)
reset_type = MIXED; /* by default */
break;
case MIXED: /* Report what has not been updated. */
update_index_refresh(0, NULL,
- quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
+ quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
break;
}
/* 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;
OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
OPT_STRING('u', NULL, &keyid, "key-id",
"use another key to sign the tag"),
- OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+ OPT_BOOLEAN('f', "force", &force, "replace the tag if exists"),
OPT_GROUP("Tag listing options"),
{
static int check_object(struct object *obj, int type, void *data)
{
if (!obj)
- return 0;
+ return 1;
if (obj->flags & FLAG_WRITTEN)
- return 1;
+ return 0;
if (type != OBJ_ANY && obj->type != type)
die("object type mismatch");
if (type != obj->type || type <= 0)
die("object of unexpected type");
obj->flags |= FLAG_WRITTEN;
- return 1;
+ return 0;
}
if (fsck_object(obj, 1, fsck_error_function))
die("Error in object");
- if (!fsck_walk(obj, check_object, NULL))
+ if (fsck_walk(obj, check_object, NULL))
die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
write_cached_object(obj);
- return 1;
+ return 0;
}
static void write_rest(void)
{
unsigned i;
- for (i = 0; i < nr_objects; i++)
- check_object(obj_list[i].obj, OBJ_ANY, NULL);
+ for (i = 0; i < nr_objects; i++) {
+ if (obj_list[i].obj)
+ check_object(obj_list[i].obj, OBJ_ANY, NULL);
+ }
}
static void added_object(unsigned nr, enum object_type type,
--- /dev/null
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char * const update_server_info_usage[] = {
+ "git update-server-info [--force]",
+ NULL
+};
+
+int cmd_update_server_info(int argc, const char **argv, const char *prefix)
+{
+ int force = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('f', "force", &force,
+ "update the info files from scratch"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ update_server_info_usage, 0);
+ if (argc > 0)
+ usage_with_options(update_server_info_usage, options);
+
+ return !!update_server_info(force);
+}
#define MAX_CHAIN 50
-static void show_pack_info(struct packed_git *p)
+#define VERIFY_PACK_VERBOSE 01
+#define VERIFY_PACK_STAT_ONLY 02
+
+static void show_pack_info(struct packed_git *p, unsigned int flags)
{
uint32_t nr_objects, i;
int cnt;
+ int stat_only = flags & VERIFY_PACK_STAT_ONLY;
unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;
nr_objects = p->num_objects;
type = packed_object_info_detail(p, offset, &size, &store_size,
&delta_chain_length,
base_sha1);
- printf("%s ", sha1_to_hex(sha1));
+ if (!stat_only)
+ printf("%s ", sha1_to_hex(sha1));
if (!delta_chain_length) {
- printf("%-6s %lu %lu %"PRIuMAX"\n",
- type, size, store_size, (uintmax_t)offset);
+ if (!stat_only)
+ printf("%-6s %lu %lu %"PRIuMAX"\n",
+ type, size, store_size, (uintmax_t)offset);
baseobjects++;
}
else {
- printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
- type, size, store_size, (uintmax_t)offset,
- delta_chain_length, sha1_to_hex(base_sha1));
+ if (!stat_only)
+ printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
+ type, size, store_size, (uintmax_t)offset,
+ delta_chain_length, sha1_to_hex(base_sha1));
if (delta_chain_length <= MAX_CHAIN)
chain_histogram[delta_chain_length]++;
else
chain_histogram[0] > 1 ? "s" : "");
}
-static int verify_one_pack(const char *path, int verbose)
+static int verify_one_pack(const char *path, unsigned int flags)
{
char arg[PATH_MAX];
int len;
+ int verbose = flags & VERIFY_PACK_VERBOSE;
+ int stat_only = flags & VERIFY_PACK_STAT_ONLY;
struct packed_git *pack;
int err;
return error("packfile %s not found.", arg);
install_packed_git(pack);
- err = verify_pack(pack);
- if (verbose) {
+ if (!stat_only)
+ err = verify_pack(pack);
+ else
+ err = open_pack_index(pack);
+
+ if (verbose || stat_only) {
if (err)
printf("%s: bad\n", pack->pack_name);
else {
- show_pack_info(pack);
- printf("%s: ok\n", pack->pack_name);
+ show_pack_info(pack, flags);
+ if (!stat_only)
+ printf("%s: ok\n", pack->pack_name);
}
}
}
static const char * const verify_pack_usage[] = {
- "git verify-pack [-v|--verbose] <pack>...",
+ "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
NULL
};
int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
int err = 0;
- int verbose = 0;
+ unsigned int flags = 0;
int i;
const struct option verify_pack_options[] = {
- OPT__VERBOSE(&verbose),
+ OPT_BIT('v', "verbose", &flags, "verbose",
+ VERIFY_PACK_VERBOSE),
+ OPT_BIT('s', "stat-only", &flags, "show statistics only",
+ VERIFY_PACK_STAT_ONLY),
OPT_END()
};
if (argc < 1)
usage_with_options(verify_pack_usage, verify_pack_options);
for (i = 0; i < argc; i++) {
- if (verify_one_pack(argv[i], verbose))
+ if (verify_one_pack(argv[i], flags))
err = 1;
discard_revindex();
}
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
-#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
+#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
/* "careful lstat()" */
-extern int check_path(const char *path, int len, struct stat *st);
+extern int check_path(const char *path, int len, struct stat *st, int skiplen);
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
-#define REFRESH_SAY_CHANGED 0x0020 /* say "changed" not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
+#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
+extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
struct lock_file {
struct lock_file *next;
};
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
while (interesting(list)) {
struct commit *commit;
struct commit_list *parents;
- struct commit_list *n;
+ struct commit_list *next;
int flags;
commit = list->item;
- n = list->next;
+ next = list->next;
free(list);
- list = n;
+ list = next;
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
free_commit_list(list);
list = result; result = NULL;
while (list) {
- struct commit_list *n = list->next;
+ struct commit_list *next = list->next;
if (!(list->item->object.flags & STALE))
insert_by_date(list->item, &result);
free(list);
- list = n;
+ list = next;
}
return result;
}
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)
{
--- /dev/null
+/*
+ * Let's make sure we always have a sane definition for ntohl()/htonl().
+ * Some libraries define those as a function call, just to perform byte
+ * shifting, bringing significant overhead to what should be a simple
+ * operation.
+ */
+
+/*
+ * Default version that the compiler ought to optimize properly with
+ * constant values.
+ */
+static inline unsigned int default_swab32(unsigned int val)
+{
+ return (((val & 0xff000000) >> 24) |
+ ((val & 0x00ff0000) >> 8) |
+ ((val & 0x0000ff00) << 8) |
+ ((val & 0x000000ff) << 24));
+}
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#define bswap32(x) ({ \
+ unsigned int __res; \
+ if (__builtin_constant_p(x)) { \
+ __res = default_swab32(x); \
+ } else { \
+ __asm__("bswap %0" : "=r" (__res) : "0" (x)); \
+ } \
+ __res; })
+
+#undef ntohl
+#undef htonl
+#define ntohl(x) bswap32(x)
+#define htonl(x) bswap32(x)
+
+#endif
# tests. These tests take up a significant amount of the total test time
# but are not needed unless you plan to talk to SVN repos.
#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
#
# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
signal(SIGCHLD, SIG_DFL);
host = strstr(url, "://");
- if(host) {
+ if (host) {
*host = '\0';
protocol = get_protocol(url);
host += 3;
def isModeExecChanged(src_mode, dst_mode):
return isModeExec(src_mode) != isModeExec(dst_mode)
-def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
cmd = p4_build_cmd("-G %s" % (cmd))
if verbose:
sys.stderr.write("Opening pipe: %s\n" % cmd)
try:
while True:
entry = marshal.load(p4.stdout)
- result.append(entry)
+ if cb is not None:
+ cb(entry)
+ else:
+ result.append(entry)
except EOFError:
pass
exitCode = p4.wait()
return branches
- ## Should move this out, doesn't use SELF.
- def readP4Files(self, files):
+ # output one file from the P4 stream
+ # - helper for streamP4Files
+
+ def streamOneP4File(self, file, contents):
+ if file["type"] == "apple":
+ print "\nfile %s is a strange apple file that forks. Ignoring" % \
+ file['depotFile']
+ return
+
+ relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+ if verbose:
+ sys.stderr.write("%s\n" % relPath)
+
+ mode = "644"
+ if isP4Exec(file["type"]):
+ mode = "755"
+ elif file["type"] == "symlink":
+ mode = "120000"
+ # p4 print on a symlink contains "target\n", so strip it off
+ last = contents.pop()
+ last = last[:-1]
+ contents.append(last)
+
+ if self.isWindows and file["type"].endswith("text"):
+ mangled = []
+ for data in contents:
+ data = data.replace("\r\n", "\n")
+ mangled.append(data)
+ contents = mangled
+
+ if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+ contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
+ elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+ contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
+
+ self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+
+ # total length...
+ length = 0
+ for d in contents:
+ length = length + len(d)
+
+ self.gitStream.write("data %d\n" % length)
+ for d in contents:
+ self.gitStream.write(d)
+ self.gitStream.write("\n")
+
+ def streamOneP4Deletion(self, file):
+ relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
+ if verbose:
+ sys.stderr.write("delete %s\n" % relPath)
+ self.gitStream.write("D %s\n" % relPath)
+
+ # handle another chunk of streaming data
+ def streamP4FilesCb(self, marshalled):
+
+ if marshalled.has_key('depotFile') and self.stream_have_file_info:
+ # start of a new file - output the old one first
+ self.streamOneP4File(self.stream_file, self.stream_contents)
+ self.stream_file = {}
+ self.stream_contents = []
+ self.stream_have_file_info = False
+
+ # pick up the new file information... for the
+ # 'data' field we need to append to our array
+ for k in marshalled.keys():
+ if k == 'data':
+ self.stream_contents.append(marshalled['data'])
+ else:
+ self.stream_file[k] = marshalled[k]
+
+ self.stream_have_file_info = True
+
+ # Stream directly from "p4 files" into "git fast-import"
+ def streamP4Files(self, files):
filesForCommit = []
filesToRead = []
+ filesToDelete = []
for f in files:
includeFile = True
filesForCommit.append(f)
if f['action'] not in ('delete', 'purge'):
filesToRead.append(f)
+ else:
+ filesToDelete.append(f)
- filedata = []
- if len(filesToRead) > 0:
- filedata = p4CmdList('-x - print',
- stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
- for f in filesToRead]),
- stdin_mode='w+')
-
- if "p4ExitCode" in filedata[0]:
- die("Problems executing p4. Error: [%d]."
- % (filedata[0]['p4ExitCode']));
-
- j = 0;
- contents = {}
- while j < len(filedata):
- stat = filedata[j]
- j += 1
- text = ''
- while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
- text += filedata[j]['data']
- del filedata[j]['data']
- j += 1
-
- if not stat.has_key('depotFile'):
- sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
- continue
+ # deleted files...
+ for f in filesToDelete:
+ self.streamOneP4Deletion(f)
- if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
- text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
- elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
- text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
+ if len(filesToRead) > 0:
+ self.stream_file = {}
+ self.stream_contents = []
+ self.stream_have_file_info = False
- contents[stat['depotFile']] = text
+ # curry self argument
+ def streamP4FilesCbSelf(entry):
+ self.streamP4FilesCb(entry)
- for f in filesForCommit:
- path = f['path']
- if contents.has_key(path):
- f['data'] = contents[path]
+ p4CmdList("-x - print",
+ '\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in filesToRead]),
+ cb=streamP4FilesCbSelf)
- return filesForCommit
+ # do the last chunk
+ if self.stream_file.has_key('depotFile'):
+ self.streamOneP4File(self.stream_file, self.stream_contents)
def commit(self, details, files, branch, branchPrefixes, parent = ""):
epoch = details["time"]
author = details["user"]
+ self.branchPrefixes = branchPrefixes
if self.verbose:
print "commit into %s" % branch
new_files.append (f)
else:
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
- files = self.readP4Files(new_files)
self.gitStream.write("commit %s\n" % branch)
# gitStream.write("mark :%s\n" % details["change"])
print "parent %s" % parent
self.gitStream.write("from %s\n" % parent)
- for file in files:
- if file["type"] == "apple":
- print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
- continue
-
- relPath = self.stripRepoPath(file['path'], branchPrefixes)
- if file["action"] in ("delete", "purge"):
- self.gitStream.write("D %s\n" % relPath)
- else:
- data = file['data']
-
- mode = "644"
- if isP4Exec(file["type"]):
- mode = "755"
- elif file["type"] == "symlink":
- mode = "120000"
- # p4 print on a symlink contains "target\n", so strip it off
- data = data[:-1]
-
- if self.isWindows and file["type"].endswith("text"):
- data = data.replace("\r\n", "\n")
-
- self.gitStream.write("M %s inline %s\n" % (mode, relPath))
- self.gitStream.write("data %s\n" % len(data))
- self.gitStream.write(data)
- self.gitStream.write("\n")
-
+ self.streamP4Files(new_files)
self.gitStream.write("\n")
change = int(details["change"])
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 + 183) / 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);
}
if (ce_uptodate(ce))
continue;
- changed = check_removed(ce, &st);
+ /* If CE_VALID is set, don't look at workdir for file removal */
+ changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st);
if (changed) {
if (changed < 0) {
perror(ce->name);
return 0;
}
-/*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
- */
-static void mark_merge_entries(void)
-{
- int i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= CE_STAGEMASK;
- }
-}
-
/*
* This gets a mix of an existing index and a tree, one pathname entry
* at a time. The index entry may be a single stage-0 one, but it could
struct rev_info *revs = o->unpack_data;
int match_missing, cached;
+ /* if the entry is not checked out, don't examine work tree */
+ cached = o->index_only || (idx && (idx->ce_flags & CE_VALID));
/*
* Backward compatibility wart - "diff-index -m" does
* not mean "do not ignore merges", but "match_missing".
* But with the revision flag parsing, that's found in
* "!revs->ignore_merges".
*/
- cached = o->index_only;
match_missing = !revs->ignore_merges;
if (cached && idx && ce_stage(idx)) {
- if (tree)
- diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+ diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode,
+ idx->sha1);
return;
}
struct unpack_trees_options opts;
struct tree_desc t;
- mark_merge_entries();
-
ent = revs->pending.objects[0].item;
tree_name = revs->pending.objects[0].name;
tree = parse_tree_indirect(ent->sha1);
num = 0;
scale = 1;
dot = 0;
- for(;;) {
+ for (;;) {
ch = *cp;
if ( !dot && ch == '.' ) {
scale = 1;
/*
* This is like 'lstat()', except it refuses to follow symlinks
- * in the path.
+ * in the path, after skipping "skiplen".
*/
-int check_path(const char *path, int len, struct stat *st)
+int check_path(const char *path, int len, struct stat *st, int skiplen)
{
- if (has_symlink_leading_path(path, len)) {
+ const char *slash = path + len;
+
+ while (path < slash && *slash != '/')
+ slash--;
+ if (!has_dirs_only_path(path, slash - path, skiplen)) {
errno = ENOENT;
return -1;
}
strcpy(path + len, ce->name);
len += ce_namelen(ce);
- if (!check_path(path, len, &st)) {
+ if (!check_path(path, len, &st, state->base_dir_len)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
if (!changed)
return 0;
# 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 --";
exit $res
fi
- if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+ if grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
echo "bisect run success"
exit 0;
fi
#endif
#endif
+#include "compat/bswap.h"
+
/* General helper functions */
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
}
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$/ );
resolve_full_httpd
# don't quote $full_httpd, there can be arguments to it (-f)
- $full_httpd "$fqgitdir/gitweb/httpd.conf"
- if test $? != 0; then
- echo "Could not execute http daemon $httpd."
- exit 1
- fi
+ case "$httpd" in
+ *mongoose*)
+ #The mongoose server doesn't have a daemon mode so we'll have to fork it
+ $full_httpd "$fqgitdir/gitweb/httpd.conf" &
+ #Save the pid before doing anything else (we'll print it later)
+ pid=$!
+
+ if test $? != 0; then
+ echo "Could not execute http daemon $httpd."
+ exit 1
+ fi
+
+ cat > "$fqgitdir/pid" <<EOF
+$pid
+EOF
+ ;;
+ *)
+ $full_httpd "$fqgitdir/gitweb/httpd.conf"
+ if test $? != 0; then
+ echo "Could not execute http daemon $httpd."
+ exit 1
+ fi
+ ;;
+ esac
}
stop_httpd () {
fi
}
+mongoose_conf() {
+ cat > "$conf" <<EOF
+# Mongoose web server configuration file.
+# Lines starting with '#' and empty lines are ignored.
+# For detailed description of every option, visit
+# http://code.google.com/p/mongoose/wiki/MongooseManual
+
+root $fqgitdir/gitweb
+ports $port
+index_files gitweb.cgi
+#ssl_cert $fqgitdir/gitweb/ssl_cert.pem
+error_log $fqgitdir/gitweb/error.log
+access_log $fqgitdir/gitweb/access.log
+
+#cgi setup
+cgi_env PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_interp $PERL
+cgi_ext cgi,pl
+
+# mimetype mapping
+mime_types .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
+EOF
+}
+
+
script='
s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
webrick)
webrick_conf
;;
+*mongoose*)
+ mongoose_conf
+ ;;
*)
echo "Unknown httpd specified: $httpd"
exit 1
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
}
}
apply_stash () {
- git update-index -q --refresh &&
- git diff-files --quiet --ignore-submodules ||
- die 'Cannot apply to a dirty working tree, please stage your changes'
-
unstash_index=
while test $# != 0
shift
done
- # current index state
- c_tree=$(git write-tree) ||
- die 'Cannot apply a stash in the middle of a merge'
+ if test $# = 0
+ then
+ have_stash || die 'Nothing to apply'
+ fi
# stash records the work tree, and is a merge between the
# base commit (first parent) and the index tree (second parent).
- s=$(git rev-parse --verify --default $ref_stash "$@") &&
- w_tree=$(git rev-parse --verify "$s:") &&
- b_tree=$(git rev-parse --verify "$s^1:") &&
- i_tree=$(git rev-parse --verify "$s^2:") ||
+ s=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
+ w_tree=$(git rev-parse --quiet --verify "$s:") &&
+ b_tree=$(git rev-parse --quiet --verify "$s^1:") &&
+ i_tree=$(git rev-parse --quiet --verify "$s^2:") ||
die "$*: no valid stashed state found"
+ git update-index -q --refresh &&
+ git diff-files --quiet --ignore-submodules ||
+ die 'Cannot apply to a dirty working tree, please stage your changes'
+
+ # current index state
+ c_tree=$(git write-tree) ||
+ die 'Cannot apply a stash in the middle of a merge'
+
unstashed_index_tree=
if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
test "$c_tree" != "$i_tree"
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
#
# Copyright (c) 2007 Lars Hjemli
-USAGE="[--quiet] [--cached] \
-[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch] [--rebase|--merge]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path>
+ or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
+ or: $dashless [--quiet] init [--] [<path>...]
+ or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+ or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+ or: $dashless [--quiet] foreach [--recursive] <command>
+ or: $dashless [--quiet] sync [--] [<path>...]"
OPTIONS_SPEC=
. git-sh-setup
. git-parse-remote
branch=
reference=
cached=
+files=
nofetch=
update=
+prefix=
# Resolve relative url by appending to parent's url
resolve_relative_url ()
#
cmd_foreach()
{
+ # parse $args after "submodule ... foreach".
+ while test $# -ne 0
+ do
+ case "$1" in
+ -q|--quiet)
+ GIT_QUIET=1
+ ;;
+ --recursive)
+ recursive=1
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+
module_list |
while read mode sha1 stage path
do
if test -e "$path"/.git
then
- say "Entering '$path'"
- (cd "$path" && eval "$@") ||
+ say "Entering '$prefix$path'"
+ name=$(module_name "$path")
+ (
+ prefix="$prefix$path/"
+ unset GIT_DIR
+ cd "$path" &&
+ eval "$@" &&
+ if test -n "$recursive"
+ then
+ cmd_foreach "--recursive" "$@"
+ fi
+ ) ||
die "Stopping at '$path'; script returned non-zero status."
fi
done
cmd_update()
{
# parse $args after "submodule ... update".
+ orig_args="$@"
while test $# -ne 0
do
case "$1" in
shift
update="merge"
;;
+ --recursive)
+ shift
+ recursive=1
+ ;;
--)
shift
break
die "Unable to $action '$sha1' in submodule path '$path'"
say "Submodule path '$path': $msg '$sha1'"
fi
+
+ if test -n "$recursive"
+ then
+ (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
+ die "Failed to recurse into submodule path '$path'"
+ fi
done
}
cmd_summary() {
summary_limit=-1
for_status=
+ diff_cmd=diff-index
# parse $args after "submodule ... summary".
while test $# -ne 0
--cached)
cached="$1"
;;
+ --files)
+ files="$1"
+ ;;
--for-status)
for_status="$1"
;;
head=HEAD
fi
+ if [ -n "$files" ]
+ then
+ test -n "$cached" &&
+ die "--cached cannot be used with --files"
+ diff_cmd=diff-files
+ head=
+ fi
+
cd_to_toplevel
# Get modified modules cared by user
- modules=$(git diff-index $cached --raw $head -- "$@" |
+ modules=$(git $diff_cmd $cached --raw $head -- "$@" |
egrep '^:([0-7]* )?160000' |
while read mod_src mod_dst sha1_src sha1_dst status name
do
test -z "$modules" && return
- git diff-index $cached --raw $head -- $modules |
+ git $diff_cmd $cached --raw $head -- $modules |
egrep '^:([0-7]* )?160000' |
cut -c2- |
while read mod_src mod_dst sha1_src sha1_dst status name
cmd_status()
{
# parse $args after "submodule ... status".
+ orig_args="$@"
while test $# -ne 0
do
case "$1" in
--cached)
cached=1
;;
+ --recursive)
+ recursive=1
+ ;;
--)
shift
break
do
name=$(module_name "$path") || exit
url=$(git config submodule."$name".url)
+ displaypath="$prefix$path"
if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
then
- say "-$sha1 $path"
+ say "-$sha1 $displaypath"
continue;
fi
set_name_rev "$path" "$sha1"
if git diff-files --quiet -- "$path"
then
- say " $sha1 $path$revname"
+ say " $sha1 $displaypath$revname"
else
if test -z "$cached"
then
sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
set_name_rev "$path" "$sha1"
fi
- say "+$sha1 $path$revname"
+ say "+$sha1 $displaypath$revname"
+ fi
+
+ if test -n "$recursive"
+ then
+ (
+ prefix="$displaypath/"
+ unset GIT_DIR
+ cd "$path" &&
+ cmd_status $orig_args
+ ) ||
+ die "Failed to recurse into submodule path '$path'"
fi
done
}
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP },
{ "update-ref", cmd_update_ref, RUN_SETUP },
+ { "update-server-info", cmd_update_server_info, RUN_SETUP },
{ "upload-archive", cmd_upload_archive },
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version },
$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
$feature{'snapshot'}{'override'} = 1;
+If you allow overriding for the snapshot feature, you can specify which
+snapshot formats are globally disabled. You can also add any command line
+options you want (such as setting the compression level). For instance,
+you can disable Zip compressed snapshots and set GZip to run at level 6 by
+adding the following lines to your $GITWEB_CONFIG:
+
+ $known_snapshot_formats{'zip'}{'disabled'} = 1;
+ $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
+
Gitweb repositories
-------------------
# 'suffix' => filename suffix,
# 'format' => --format for git-archive,
# 'compressor' => [compressor command and arguments]
- # (array reference, optional)}
+ # (array reference, optional)
+ # 'disabled' => boolean (optional)}
#
'tgz' => {
'display' => 'tar.gz',
'format' => 'tar',
'compressor' => ['bzip2']},
+ 'txz' => {
+ 'display' => 'tar.xz',
+ 'type' => 'application/x-xz',
+ 'suffix' => '.tar.xz',
+ 'format' => 'tar',
+ 'compressor' => ['xz'],
+ 'disabled' => 1},
+
'zip' => {
'display' => 'zip',
'type' => 'application/x-zip',
our %known_snapshot_format_aliases = (
'gzip' => 'tgz',
'bzip2' => 'tbz2',
+ 'xz' => 'txz',
# backward compatibility: legacy gitweb config support
'x-gzip' => undef, 'gz' => undef,
@{$feature{$name}{'default'}});
if (!$override) { return @defaults; }
if (!defined $sub) {
- warn "feature $name is not overrideable";
+ warn "feature $name is not overridable";
return @defaults;
}
return $sub->(@defaults);
exists $known_snapshot_format_aliases{$_} ?
$known_snapshot_format_aliases{$_} : $_} @fmts;
@fmts = grep {
- exists $known_snapshot_formats{$_} } @fmts;
+ exists $known_snapshot_formats{$_} &&
+ !$known_snapshot_formats{$_}{'disabled'}} @fmts;
}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
$long =~ s/[[:cntrl:]]/?/g;
return $cgi->a({-href => $href, -class => "list subject",
-title => to_utf8($long)},
- esc_html($short) . $extra);
+ esc_html($short)) . $extra;
} else {
return $cgi->a({-href => $href, -class => "list subject"},
- esc_html($long) . $extra);
+ esc_html($long)) . $extra;
}
}
die_error(400, "Invalid snapshot format parameter");
} elsif (!exists($known_snapshot_formats{$format})) {
die_error(400, "Unknown 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");
}
}
/*
- * Uninteresting and pruned commits won't be printed
+ * Otherwise, use get_commit_action() to see if this commit is
+ * interesting
*/
- return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1;
+ return get_commit_action(graph->revs, commit) == commit_show;
}
static struct commit_list *next_interesting_parent(struct git_graph *graph,
free(freq->url);
freq->url = NULL;
}
- freq->slot = NULL;
+ if (freq->slot != NULL) {
+ freq->slot->callback_func = NULL;
+ freq->slot->callback_data = NULL;
+ release_active_slot(freq->slot);
+ freq->slot = NULL;
+ }
}
struct object *obj = parse_object(sha1);
if (!obj)
return 0;
- refname = prettify_refname(refname);
+ if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
+ refname = prettify_refname(refname);
add_name_decoration("", refname, obj);
while (obj->type == OBJ_TAG) {
obj = ((struct tag *)obj)->tagged;
return 0;
}
-void load_ref_decorations(void)
+void load_ref_decorations(int flags)
{
static int loaded;
if (!loaded) {
loaded = 1;
- for_each_ref(add_ref_decoration, NULL);
+ for_each_ref(add_ref_decoration, &flags);
}
}
const char **subject_p,
const char **extra_headers_p,
int *need_8bit_cte_p);
-void load_ref_decorations(void);
+void load_ref_decorations(int flags);
#define FORMAT_PATCH_NAME_MAX 64
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
+++ /dev/null
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Reference Implementation (Compact version)
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-#include "sha1.h"
-
-static void shaHashBlock(moz_SHA_CTX *ctx);
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx) {
- int i;
-
- ctx->lenW = 0;
- ctx->sizeHi = ctx->sizeLo = 0;
-
- /* Initialize H with the magic constants (see FIPS180 for constants)
- */
- ctx->H[0] = 0x67452301;
- ctx->H[1] = 0xefcdab89;
- ctx->H[2] = 0x98badcfe;
- ctx->H[3] = 0x10325476;
- ctx->H[4] = 0xc3d2e1f0;
-
- for (i = 0; i < 80; i++)
- ctx->W[i] = 0;
-}
-
-
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *_dataIn, int len) {
- const unsigned char *dataIn = _dataIn;
- int i;
-
- /* Read the data into W and process blocks as they get full
- */
- for (i = 0; i < len; i++) {
- ctx->W[ctx->lenW / 4] <<= 8;
- ctx->W[ctx->lenW / 4] |= (unsigned int)dataIn[i];
- if ((++ctx->lenW) % 64 == 0) {
- shaHashBlock(ctx);
- ctx->lenW = 0;
- }
- ctx->sizeLo += 8;
- ctx->sizeHi += (ctx->sizeLo < 8);
- }
-}
-
-
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx) {
- unsigned char pad0x80 = 0x80;
- unsigned char pad0x00 = 0x00;
- unsigned char padlen[8];
- int i;
-
- /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
- */
- padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
- padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
- padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
- padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
- padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
- padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
- padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
- padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
- moz_SHA1_Update(ctx, &pad0x80, 1);
- while (ctx->lenW != 56)
- moz_SHA1_Update(ctx, &pad0x00, 1);
- moz_SHA1_Update(ctx, padlen, 8);
-
- /* Output hash
- */
- for (i = 0; i < 20; i++) {
- hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
- ctx->H[i / 4] <<= 8;
- }
-
- /*
- * Re-initialize the context (also zeroizes contents)
- */
- moz_SHA1_Init(ctx);
-}
-
-
-#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n))))
-
-static void shaHashBlock(moz_SHA_CTX *ctx) {
- int t;
- unsigned int A,B,C,D,E,TEMP;
-
- for (t = 16; t <= 79; t++)
- ctx->W[t] =
- SHA_ROT(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
-
- A = ctx->H[0];
- B = ctx->H[1];
- C = ctx->H[2];
- D = ctx->H[3];
- E = ctx->H[4];
-
- for (t = 0; t <= 19; t++) {
- TEMP = SHA_ROT(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 20; t <= 39; t++) {
- TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 40; t <= 59; t++) {
- TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdc;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 60; t <= 79; t++) {
- TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
-
- ctx->H[0] += A;
- ctx->H[1] += B;
- ctx->H[2] += C;
- ctx->H[3] += D;
- ctx->H[4] += E;
-}
+++ /dev/null
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Header File
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-typedef struct {
- unsigned int H[5];
- unsigned int W[80];
- int lenW;
- unsigned int sizeHi,sizeLo;
-} moz_SHA_CTX;
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx);
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *dataIn, int len);
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx);
-
-#define git_SHA_CTX moz_SHA_CTX
-#define git_SHA1_Init moz_SHA1_Init
-#define git_SHA1_Update moz_SHA1_Update
-#define git_SHA1_Final moz_SHA1_Final
} 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;
struct name_decoration *d;
const char *prefix = " (";
- load_ref_decorations();
+ load_ref_decorations(DECORATE_SHORT_REFS);
d = lookup_decoration(&name_decoration, &commit->object);
while (d) {
strbuf_addstr(sb, prefix);
return updated;
}
-int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen)
+static void show_file(const char * fmt, const char * name, int in_porcelain,
+ int * first, char *header_msg)
+{
+ if (in_porcelain && *first && header_msg) {
+ printf("%s\n", header_msg);
+ *first=0;
+ }
+ printf(fmt, name);
+}
+
+int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
+ char *seen, char *header_msg)
{
int i;
int has_errors = 0;
int quiet = (flags & REFRESH_QUIET) != 0;
int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
+ int first = 1;
+ int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
- const char *needs_update_message;
+ const char *needs_update_fmt;
+ const char *needs_merge_fmt;
- needs_update_message = ((flags & REFRESH_SAY_CHANGED)
- ? "locally modified" : "needs update");
+ needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
+ needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce, *new;
int cache_errno = 0;
i--;
if (allow_unmerged)
continue;
- printf("%s: needs merge\n", ce->name);
+ show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg);
has_errors = 1;
continue;
}
}
if (quiet)
continue;
- printf("%s: %s\n", ce->name, needs_update_message);
+ show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg);
has_errors = 1;
continue;
}
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"
revs->simplify_by_decoration = 1;
revs->limited = 1;
revs->prune = 1;
- load_ref_decorations();
+ load_ref_decorations(DECORATE_SHORT_REFS);
} else if (!strcmp(arg, "--date-order")) {
revs->lifo = 0;
revs->topo_order = 1;
return (revs->rewrite_parents || revs->children.name);
}
-enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
{
if (commit->object.flags & SHOWN)
return commit_ignore;
if (!commit->parents || !commit->parents->next)
return commit_ignore;
}
- if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
- return commit_error;
}
return commit_show;
}
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+ enum commit_action action = get_commit_action(revs, commit);
+
+ if (action == commit_show &&
+ !revs->show_all &&
+ revs->prune && revs->dense && want_ancestry(revs)) {
+ if (rewrite_parents(revs, commit) < 0)
+ return commit_error;
+ }
+ return action;
+}
+
static struct commit *get_revision_1(struct rev_info *revs)
{
if (!revs->commits)
#define SYMMETRIC_LEFT (1u<<8)
#define ALL_REV_FLAGS ((1u<<9)-1)
+#define DECORATE_SHORT_REFS 1
+#define DECORATE_FULL_REFS 2
+
struct rev_info;
struct log_info;
commit_error
};
+extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
#endif
--- /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='test read-tree into a fresh index file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >a &&
+ git add a &&
+ git commit -m initial
+'
+
+test_expect_success 'non-existent index file' '
+ rm -f new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_expect_success 'empty index file' '
+ rm -f new-index &&
+ > new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_done
+
'git checkout-index conflicting paths.' \
'test -f path0 && test -d path1 && test -f path1/file1'
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+ mkdir -p tar/get &&
+ ln -s tar/get there &&
+ echo first &&
+ git checkout-index -a -f --prefix=there/ &&
+ echo second &&
+ git checkout-index -a -f --prefix=there/
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='checkout from unborn branch protects contents'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent &&
+ git init &&
+ echo content >file &&
+ git add file &&
+ git commit -m base
+ ) &&
+ git fetch parent master:origin
+'
+
+test_expect_success 'checkout from unborn preserves untracked files' '
+ echo precious >expect &&
+ echo precious >file &&
+ test_must_fail git checkout -b new origin &&
+ test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn preserves index contents' '
+ echo precious >expect &&
+ echo precious >file &&
+ git add file &&
+ test_must_fail git checkout -b new origin &&
+ test_cmp expect file &&
+ git show :file >file &&
+ test_cmp expect file
+'
+
+test_expect_success 'checkout from unborn merges identical index contents' '
+ echo content >file &&
+ git add file &&
+ git checkout -b new origin
+'
+
+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
log -SF master
log -SF -p master
log --decorate --all
+log --decorate=full --all
rev-list --parents HEAD
rev-list --children HEAD
--- /dev/null
+$ git log --decorate=full --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
--- /dev/null
+#!/bin/sh
+
+test_description='diff with assume-unchanged entries'
+
+. ./test-lib.sh
+
+# external diff has been tested in t4020-diff-external.sh
+
+test_expect_success 'setup' '
+ echo zero > zero &&
+ git add zero &&
+ git commit -m zero &&
+ echo one > one &&
+ echo two > two &&
+ git add one two &&
+ git commit -m onetwo &&
+ git update-index --assume-unchanged one &&
+ echo borked >> one &&
+ test "$(git ls-files -v one)" = "h one"
+'
+
+test_expect_success 'diff-index does not examine assume-unchanged entries' '
+ git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+'
+
+test_expect_success 'diff-files does not examine assume-unchanged entries' '
+ rm one &&
+ test -z "$(git diff-files -- one)"
+'
+
+test_done
)
'
+test_expect_success 'simple fetch in shallow repo' '
+ (
+ cd shallow &&
+ git fetch
+ )
+'
+
+test_expect_success 'no changes expected' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow.2 &&
+ cmp count.shallow count.shallow.2
+'
+
+test_expect_success 'fetch same depth in shallow repo' '
+ (
+ cd shallow &&
+ git fetch --depth=2
+ )
+'
+
+test_expect_success 'no changes expected' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow.3 &&
+ cmp count.shallow count.shallow.3
+'
+
test_expect_success 'add two more' '
add B66 $B65 &&
add B67 $B66
)
'
+test_expect_success 'additional simple shallow deepenings' '
+ (
+ cd shallow &&
+ git fetch --depth=8 &&
+ git fetch --depth=10 &&
+ git fetch --depth=11
+ )
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^count: 52" count.shallow
+'
+
test_done
--- /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
--- /dev/null
+#!/bin/sh
+
+test_description='unpack-objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir pub.git &&
+ GIT_DIR=pub.git git init --bare
+ GIT_DIR=pub.git git config receive.fsckobjects true &&
+ mkdir work &&
+ (
+ cd work &&
+ git init &&
+ mkdir -p gar/bage &&
+ (
+ cd gar/bage &&
+ git init &&
+ >junk &&
+ git add junk &&
+ git commit -m "Initial junk"
+ ) &&
+ git add gar/bage &&
+ git commit -m "Initial superproject"
+ )
+'
+
+test_expect_success push '
+ (
+ cd work &&
+ git push ../pub.git master
+ )
+'
+
+test_done
(
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
--- /dev/null
+#!/bin/sh
+
+test_description='--show-all --parents does not rewrite TREESAME commits'
+
+. ./test-lib.sh
+
+test_expect_success 'set up --show-all --parents test' '
+ test_commit one foo.txt &&
+ commit1=`git rev-list -1 HEAD` &&
+ test_commit two bar.txt &&
+ commit2=`git rev-list -1 HEAD` &&
+ test_commit three foo.txt &&
+ commit3=`git rev-list -1 HEAD`
+ '
+
+test_expect_success '--parents rewrites TREESAME parents correctly' '
+ echo $commit3 $commit1 > expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
+ echo $commit3 $commit2 > expected &&
+ echo $commit2 $commit1 >> expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents --show-all HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_done
--- /dev/null
+#!/bin/sh
+
+# There's more than one "correct" way to represent the history graphically.
+# These tests depend on the current behavior of the graphing code. If the
+# graphing code is ever changed to draw the output differently, these tests
+# cases will need to be updated to know about the new layout.
+
+test_description='--graph and simplified history'
+
+. ./test-lib.sh
+
+test_expect_success 'set up rev-list --graph test' '
+ # 3 commits on branch A
+ test_commit A1 foo.txt &&
+ test_commit A2 bar.txt &&
+ test_commit A3 bar.txt &&
+ git branch -m master A &&
+
+ # 2 commits on branch B, started from A1
+ git checkout -b B A1 &&
+ test_commit B1 foo.txt &&
+ test_commit B2 abc.txt &&
+
+ # 2 commits on branch C, started from A2
+ git checkout -b C A2 &&
+ test_commit C1 xyz.txt &&
+ test_commit C2 xyz.txt &&
+
+ # Octopus merge B and C into branch A
+ git checkout A &&
+ git merge B C &&
+ git tag A4
+
+ test_commit A5 bar.txt &&
+
+ # More commits on C, then merge C into A
+ git checkout C &&
+ test_commit C3 foo.txt &&
+ test_commit C4 bar.txt &&
+ git checkout A &&
+ git merge -s ours C &&
+ git tag A6
+
+ test_commit A7 bar.txt &&
+
+ # Store commit names in variables for later use
+ A1=$(git rev-parse --verify A1) &&
+ A2=$(git rev-parse --verify A2) &&
+ A3=$(git rev-parse --verify A3) &&
+ A4=$(git rev-parse --verify A4) &&
+ A5=$(git rev-parse --verify A5) &&
+ A6=$(git rev-parse --verify A6) &&
+ A7=$(git rev-parse --verify A7) &&
+ B1=$(git rev-parse --verify B1) &&
+ B2=$(git rev-parse --verify B2) &&
+ C1=$(git rev-parse --verify C1) &&
+ C2=$(git rev-parse --verify C2) &&
+ C3=$(git rev-parse --verify C3) &&
+ C4=$(git rev-parse --verify C4)
+ '
+
+test_expect_success '--graph --all' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all > actual &&
+ test_cmp expected actual
+ '
+
+# Make sure the graph_is_interesting() code still realizes
+# that undecorated merges are interesting, even with --simplify-by-decoration
+test_expect_success '--graph --simplify-by-decoration' '
+ rm -f expected &&
+ git tag -d A4
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all --simplify-by-decoration > actual &&
+ test_cmp expected actual
+ '
+
+# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success '--graph --simplify-by-decoration prune branch B' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --simplify-by-decoration --all > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --simplify-merges --all \
+ -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --sparse -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --sparse --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C4' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C4 > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+# I don't think the ordering of the boundary commits is really
+# that important, but this test depends on it. If the ordering ever changes
+# in the code, we'll need to update this test.
+test_expect_success '--graph --boundary ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| * | | $B2" >> expected &&
+ echo "| * | | $B1" >> expected &&
+ echo "* | | | $A3" >> expected &&
+ echo "o | | | $A2" >> expected &&
+ echo "|/ / / " >> expected &&
+ echo "o | | $A1" >> expected &&
+ echo " / / " >> expected &&
+ echo "| o $C3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "o $C2" >> expected &&
+ git rev-list --graph --boundary --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+test_done
git bisect start $HASH4 $HASH1 &&
git bisect skip &&
git bisect bad > my_bisect_log.txt &&
- grep "$HASH2 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH2 is the first bad commit" my_bisect_log.txt &&
git bisect reset
'
git bisect good $HASH1 &&
git bisect bad $HASH4 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
git bisect reset'
# We want to automatically find the commit that
chmod +x test_script.sh &&
git bisect start $HASH4 $HASH1 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
git bisect reset'
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
git bisect start $HASH5 $HASH1 &&
git bisect skip &&
git bisect good > my_bisect_log.txt &&
- grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
git bisect log > log_to_replay.txt &&
git bisect reset
'
test_expect_success 'bisect skip and bisect replay' '
git bisect replay log_to_replay.txt > my_bisect_log.txt &&
- grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
git bisect reset
'
chmod +x test_script.sh &&
git bisect start $HASH7 $HASH1 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH6 is first bad commit" my_bisect_log.txt
+ grep "$HASH6 is the first bad commit" my_bisect_log.txt
'
test_expect_success 'bisect skip only one range' '
rev_hash6=$(git rev-parse --verify HEAD) &&
test "$rev_hash6" = "$HASH6" &&
git bisect good > my_bisect_log.txt &&
- grep "$HASH7 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH7 is the first bad commit" my_bisect_log.txt &&
git bisect reset &&
rev_hash6=$(git rev-parse --verify bisect) &&
test "$rev_hash6" = "$HASH6" &&
para1=$(git rev-parse --verify HEAD) &&
test "$para1" = "$PARA_HASH1" &&
git bisect bad > my_bisect_log.txt &&
- grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
+ grep "$PARA_HASH1 is the first bad commit" my_bisect_log.txt
'
test_expect_success 'restricting bisection on one dir and a file' '
para1=$(git rev-parse --verify HEAD) &&
test "$para1" = "$PARA_HASH1" &&
git bisect good > my_bisect_log.txt &&
- grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
+ grep "$PARA_HASH4 is the first bad commit" my_bisect_log.txt
'
test_expect_success 'skipping away from skipped commit' '
--- /dev/null
+#!/bin/sh
+
+test_description='basic work tree status reporting'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B oneside added &&
+ git checkout A^0 &&
+ test_commit C oneside created
+'
+
+test_expect_success 'A/A conflict' '
+ git checkout B^0 &&
+ test_must_fail git merge C
+'
+
+test_expect_success 'Report path with conflict' '
+ git diff --cached --name-status >actual &&
+ echo "U oneside" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Report new path with conflict' '
+ git diff --cached --name-status HEAD^ >actual &&
+ echo "U oneside" >expect &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+# On branch side
+# Unmerged paths:
+# (use "git reset HEAD <file>..." to unstage)
+# (use "git add <file>..." to mark resolution)
+#
+# deleted by us: foo
+#
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success 'M/D conflict does not segfault' '
+ mkdir mdconflict &&
+ (
+ cd mdconflict &&
+ git init &&
+ test_commit initial foo "" &&
+ test_commit modify foo foo &&
+ git checkout -b side HEAD^ &&
+ git rm foo &&
+ git commit -m delete &&
+ test_must_fail git merge master &&
+ test_must_fail git status > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_done
'
cat > expect << EOF
-file2: locally modified
+Unstaged changes after reset:
+M file2
EOF
test_expect_success '--mixed refreshes the index' '
--- /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
EOF
"
+test_expect_success 'modified submodule(forward), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+ > Add foo3
+
+EOF
+"
+
commit_file sm1 &&
cd sm1 &&
git reset --hard HEAD~2 >/dev/null &&
EOF
"
+test_expect_success 'typechanged submodule(submodule->blob), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head5(blob)->$head4(submodule) (3):
+ > Add foo5
+
+EOF
+"
+
rm -rf sm1 &&
git checkout-index sm1
test_expect_success 'typechanged submodule(submodule->blob)' "
EOF
"
+test_expect_success 'fail when using --files together with --cached' "
+ test_must_fail git submodule summary --files --cached
+"
+
test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='Test "git submodule foreach"
+
+This test verifies that "git submodule foreach" correctly visits all submodules
+that are currently checked out.
+'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup a submodule tree' '
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m upstream
+ git clone . super &&
+ git clone super submodule &&
+ (
+ cd super &&
+ git submodule add ../submodule sub1 &&
+ git submodule add ../submodule sub2 &&
+ git submodule add ../submodule sub3 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub1 submodule.foo1 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub2 submodule.foo2 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub3 submodule.foo3 &&
+ git add .gitmodules
+ test_tick &&
+ git commit -m "submodules" &&
+ git submodule init sub1 &&
+ git submodule init sub2 &&
+ git submodule init sub3
+ ) &&
+ (
+ cd submodule &&
+ echo different > file &&
+ git add file &&
+ test_tick &&
+ git commit -m "different"
+ ) &&
+ (
+ cd super &&
+ (
+ cd sub3 &&
+ git pull
+ ) &&
+ git add sub3 &&
+ test_tick &&
+ git commit -m "update sub3"
+ )
+'
+
+sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
+sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
+
+cat > expect <<EOF
+Entering 'sub1'
+foo1-sub1-$sub1sha1
+Entering 'sub3'
+foo3-sub3-$sub3sha1
+EOF
+
+test_expect_success 'test basic "submodule foreach" usage' '
+ git clone super clone &&
+ (
+ cd clone &&
+ git submodule update --init -- sub1 sub3 &&
+ git submodule foreach "echo \$name-\$path-\$sha1" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup nested submodules' '
+ git clone submodule nested1 &&
+ git clone submodule nested2 &&
+ git clone submodule nested3 &&
+ (
+ cd nested3 &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule" &&
+ git submodule init submodule
+ ) &&
+ (
+ cd nested2 &&
+ git submodule add ../nested3 nested3 &&
+ test_tick &&
+ git commit -m "nested3" &&
+ git submodule init nested3
+ ) &&
+ (
+ cd nested1 &&
+ git submodule add ../nested2 nested2 &&
+ test_tick &&
+ git commit -m "nested2" &&
+ git submodule init nested2
+ ) &&
+ (
+ cd super &&
+ git submodule add ../nested1 nested1 &&
+ test_tick &&
+ git commit -m "nested1" &&
+ git submodule init nested1
+ )
+'
+
+test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
+ git clone super clone2 &&
+ (
+ cd clone2 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test ! -d nested1/nested2/.git &&
+ git submodule foreach "git submodule update --init" &&
+ test -d nested1/nested2/.git &&
+ test ! -d nested1/nested2/nested3/.git
+ )
+'
+
+test_expect_success 'use "foreach --recursive" to checkout all submodules' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "git submodule update --init" &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+cat > expect <<EOF
+Entering 'nested1'
+Entering 'nested1/nested2'
+Entering 'nested1/nested2/nested3'
+Entering 'nested1/nested2/nested3/submodule'
+Entering 'sub1'
+Entering 'sub2'
+Entering 'sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "true" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+cat > expect <<EOF
+nested1-nested1
+nested2-nested2
+nested3-nested3
+submodule-submodule
+foo1-sub1
+foo2-sub2
+foo3-sub3
+EOF
+
+test_expect_success 'test "foreach --quiet --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach -q --recursive "echo \$name-\$path" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "update --recursive" to checkout all submodules' '
+ git clone super clone3 &&
+ (
+ cd clone3 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init --recursive &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test -d nested1/nested2/.git &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD)
+sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD)
+sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
+ $nested2sha1 nested1/nested2 (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 sub1 ($sub1sha1_short)
+ $sub2sha1 sub2 ($sub2sha1_short)
+ $sub3sha1 sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive"' '
+ (
+ cd clone3 &&
+ git submodule status --recursive > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "git clone --recursive" to checkout all submodules' '
+ git clone --recursive super clone4 &&
+ test -d clone4/.git &&
+ test -d clone4/sub1/.git &&
+ test -d clone4/sub2/.git &&
+ test -d clone4/sub3/.git &&
+ test -d clone4/nested1/.git &&
+ test -d clone4/nested1/nested2/.git &&
+ test -d clone4/nested1/nested2/nested3/.git &&
+ test -d clone4/nested1/nested2/nested3/submodule/.git
+'
+
+test_done
+++ /dev/null
-ISO8859_5_NAME="¸ÒÐÝ ¿ÕâàÞÒØç ÁØÔÞàÞÒ"
-ISO8859_5_MSG="ÂÕáâÞÒÞÕ áÞÞÑéÕÝØÕ"
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
esac
done
+# Provide an implementation of the 'yes' utility
+yes () {
+ if test $# = 0
+ then
+ y=y
+ else
+ y="$*"
+ fi
+
+ while echo "$y"
+ do
+ :
+ done
+}
+
# Fix some commands on Windows
case $(uname -s) in
*MINGW*)
#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;
}
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_fetch_refs(struct transport *transport, const struct ref *refs)
{
int rc;
- int nr_heads = 0, nr_alloc = 0;
+ int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
const struct ref **heads = NULL;
const struct ref *rm;
for (rm = refs; rm; rm = rm->next) {
+ nr_refs++;
if (rm->peer_ref &&
!hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
continue;
heads[nr_heads++] = rm;
}
+ if (!nr_heads) {
+ /*
+ * When deepening of a shallow repository is requested,
+ * then local and remote refs are likely to still be equal.
+ * Just feed them all to the fetch method in that case.
+ * This condition shouldn't be met in a non-deepening fetch
+ * (see builtin-fetch.c:quickfetch()).
+ */
+ heads = xmalloc(nr_refs * sizeof(*heads));
+ for (rm = refs; rm; rm = rm->next)
+ heads[nr_heads++] = rm;
+ }
+
rc = transport->fetch(transport, nr_heads, heads);
free(heads);
return rc;
+++ /dev/null
-#include "cache.h"
-#include "exec_cmd.h"
-
-static const char update_server_info_usage[] =
-"git update-server-info [--force]";
-
-int main(int ac, char **av)
-{
- int i;
- int force = 0;
- for (i = 1; i < ac; i++) {
- if (av[i][0] == '-') {
- if (!strcmp("--force", av[i]) ||
- !strcmp("-f", av[i]))
- force = 1;
- else
- usage(update_server_info_usage);
- }
- }
- if (i != ac)
- usage(update_server_info_usage);
-
- git_extract_argv0_path(av[0]);
-
- setup_git_directory();
-
- return !!update_server_info(force);
-}
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).
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;
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();
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);
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;
#include "cache.h"
#include "wt-status.h"
-#include "color.h"
#include "object.h"
#include "dir.h"
#include "commit.h"
#include "run-command.h"
#include "remote.h"
-int wt_status_relative_paths = 1;
-int wt_status_use_color = -1;
-static int wt_status_submodule_summary;
-static char wt_status_colors[][COLOR_MAXLEN] = {
+static char default_wt_status_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */
GIT_COLOR_RED, /* WT_STATUS_CHANGED */
GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */
GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */
+ GIT_COLOR_RED, /* WT_STATUS_UNMERGED */
};
-enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-
-static int parse_status_slot(const char *var, int offset)
-{
- if (!strcasecmp(var+offset, "header"))
- return WT_STATUS_HEADER;
- if (!strcasecmp(var+offset, "updated")
- || !strcasecmp(var+offset, "added"))
- return WT_STATUS_UPDATED;
- if (!strcasecmp(var+offset, "changed"))
- return WT_STATUS_CHANGED;
- if (!strcasecmp(var+offset, "untracked"))
- return WT_STATUS_UNTRACKED;
- if (!strcasecmp(var+offset, "nobranch"))
- return WT_STATUS_NOBRANCH;
- die("bad config variable '%s'", var);
-}
-
-static const char *color(int slot)
+static const char *color(int slot, struct wt_status *s)
{
- return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
+ return s->use_color > 0 ? s->color_palette[slot] : "";
}
void wt_status_prepare(struct wt_status *s)
const char *head;
memset(s, 0, sizeof(*s));
+ memcpy(s->color_palette, default_wt_status_colors,
+ sizeof(default_wt_status_colors));
+ s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ s->use_color = -1;
+ s->relative_paths = 1;
head = resolve_ref("HEAD", sha1, 0, NULL);
s->branch = head ? xstrdup(head) : NULL;
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
+ s->change.strdup_strings = 1;
+ s->untracked.strdup_strings = 1;
+}
+
+static void wt_status_print_unmerged_header(struct wt_status *s)
+{
+ const char *c = color(WT_STATUS_HEADER, s);
+ color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+ if (!s->is_initial)
+ color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
+ else
+ color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
+ color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to mark resolution)");
+ color_fprintf_ln(s->fp, c, "#");
}
static void wt_status_print_cached_header(struct wt_status *s)
{
- const char *c = color(WT_STATUS_HEADER);
+ const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
if (!s->is_initial) {
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
static void wt_status_print_dirty_header(struct wt_status *s,
int has_deleted)
{
- const char *c = color(WT_STATUS_HEADER);
+ const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Changed but not updated:");
if (!has_deleted)
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)");
static void wt_status_print_untracked_header(struct wt_status *s)
{
- const char *c = color(WT_STATUS_HEADER);
+ const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Untracked files:");
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
color_fprintf_ln(s->fp, c, "#");
static void wt_status_print_trailer(struct wt_status *s)
{
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
#define quote_path quote_path_relative
-static void wt_status_print_filepair(struct wt_status *s,
- int t, struct diff_filepair *p)
+static void wt_status_print_unmerged_data(struct wt_status *s,
+ struct string_list_item *it)
{
- const char *c = color(t);
+ const char *c = color(WT_STATUS_UNMERGED, s);
+ struct wt_status_change_data *d = it->util;
+ struct strbuf onebuf = STRBUF_INIT;
+ const char *one, *how = "bug";
+
+ one = quote_path(it->string, -1, &onebuf, s->prefix);
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+ switch (d->stagemask) {
+ case 1: how = "both deleted:"; break;
+ case 2: how = "added by us:"; break;
+ case 3: how = "deleted by them:"; break;
+ case 4: how = "added by them:"; break;
+ case 5: how = "deleted by us:"; break;
+ case 6: how = "both added:"; break;
+ case 7: how = "both modified:"; break;
+ }
+ color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+ strbuf_release(&onebuf);
+}
+
+static void wt_status_print_change_data(struct wt_status *s,
+ int change_type,
+ struct string_list_item *it)
+{
+ struct wt_status_change_data *d = it->util;
+ const char *c = color(change_type, s);
+ int status = status;
+ char *one_name;
+ char *two_name;
const char *one, *two;
struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
- one = quote_path(p->one->path, -1, &onebuf, s->prefix);
- two = quote_path(p->two->path, -1, &twobuf, s->prefix);
+ one_name = two_name = it->string;
+ switch (change_type) {
+ case WT_STATUS_UPDATED:
+ status = d->index_status;
+ if (d->head_path)
+ one_name = d->head_path;
+ break;
+ case WT_STATUS_CHANGED:
+ status = d->worktree_status;
+ break;
+ }
- color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
- switch (p->status) {
+ one = quote_path(one_name, -1, &onebuf, s->prefix);
+ two = quote_path(two_name, -1, &twobuf, s->prefix);
+
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+ switch (status) {
case DIFF_STATUS_ADDED:
color_fprintf(s->fp, c, "new file: %s", one);
break;
color_fprintf(s->fp, c, "unmerged: %s", one);
break;
default:
- die("bug: unhandled diff status %c", p->status);
+ die("bug: unhandled diff status %c", status);
}
fprintf(s->fp, "\n");
strbuf_release(&onebuf);
strbuf_release(&twobuf);
}
-static void wt_status_print_updated_cb(struct diff_queue_struct *q,
- struct diff_options *options,
- void *data)
+static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
{
struct wt_status *s = data;
- int shown_header = 0;
int i;
+
+ if (!q->nr)
+ return;
+ s->workdir_dirty = 1;
for (i = 0; i < q->nr; i++) {
- if (q->queue[i]->status == 'U')
- continue;
- if (!shown_header) {
- wt_status_print_cached_header(s);
- s->commitable = 1;
- shown_header = 1;
+ struct diff_filepair *p;
+ struct string_list_item *it;
+ struct wt_status_change_data *d;
+
+ p = q->queue[i];
+ it = string_list_insert(p->one->path, &s->change);
+ d = it->util;
+ if (!d) {
+ d = xcalloc(1, sizeof(*d));
+ it->util = d;
}
- wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
+ if (!d->worktree_status)
+ d->worktree_status = p->status;
}
- if (shown_header)
- wt_status_print_trailer(s);
}
-static void wt_status_print_changed_cb(struct diff_queue_struct *q,
- struct diff_options *options,
- void *data)
+static int unmerged_mask(const char *path)
+{
+ int pos, mask;
+ struct cache_entry *ce;
+
+ pos = cache_name_pos(path, strlen(path));
+ if (0 <= pos)
+ return 0;
+
+ mask = 0;
+ pos = -pos-1;
+ while (pos < active_nr) {
+ ce = active_cache[pos++];
+ if (strcmp(ce->name, path) || !ce_stage(ce))
+ break;
+ mask |= (1 << (ce_stage(ce) - 1));
+ }
+ return mask;
+}
+
+static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
{
struct wt_status *s = data;
int i;
- if (q->nr) {
- int has_deleted = 0;
- s->workdir_dirty = 1;
- for (i = 0; i < q->nr; i++)
- if (q->queue[i]->status == DIFF_STATUS_DELETED) {
- has_deleted = 1;
- break;
- }
- wt_status_print_dirty_header(s, has_deleted);
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p;
+ struct string_list_item *it;
+ struct wt_status_change_data *d;
+
+ p = q->queue[i];
+ it = string_list_insert(p->two->path, &s->change);
+ d = it->util;
+ if (!d) {
+ d = xcalloc(1, sizeof(*d));
+ it->util = d;
+ }
+ if (!d->index_status)
+ d->index_status = p->status;
+ switch (p->status) {
+ case DIFF_STATUS_COPIED:
+ case DIFF_STATUS_RENAMED:
+ d->head_path = xstrdup(p->one->path);
+ break;
+ case DIFF_STATUS_UNMERGED:
+ d->stagemask = unmerged_mask(p->two->path);
+ break;
+ }
}
- for (i = 0; i < q->nr; i++)
- wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
- if (q->nr)
- wt_status_print_trailer(s);
}
-static void wt_status_print_updated(struct wt_status *s)
+static void wt_status_collect_changes_worktree(struct wt_status *s)
{
struct rev_info rev;
+
+ init_revisions(&rev, NULL);
+ setup_revisions(0, NULL, &rev, NULL);
+ rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = wt_status_collect_changed_cb;
+ rev.diffopt.format_callback_data = s;
+ run_diff_files(&rev, 0);
+}
+
+static void wt_status_collect_changes_index(struct wt_status *s)
+{
+ struct rev_info rev;
+
init_revisions(&rev, NULL);
setup_revisions(0, NULL, &rev,
s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = wt_status_print_updated_cb;
+ rev.diffopt.format_callback = wt_status_collect_updated_cb;
rev.diffopt.format_callback_data = s;
rev.diffopt.detect_rename = 1;
rev.diffopt.rename_limit = 200;
run_diff_index(&rev, 1);
}
+static void wt_status_collect_changes_initial(struct wt_status *s)
+{
+ int i;
+
+ for (i = 0; i < active_nr; i++) {
+ struct string_list_item *it;
+ struct wt_status_change_data *d;
+ struct cache_entry *ce = active_cache[i];
+
+ it = string_list_insert(ce->name, &s->change);
+ d = it->util;
+ if (!d) {
+ d = xcalloc(1, sizeof(*d));
+ it->util = d;
+ }
+ if (ce_stage(ce)) {
+ d->index_status = DIFF_STATUS_UNMERGED;
+ d->stagemask |= (1 << (ce_stage(ce) - 1));
+ }
+ else
+ d->index_status = DIFF_STATUS_ADDED;
+ }
+}
+
+static void wt_status_collect_untracked(struct wt_status *s)
+{
+ int i;
+ struct dir_struct dir;
+
+ if (!s->show_untracked_files)
+ return;
+ memset(&dir, 0, sizeof(dir));
+ if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
+ dir.flags |=
+ DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+ setup_standard_excludes(&dir);
+
+ fill_directory(&dir, NULL);
+ for (i = 0; i < dir.nr; i++) {
+ struct dir_entry *ent = dir.entries[i];
+ if (!cache_name_is_other(ent->name, ent->len))
+ continue;
+ s->workdir_untracked = 1;
+ string_list_insert(ent->name, &s->untracked);
+ }
+}
+
+void wt_status_collect(struct wt_status *s)
+{
+ wt_status_collect_changes_worktree(s);
+
+ if (s->is_initial)
+ wt_status_collect_changes_initial(s);
+ else
+ wt_status_collect_changes_index(s);
+ wt_status_collect_untracked(s);
+}
+
+static void wt_status_print_unmerged(struct wt_status *s)
+{
+ int shown_header = 0;
+ int i;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ struct string_list_item *it;
+ it = &(s->change.items[i]);
+ d = it->util;
+ if (!d->stagemask)
+ continue;
+ if (!shown_header) {
+ wt_status_print_unmerged_header(s);
+ shown_header = 1;
+ }
+ wt_status_print_unmerged_data(s, it);
+ }
+ if (shown_header)
+ wt_status_print_trailer(s);
+
+}
+
+static void wt_status_print_updated(struct wt_status *s)
+{
+ int shown_header = 0;
+ int i;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ struct string_list_item *it;
+ it = &(s->change.items[i]);
+ d = it->util;
+ if (!d->index_status ||
+ d->index_status == DIFF_STATUS_UNMERGED)
+ continue;
+ if (!shown_header) {
+ wt_status_print_cached_header(s);
+ s->commitable = 1;
+ shown_header = 1;
+ }
+ wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
+ }
+ if (shown_header)
+ wt_status_print_trailer(s);
+}
+
+/*
+ * -1 : has delete
+ * 0 : no change
+ * 1 : some change but no delete
+ */
+static int wt_status_check_worktree_changes(struct wt_status *s)
+{
+ int i;
+ int changes = 0;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ d = s->change.items[i].util;
+ if (!d->worktree_status ||
+ d->worktree_status == DIFF_STATUS_UNMERGED)
+ continue;
+ changes = 1;
+ if (d->worktree_status == DIFF_STATUS_DELETED)
+ return -1;
+ }
+ return changes;
+}
+
static void wt_status_print_changed(struct wt_status *s)
{
- struct rev_info rev;
- init_revisions(&rev, "");
- setup_revisions(0, NULL, &rev, NULL);
- rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = wt_status_print_changed_cb;
- rev.diffopt.format_callback_data = s;
- run_diff_files(&rev, 0);
+ int i;
+ int worktree_changes = wt_status_check_worktree_changes(s);
+
+ if (!worktree_changes)
+ return;
+
+ wt_status_print_dirty_header(s, worktree_changes < 0);
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ struct string_list_item *it;
+ it = &(s->change.items[i]);
+ d = it->util;
+ if (!d->worktree_status ||
+ d->worktree_status == DIFF_STATUS_UNMERGED)
+ continue;
+ wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
+ }
+ wt_status_print_trailer(s);
}
static void wt_status_print_submodule_summary(struct wt_status *s)
NULL
};
- sprintf(summary_limit, "%d", wt_status_submodule_summary);
+ sprintf(summary_limit, "%d", s->submodule_summary);
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
memset(&sm_summary, 0, sizeof(sm_summary));
static void wt_status_print_untracked(struct wt_status *s)
{
- struct dir_struct dir;
int i;
- int shown_header = 0;
struct strbuf buf = STRBUF_INIT;
- memset(&dir, 0, sizeof(dir));
-
- if (!s->untracked)
- dir.flags |=
- DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
- setup_standard_excludes(&dir);
+ if (!s->untracked.nr)
+ return;
- fill_directory(&dir, NULL);
- for(i = 0; i < dir.nr; i++) {
- struct dir_entry *ent = dir.entries[i];
- if (!cache_name_is_other(ent->name, ent->len))
- continue;
- if (!shown_header) {
- s->workdir_untracked = 1;
- wt_status_print_untracked_header(s);
- shown_header = 1;
- }
- color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
- color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
- quote_path(ent->name, ent->len,
- &buf, s->prefix));
+ wt_status_print_untracked_header(s);
+ for (i = 0; i < s->untracked.nr; i++) {
+ struct string_list_item *it;
+ it = &(s->untracked.items[i]);
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+ color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
+ quote_path(it->string, strlen(it->string),
+ &buf, s->prefix));
}
strbuf_release(&buf);
}
return;
for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
"# %.*s", (int)(ep - cp), cp);
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
void wt_status_print(struct wt_status *s)
{
unsigned char sha1[20];
- const char *branch_color = color(WT_STATUS_HEADER);
+ const char *branch_color = color(WT_STATUS_HEADER, s);
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
if (s->branch) {
branch_name += 11;
else if (!strcmp(branch_name, "HEAD")) {
branch_name = "";
- branch_color = color(WT_STATUS_NOBRANCH);
+ branch_color = color(WT_STATUS_NOBRANCH, s);
on_what = "Not currently on any branch.";
}
- color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
if (!s->is_initial)
wt_status_print_tracking(s);
}
+ wt_status_collect(s);
+
if (s->is_initial) {
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
wt_status_print_updated(s);
+ wt_status_print_unmerged(s);
wt_status_print_changed(s);
- if (wt_status_submodule_summary)
+ if (s->submodule_summary)
wt_status_print_submodule_summary(s);
- if (show_untracked_files)
+ if (s->show_untracked_files)
wt_status_print_untracked(s);
else if (s->commitable)
fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
; /* nothing */
else if (s->workdir_dirty)
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
- else if (s->workdir_untracked)
+ else if (s->untracked.nr)
printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
else if (s->is_initial)
printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
- else if (!show_untracked_files)
+ else if (!s->show_untracked_files)
printf("nothing to commit (use -u to show untracked files)\n");
else
printf("nothing to commit (working directory clean)\n");
}
}
-
-int git_status_config(const char *k, const char *v, void *cb)
-{
- if (!strcmp(k, "status.submodulesummary")) {
- int is_bool;
- wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool);
- if (is_bool && wt_status_submodule_summary)
- wt_status_submodule_summary = -1;
- return 0;
- }
- if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
- wt_status_use_color = git_config_colorbool(k, v, -1);
- return 0;
- }
- if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
- int slot = parse_status_slot(k, 13);
- if (!v)
- return config_error_nonbool(k);
- color_parse(v, k, wt_status_colors[slot]);
- return 0;
- }
- if (!strcmp(k, "status.relativepaths")) {
- wt_status_relative_paths = git_config_bool(k, v);
- return 0;
- }
- if (!strcmp(k, "status.showuntrackedfiles")) {
- if (!v)
- return config_error_nonbool(k);
- else if (!strcmp(v, "no"))
- show_untracked_files = SHOW_NO_UNTRACKED_FILES;
- else if (!strcmp(v, "normal"))
- show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
- else if (!strcmp(v, "all"))
- show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
- else
- return error("Invalid untracked files mode '%s'", v);
- return 0;
- }
- return git_diff_ui_config(k, v, cb);
-}
#define STATUS_H
#include <stdio.h>
+#include "string-list.h"
+#include "color.h"
enum color_wt_status {
- WT_STATUS_HEADER,
+ WT_STATUS_HEADER = 0,
WT_STATUS_UPDATED,
WT_STATUS_CHANGED,
WT_STATUS_UNTRACKED,
WT_STATUS_NOBRANCH,
+ WT_STATUS_UNMERGED,
};
enum untracked_status_type {
SHOW_NORMAL_UNTRACKED_FILES,
SHOW_ALL_UNTRACKED_FILES
};
-extern enum untracked_status_type show_untracked_files;
+
+struct wt_status_change_data {
+ int worktree_status;
+ int index_status;
+ int stagemask;
+ char *head_path;
+};
struct wt_status {
int is_initial;
const char *reference;
int verbose;
int amend;
- int untracked;
int nowarn;
+ int use_color;
+ int relative_paths;
+ int submodule_summary;
+ enum untracked_status_type show_untracked_files;
+ char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
+
/* These are computed during processing of the individual sections */
int commitable;
int workdir_dirty;
const char *index_file;
FILE *fp;
const char *prefix;
+ struct string_list change;
+ struct string_list untracked;
};
-int git_status_config(const char *var, const char *value, void *cb);
-extern int wt_status_use_color;
-extern int wt_status_relative_paths;
void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);
+void wt_status_collect(struct wt_status *s);
#endif /* STATUS_H */
{
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;