Merge branch 'master' of git://repo.or.cz/alt-git

This commit is contained in:
Johannes Sixt
2010-01-21 08:07:29 +01:00
110 changed files with 2875 additions and 1145 deletions

View File

@@ -0,0 +1,28 @@
Git v1.6.5.8 Release Notes
==========================
Fixes since v1.6.5.7
--------------------
* "git count-objects" did not handle packfiles that are bigger than 4G on
platforms with 32-bit off_t.
* "git rebase -i" did not abort cleanly if it failed to launch the editor.
* "git blame" did not work well when commit lacked the author name.
* "git fast-import" choked when handling a tag that points at an object
that is not a commit.
* "git reset --hard" did not work correctly when GIT_WORK_TREE environment
variable is used to point at the root of the true work tree.
* "git grep" fed a buffer that is not NUL-terminated to underlying
regexec().
* "git checkout -m other" while on a branch that does not have any commit
segfaulted, instead of failing.
* "git branch -a other" should have diagnosed the command as an error.
Other minor documentation updates are also included.

View File

@@ -4,12 +4,34 @@ Git v1.6.6.1 Release Notes
Fixes since v1.6.6
------------------
* "git blame" did not work well when commit lacked the author name.
* "git branch -a name" wasn't diagnosed as an error.
* "git count-objects" did not handle packfiles that are bigger than 4G on
platforms with 32-bit off_t.
* "git checkout -m other" while on a branch that does not have any commit
segfaulted, instead of failing.
* "git fast-import" choked when fed a tag that do not point at a
commit.
* "git grep" finding from work tree files could have fed garbage to
the underlying regexec(3).
* "git grep -L" didn't show empty files (they should never match, and
they should always appear in -L output as unmatching).
* "git rebase -i" did not abort cleanly if it failed to launch the editor.
* "git reset --hard" did not work correctly when GIT_WORK_TREE environment
variable is used to point at the root of the true work tree.
* http-backend was not listed in the command list in the documentation.
Other minor documentation updates are included.
* Building on FreeBSD (both 7 and 8) needs OLD_ICONV set in the Makefile
--
exec >/var/tmp/1
O=v1.6.6-4-gd828fdb
echo O=$(git describe maint)
git shortlog --no-merges $O..maint
* "git checkout -m some-branch" while on an unborn branch crashed.
Other minor documentation updates are included.

View File

@@ -50,8 +50,14 @@ Updates since v1.6.6
(portability)
* Some more MSVC portability patches for msysgit port.
* Minimum Pthreads emulation for msysgit port.
(performance)
* More performance improvement patches for msysgit port.
(usability, bells and whistles)
* More commands learned "--quiet" and "--[no-]progress" options.
@@ -66,6 +72,11 @@ Updates since v1.6.6
* HTTP transfer can use authentication scheme other than basic
(i.e./e.g. digest).
* Switching from a version of superproject that used to have a submodule
to another version of superproject that no longer has it did not remove
the submodule directory when it should (namely, when you are not
interested in the submodule at all and didn't clone/checkout).
* "git checkout A...B" is a way to detach HEAD at the merge base between
A and B.
@@ -87,6 +98,10 @@ Updates since v1.6.6
* Use of "git reset --merge" has become easier when resetting away a
conflicted mess left in the work tree.
* "git rerere" had rerere.autoupdate configuration but there was no way
to countermand it from the command line; --no-rerere-autoupdate option
given to "merge", "revert", etc. fixes this.
* "git status" learned "-s(hort)" output format.
(developers)
@@ -102,6 +117,6 @@ release, unless otherwise noted.
--
exec >/var/tmp/1
O=v1.6.6-242-gf287c65
O=v1.6.6-263-ge33fd3c
echo O=$(git describe master)
git shortlog --no-merges $O..master ^maint

View File

@@ -130,6 +130,14 @@ advice.*::
Advice shown when linkgit:git-merge[1] refuses to
merge to avoid overwritting local changes.
Default: true.
resolveConflict::
Advices shown by various commands when conflicts
prevent the operation from being performed.
Default: true.
implicitIdentity::
Advice on how to set your identity configuration when
your information is guessed from the system username and
domain name. Default: true.
--
core.fileMode::
@@ -656,14 +664,6 @@ color.grep::
`never`), never. When set to `true` or `auto`, use color only
when the output is written to the terminal. Defaults to `false`.
color.grep.external::
The string value of this variable is passed to an external 'grep'
command as a command line option if match highlighting is turned
on. If set to an empty string, no option is passed at all,
turning off coloring for external 'grep' calls; this is the default.
For GNU grep, set it to `--color=always` to highlight matches even
when a pager is used.
color.grep.match::
Use customized color for matches. The value of this variable
may be specified as in color.branch.<slot>. It is passed using

View File

@@ -157,6 +157,10 @@ $ git diff -R <2>
rewrites (very expensive).
<2> Output diff in reverse.
SEE ALSO
--------
linkgit:git-difftool[1]::
Show changes using common diff tools
Author
------

View File

@@ -7,7 +7,7 @@ git-difftool - Show changes using common diff tools
SYNOPSIS
--------
'git difftool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<'git diff' options>]
'git difftool' [<options>] <commit>{0,2} [--] [<path>...]
DESCRIPTION
-----------
@@ -58,6 +58,18 @@ is set to the name of the temporary file containing the contents
of the diff post-image. `$BASE` is provided for compatibility
with custom merge tool commands and has the same value as `$LOCAL`.
-x <command>::
--extcmd=<command>::
Specify a custom command for viewing diffs.
'git-difftool' ignores the configured defaults and runs
`$command $LOCAL $REMOTE` when this option is specified.
-g::
--gui::
When 'git-difftool' is invoked with the `-g` or `--gui` option
the default diff tool will be read from the configured
`diff.guitool` variable instead of `diff.tool`.
See linkgit:git-diff[1] for the full list of supported options.
CONFIG VARIABLES
@@ -68,6 +80,9 @@ difftool equivalents have not been defined.
diff.tool::
The default diff tool to use.
diff.guitool::
The default diff tool to use when `--gui` is specified.
difftool.<tool>.path::
Override the path for the given tool. This is useful in case
your tool is not in the PATH.

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
[-m <msg>] <remote>...
[--[no-]rerere-autoupdate] [-m <msg>] <remote>...
'git merge' <msg> HEAD <remote>...
DESCRIPTION
@@ -33,6 +33,11 @@ include::merge-options.txt[]
used to give a good default for automated 'git merge'
invocations.
--rerere-autoupdate::
--no-rerere-autoupdate::
Allow the rerere mechanism to update the index with the
result of auto-conflict resolution if possible.
<remote>...::
Other branch heads to merge into our branch. You need at
least one <remote>. Specifying more than one <remote>

View File

@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-v | --verbose]
[--repo=<repository>] [-f | --force] [-v | --verbose] [-u | --set-upstream]
[<repository> <refspec>...]
DESCRIPTION
@@ -122,6 +122,13 @@ nor in any Push line of the corresponding remotes file---see below).
the name "origin" is used. For this latter case, this option
can be used to override the name "origin". In other words,
the difference between these two commands
-u::
--set-upstream::
For every branch that is up to date or successfully pushed, add
upstream (tracking) reference, used by argument-less
linkgit:git-pull[1] and other commands. For more information,
see 'branch.<name>.merge' in linkgit:git-config[1].
+
--------------------------
git push public #1

View File

@@ -308,6 +308,16 @@ which makes little sense.
root commits will be rewritten to have <newbase> as parent
instead.
--autosquash::
When the commit log message begins with "squash! ..." (or
"fixup! ..."), and there is a commit whose title begins with
the same ..., automatically modify the todo list of rebase -i
so that the commit marked for quashing come right after the
commit to be modified, and change the action of the moved
commit from `pick` to `squash` (or `fixup`).
+
This option is only valid when '--interactive' option is used.
include::merge-strategies.txt[]
NOTES
@@ -382,9 +392,12 @@ If you just want to edit the commit message for a commit, replace the
command "pick" with the command "reword".
If you want to fold two or more commits into one, replace the command
"pick" with "squash" for the second and subsequent commit. If the
commits had different authors, it will attribute the squashed commit to
the author of the first commit.
"pick" for the second and subsequent commits with "squash" or "fixup".
If the commits had different authors, the folded commit will be
attributed to the author of the first commit. The suggested commit
message for the folded commit is the concatenation of the commit
messages of the first commit and of those with the "squash" command,
but omits the commit messages of commits with the "fixup" command.
'git-rebase' will stop when "pick" has been replaced with "edit" or
when a command fails due to merge errors. When you are done editing
@@ -512,8 +525,8 @@ Easy case: The changes are literally the same.::
Hard case: The changes are not the same.::
This happens if the 'subsystem' rebase had conflicts, or used
`\--interactive` to omit, edit, or squash commits; or if the
upstream used one of `commit \--amend`, `reset`, or
`\--interactive` to omit, edit, squash, or fixup commits; or
if the upstream used one of `commit \--amend`, `reset`, or
`filter-branch`.

View File

@@ -112,6 +112,9 @@ OPTIONS
--remotes::
Show tag refs found in `$GIT_DIR/refs/remotes`.
--show-toplevel::
Show the absolute path of the top-level directory.
--show-prefix::
When the command is invoked from a subdirectory, show the
path of the current directory relative to the top-level

View File

@@ -43,14 +43,16 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
* link:v1.6.6/git.html[documentation for release 1.6.6]
* link:v1.6.6.1/git.html[documentation for release 1.6.6.1]
* release notes for
link:RelNotes-1.6.6.1.txt[1.6.6.1],
link:RelNotes-1.6.6.txt[1.6.6].
* link:v1.6.5.7/git.html[documentation for release 1.6.5.7]
* link:v1.6.5.8/git.html[documentation for release 1.6.5.8]
* release notes for
link:RelNotes-1.6.5.8.txt[1.6.5.8],
link:RelNotes-1.6.5.7.txt[1.6.5.7],
link:RelNotes-1.6.5.6.txt[1.6.5.6],
link:RelNotes-1.6.5.5.txt[1.6.5.5],

View File

@@ -27,6 +27,14 @@ interfaces on top of it called "porcelain". You may not want to use the
plumbing directly very often, but it can be good to know what the
plumbing does for when the porcelain isn't flushing.
Back when this document was originally written, many porcelain
commands were shell scripts. For simplicity, it still uses them as
examples to illustrate how plumbing is fit together to form the
porcelain commands. The source tree includes some of these scripts in
contrib/examples/ for reference. Although these are not implemented as
shell scripts anymore, the description of what the plumbing layer
commands do is still valid.
[NOTE]
Deeper technical details are often marked as Notes, which you can
skip on your first reading.

View File

@@ -134,6 +134,7 @@ The placeholders are:
- '%C(...)': color specification, as described in color.branch.* config option
- '%m': left, right or boundary mark
- '%n': newline
- '%%': a raw '%'
- '%x00': print a byte from a hex code
- '%w([<w>[,<i1>[,<i2>]]])': switch line wrapping, like the -w option of
linkgit:git-shortlog[1].

View File

@@ -199,6 +199,10 @@ character if the letter `n` appears after a `%`. The function returns
the length of the placeholder recognized and `strbuf_expand()` skips
over it.
+
The format `%%` is automatically expanded to a single `%` as a quoting
mechanism; callers do not need to handle the `%` placeholder themselves,
and the callback function will not be invoked for this placeholder.
+
All other characters (non-percent and not skipped ones) are copied
verbatim to the strbuf. If the callback returned zero, meaning that the
placeholder is unknown, then the percent sign is copied, too.
@@ -214,6 +218,13 @@ which can be used by the programmer of the callback as she sees fit.
placeholder and replacement string. The array needs to be
terminated by an entry with placeholder set to NULL.
`strbuf_addbuf_percentquote`::
Append the contents of one strbuf to another, quoting any
percent signs ("%") into double-percents ("%%") in the
destination. This is useful for literal data to be fed to either
strbuf_expand or to the *printf family of functions.
`strbuf_addf`::
Add a formatted string to the buffer.

View File

@@ -187,10 +187,6 @@ all::
# is a simplified version of the merge sort used in glibc. This is
# recommended if Git triggers O(n^2) behavior in your platform's qsort().
#
# Define NO_EXTERNAL_GREP if you don't want "git grep" to ever call
# your external grep (e.g., if your system lacks grep, if its grep is
# broken, or spawning external process is slower than built-in grep git has).
#
# Define UNRELIABLE_FSTAT if your system's fstat does not return the same
# information on a not yet closed file that lstat would return for the same
# file after it was closed.
@@ -424,16 +420,6 @@ BUILT_INS += git-stage$X
BUILT_INS += git-status$X
BUILT_INS += git-whatchanged$X
ifdef NO_CURL
REMOTE_CURL_PRIMARY =
REMOTE_CURL_ALIASES =
REMOTE_CURL_NAMES =
else
REMOTE_CURL_PRIMARY = git-remote-http$X
REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
endif
# what 'all' will build and 'install' will install in gitexecdir,
# excluding programs for built-in commands
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
@@ -478,6 +464,7 @@ LIB_H += commit.h
LIB_H += compat/bswap.h
LIB_H += compat/cygwin.h
LIB_H += compat/mingw.h
LIB_H += compat/win32/pthread.h
LIB_H += csum-file.h
LIB_H += decorate.h
LIB_H += delta.h
@@ -510,6 +497,7 @@ LIB_H += reflog-walk.h
LIB_H += refs.h
LIB_H += remote.h
LIB_H += rerere.h
LIB_H += resolve-undo.h
LIB_H += revision.h
LIB_H += run-command.h
LIB_H += sha1-lookup.h
@@ -605,6 +593,7 @@ LIB_OBJS += refs.o
LIB_OBJS += remote.o
LIB_OBJS += replace_object.o
LIB_OBJS += rerere.o
LIB_OBJS += resolve-undo.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
LIB_OBJS += server-info.o
@@ -804,7 +793,6 @@ ifeq ($(uname_S),SunOS)
NO_MKDTEMP = YesPlease
NO_MKSTEMPS = YesPlease
NO_REGEX = YesPlease
NO_EXTERNAL_GREP = YesPlease
THREADED_DELTA_SEARCH = YesPlease
ifeq ($(uname_R),5.7)
NEEDS_RESOLV = YesPlease
@@ -923,7 +911,6 @@ ifeq ($(uname_S),IRIX)
# NO_MMAP. If you suspect that your compiler is not affected by this
# issue, comment out the NO_MMAP statement.
NO_MMAP = YesPlease
NO_EXTERNAL_GREP = UnfortunatelyYes
SNPRINTF_RETURNS_BOGUS = YesPlease
SHELL_PATH = /usr/gnu/bin/bash
NEEDS_LIBGEN = YesPlease
@@ -943,7 +930,6 @@ ifeq ($(uname_S),IRIX64)
# NO_MMAP. If you suspect that your compiler is not affected by this
# issue, comment out the NO_MMAP statement.
NO_MMAP = YesPlease
NO_EXTERNAL_GREP = UnfortunatelyYes
SNPRINTF_RETURNS_BOGUS = YesPlease
SHELL_PATH=/usr/gnu/bin/bash
NEEDS_LIBGEN = YesPlease
@@ -995,15 +981,16 @@ ifeq ($(uname_S),Windows)
OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
NO_REGEX = YesPlease
NO_CURL = YesPlease
NO_PTHREADS = YesPlease
NO_PYTHON = YesPlease
BLK_SHA1 = YesPlease
THREADED_DELTA_SEARCH = YesPlease
CC = compat/vcbuild/scripts/clink.pl
AR = compat/vcbuild/scripts/lib.pl
CFLAGS =
BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o
COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
lib =
@@ -1045,10 +1032,13 @@ ifneq (,$(findstring MINGW,$(uname_S)))
UNRELIABLE_FSTAT = UnfortunatelyYes
OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
NO_REGEX = YesPlease
NO_PYTHON = YesPlease
BLK_SHA1 = YesPlease
THREADED_DELTA_SEARCH = YesPlease
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
compat/win32/pthread.o
EXTLIBS += -lws2_32
X = .exe
ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
@@ -1058,10 +1048,8 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
EXTLIBS += /mingw/lib/libz.a
NO_R_TO_GCC_LINKER = YesPlease
INTERNAL_QSORT = YesPlease
THREADED_DELTA_SEARCH = YesPlease
else
NO_CURL = YesPlease
NO_PTHREADS = YesPlease
endif
endif
@@ -1109,6 +1097,9 @@ endif
ifdef NO_CURL
BASIC_CFLAGS += -DNO_CURL
REMOTE_CURL_PRIMARY =
REMOTE_CURL_ALIASES =
REMOTE_CURL_NAMES =
else
ifdef CURLDIR
# Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@ -1117,6 +1108,9 @@ else
else
CURL_LIBCURL = -lcurl
endif
REMOTE_CURL_PRIMARY = git-remote-http$X
REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
PROGRAMS += $(REMOTE_CURL_NAMES) git-http-fetch$X
curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "070908"
@@ -1350,9 +1344,6 @@ endif
ifdef DIR_HAS_BSD_GROUP_SEMANTICS
COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS
endif
ifdef NO_EXTERNAL_GREP
BASIC_CFLAGS += -DNO_EXTERNAL_GREP
endif
ifdef UNRELIABLE_FSTAT
BASIC_CFLAGS += -DUNRELIABLE_FSTAT
endif
@@ -1789,6 +1780,7 @@ TEST_PROGRAMS_NEED_X += test-genrandom
TEST_PROGRAMS_NEED_X += test-match-trees
TEST_PROGRAMS_NEED_X += test-parse-options
TEST_PROGRAMS_NEED_X += test-path-utils
TEST_PROGRAMS_NEED_X += test-run-command
TEST_PROGRAMS_NEED_X += test-sha1
TEST_PROGRAMS_NEED_X += test-sigchain
TEST_PROGRAMS_NEED_X += test-index-version

View File

@@ -3,6 +3,8 @@
int advice_push_nonfastforward = 1;
int advice_status_hints = 1;
int advice_commit_before_merge = 1;
int advice_resolve_conflict = 1;
int advice_implicit_identity = 1;
static struct {
const char *name;
@@ -11,6 +13,8 @@ static struct {
{ "pushnonfastforward", &advice_push_nonfastforward },
{ "statushints", &advice_status_hints },
{ "commitbeforemerge", &advice_commit_before_merge },
{ "resolveconflict", &advice_resolve_conflict },
{ "implicitidentity", &advice_implicit_identity },
};
int git_default_advice_config(const char *var, const char *value)
@@ -27,3 +31,17 @@ int git_default_advice_config(const char *var, const char *value)
return 0;
}
void NORETURN die_resolve_conflict(const char *me)
{
if (advice_resolve_conflict)
/*
* Message used both when 'git commit' fails and when
* other commands doing a merge do.
*/
die("'%s' is not possible because you have unmerged files.\n"
"Please, fix them up in the work tree, and then use 'git add/rm <file>' as\n"
"appropriate to mark resolution and make a commit, or use 'git commit -a'.", me);
else
die("'%s' is not possible because you have unmerged files.", me);
}

View File

@@ -1,10 +1,16 @@
#ifndef ADVICE_H
#define ADVICE_H
#include "git-compat-util.h"
extern int advice_push_nonfastforward;
extern int advice_status_hints;
extern int advice_commit_before_merge;
extern int advice_resolve_conflict;
extern int advice_implicit_identity;
int git_default_advice_config(const char *var, const char *value);
extern void NORETURN die_resolve_conflict(const char *me);
#endif /* ADVICE_H */

View File

@@ -593,7 +593,7 @@ struct commit_list *filter_skipped(struct commit_list *list,
* is increased by one between each call, but that should not matter
* for this application.
*/
int get_prn(int count) {
static int get_prn(int count) {
count = count * 1103515245 + 12345;
return ((unsigned)(count/65536) % PRN_MODULO);
}
@@ -956,7 +956,7 @@ int bisect_next_all(const char *prefix)
{
struct rev_info revs;
struct commit_list *tried;
int reaches = 0, all = 0, nr;
int reaches = 0, all = 0, nr, steps;
const unsigned char *bisect_rev;
char bisect_rev_hex[41];
@@ -998,8 +998,10 @@ int bisect_next_all(const char *prefix)
}
nr = all - reaches - 1;
printf("Bisecting: %d revisions left to test after this "
"(roughly %d steps)\n", nr, estimate_bisect_steps(all));
steps = estimate_bisect_steps(all);
printf("Bisecting: %d revision%s left to test after this "
"(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
steps, (steps == 1 ? "" : "s"));
return bisect_checkout(bisect_rev_hex);
}

View File

@@ -27,8 +27,6 @@ struct rev_list_info {
const char *header_prefix;
};
extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
extern int bisect_next_all(const char *prefix);
extern int estimate_bisect_steps(int all);

21
blob.c
View File

@@ -23,24 +23,3 @@ int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
item->object.parsed = 1;
return 0;
}
int parse_blob(struct blob *item)
{
enum object_type type;
void *buffer;
unsigned long size;
int ret;
if (item->object.parsed)
return 0;
buffer = read_sha1_file(item->object.sha1, &type, &size);
if (!buffer)
return error("Could not read %s",
sha1_to_hex(item->object.sha1));
if (type != OBJ_BLOB)
return error("Object %s not a blob",
sha1_to_hex(item->object.sha1));
ret = parse_blob_buffer(item, buffer, size);
free(buffer);
return ret;
}

9
blob.h
View File

@@ -13,6 +13,13 @@ struct blob *lookup_blob(const unsigned char *sha1);
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
int parse_blob(struct blob *item);
/**
* Blobs do not contain references to other objects and do not have
* structured data that needs parsing. However, code may use the
* "parsed" bit in the struct object for a blob to determine whether
* its content has been found to actually be available, so
* parse_blob_buffer() is used (by object.c) to flag that the object
* has been read successfully from the database.
**/
#endif /* BLOB_H */

View File

@@ -17,6 +17,7 @@
#include "blob.h"
#include "xdiff-interface.h"
#include "ll-merge.h"
#include "resolve-undo.h"
static const char * const checkout_usage[] = {
"git checkout [options] <branch>",
@@ -234,6 +235,10 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec,
if (report_path_error(ps_matched, pathspec, 0))
return 1;
/* "checkout -m path" to recreate conflicted state */
if (opts->merge)
unmerge_cache(pathspec);
/* Any unmerged paths? */
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
@@ -370,6 +375,7 @@ static int merge_working_tree(struct checkout_opts *opts,
if (read_cache_preload(NULL) < 0)
return error("corrupt index file");
resolve_undo_clear();
if (opts->force) {
ret = reset_tree(new->commit->tree, opts, 1);
if (ret)

View File

@@ -36,7 +36,20 @@ static const char * const builtin_status_usage[] = {
NULL
};
static const char implicit_ident_advice[] =
"Your name and email address were configured automatically based\n"
"on your username and hostname. Please check that they are accurate.\n"
"You can suppress this message by setting them explicitly:\n"
"\n"
" git config --global user.name Your Name\n"
" git config --global user.email you@example.com\n"
"\n"
"If the identity used for this commit is wrong, you can fix it with:\n"
"\n"
" git commit --amend --author='Your Name <you@example.com>'\n";
static unsigned char head_sha1[20];
static char *use_message_buffer;
static const char commit_editmsg[] = "COMMIT_EDITMSG";
static struct lock_file index_lock; /* real index */
@@ -245,6 +258,16 @@ static void create_base_index(void)
exit(128); /* We've already reported the error, finish dying */
}
static void refresh_cache_or_die(int refresh_flags)
{
/*
* refresh_flags contains REFRESH_QUIET, so the only errors
* are for unmerged entries.
*/
if (refresh_cache(refresh_flags | REFRESH_IN_PORCELAIN))
die_resolve_conflict("commit");
}
static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
{
int fd;
@@ -284,7 +307,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
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_flags);
refresh_cache_or_die(refresh_flags);
if (write_cache(fd, active_cache, active_nr) ||
close_lock_file(&index_lock))
die("unable to write new_index file");
@@ -303,7 +326,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix, int
*/
if (!pathspec || !*pathspec) {
fd = hold_locked_index(&index_lock, 1);
refresh_cache(refresh_flags);
refresh_cache_or_die(refresh_flags);
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(&index_lock))
die("unable to write new_index file");
@@ -633,7 +656,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
author_ident);
free(author_ident);
if (!user_ident_explicitly_given)
if (!user_ident_sufficiently_given())
fprintf(fp,
"%s"
"# Committer: %s\n",
@@ -1055,9 +1078,12 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
{
struct rev_info rev;
struct commit *commit;
static const char *format = "format:%h] %s";
struct strbuf format = STRBUF_INIT;
unsigned char junk_sha1[20];
const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
struct pretty_print_context pctx = {0};
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
commit = lookup_commit(sha1);
if (!commit)
@@ -1065,6 +1091,25 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
if (!commit || parse_commit(commit))
die("could not parse newly created commit");
strbuf_addstr(&format, "format:%h] %s");
format_commit_message(commit, "%an <%ae>", &author_ident, &pctx);
format_commit_message(commit, "%cn <%ce>", &committer_ident, &pctx);
if (strbuf_cmp(&author_ident, &committer_ident)) {
strbuf_addstr(&format, "\n Author: ");
strbuf_addbuf_percentquote(&format, &author_ident);
}
if (!user_ident_sufficiently_given()) {
strbuf_addstr(&format, "\n Committer: ");
strbuf_addbuf_percentquote(&format, &committer_ident);
if (advice_implicit_identity) {
strbuf_addch(&format, '\n');
strbuf_addstr(&format, implicit_ident_advice);
}
}
strbuf_release(&author_ident);
strbuf_release(&committer_ident);
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
@@ -1075,7 +1120,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
rev.verbose_header = 1;
rev.show_root_diff = 1;
get_commit_format(format, &rev);
get_commit_format(format.buf, &rev);
rev.always_show_header = 0;
rev.diffopt.detect_rename = 1;
rev.diffopt.rename_limit = 100;
@@ -1094,10 +1139,11 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
struct pretty_print_context ctx = {0};
struct strbuf buf = STRBUF_INIT;
ctx.date_mode = DATE_NORMAL;
format_commit_message(commit, format + 7, &buf, &ctx);
format_commit_message(commit, format.buf + 7, &buf, &ctx);
printf("%s\n", buf.buf);
strbuf_release(&buf);
}
strbuf_release(&format);
}
static int git_commit_config(const char *k, const char *v, void *cb)
@@ -1255,7 +1301,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"new_index file. Check that disk is not full or quota is\n"
"not exceeded, and then \"git reset HEAD\" to recover.");
rerere();
rerere(0);
run_hook(get_index_file(), "post-commit", NULL);
if (!quiet)
print_summary(prefix, commit_sha1);

View File

@@ -14,14 +14,7 @@
#include "userdiff.h"
#include "grep.h"
#include "quote.h"
#ifndef NO_EXTERNAL_GREP
#ifdef __unix__
#define NO_EXTERNAL_GREP 0
#else
#define NO_EXTERNAL_GREP 1
#endif
#endif
#include "dir.h"
static char const * const grep_usage[] = {
"git grep [options] [-e] <pattern> [<rev>...] [[--] path...]",
@@ -42,8 +35,6 @@ static int grep_config(const char *var, const char *value, void *cb)
opt->color = git_config_colorbool(var, value, -1);
return 0;
}
if (!strcmp(var, "color.grep.external"))
return git_config_string(&(opt->color_external), var, value);
if (!strcmp(var, "color.grep.match")) {
if (!value)
return config_error_nonbool(var);
@@ -205,6 +196,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
return 0;
}
close(i);
data[sz] = 0;
if (opt->relative && opt->prefix_length)
filename = quote_path_relative(filename, -1, &buf, opt->prefix);
i = grep_buffer(opt, filename, data, sz);
@@ -213,309 +205,12 @@ static int grep_file(struct grep_opt *opt, const char *filename)
return i;
}
#if !NO_EXTERNAL_GREP
static int exec_grep(int argc, const char **argv)
{
pid_t pid;
int status;
argv[argc] = NULL;
trace_argv_printf(argv, "trace: grep:");
pid = fork();
if (pid < 0)
return pid;
if (!pid) {
execvp("grep", (char **) argv);
exit(255);
}
while (waitpid(pid, &status, 0) < 0) {
if (errno == EINTR)
continue;
return -1;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
return 1;
return 0;
}
return -1;
}
#define MAXARGS 1000
#define ARGBUF 4096
#define push_arg(a) do { \
if (nr < MAXARGS) argv[nr++] = (a); \
else die("maximum number of args exceeded"); \
} while (0)
/*
* If you send a singleton filename to grep, it does not give
* the name of the file. GNU grep has "-H" but we would want
* that behaviour in a portable way.
*
* So we keep two pathnames in argv buffer unsent to grep in
* the main loop if we need to do more than one grep.
*/
static int flush_grep(struct grep_opt *opt,
int argc, int arg0, const char **argv, int *kept)
{
int status;
int count = argc - arg0;
const char *kept_0 = NULL;
if (count <= 2) {
/*
* Because we keep at least 2 paths in the call from
* the main loop (i.e. kept != NULL), and MAXARGS is
* far greater than 2, this usually is a call to
* conclude the grep. However, the user could attempt
* to overflow the argv buffer by giving too many
* options to leave very small number of real
* arguments even for the call in the main loop.
*/
if (kept)
die("insanely many options to grep");
/*
* If we have two or more paths, we do not have to do
* anything special, but we need to push /dev/null to
* get "-H" behaviour of GNU grep portably but when we
* are not doing "-l" nor "-L" nor "-c".
*/
if (count == 1 &&
!opt->name_only &&
!opt->unmatch_name_only &&
!opt->count) {
argv[argc++] = "/dev/null";
argv[argc] = NULL;
}
}
else if (kept) {
/*
* Called because we found many paths and haven't finished
* iterating over the cache yet. We keep two paths
* for the concluding call. argv[argc-2] and argv[argc-1]
* has the last two paths, so save the first one away,
* replace it with NULL while sending the list to grep,
* and recover them after we are done.
*/
*kept = 2;
kept_0 = argv[argc-2];
argv[argc-2] = NULL;
argc -= 2;
}
if (opt->pre_context || opt->post_context) {
/*
* grep handles hunk marks between files, but we need to
* do that ourselves between multiple calls.
*/
if (opt->show_hunk_mark)
write_or_die(1, "--\n", 3);
else
opt->show_hunk_mark = 1;
}
status = exec_grep(argc, argv);
if (kept_0) {
/*
* Then recover them. Now the last arg is beyond the
* terminating NULL which is at argc, and the second
* from the last is what we saved away in kept_0
*/
argv[arg0++] = kept_0;
argv[arg0] = argv[argc+1];
}
return status;
}
static void grep_add_color(struct strbuf *sb, const char *escape_seq)
{
size_t orig_len = sb->len;
while (*escape_seq) {
if (*escape_seq == 'm')
strbuf_addch(sb, ';');
else if (*escape_seq != '\033' && *escape_seq != '[')
strbuf_addch(sb, *escape_seq);
escape_seq++;
}
if (sb->len > orig_len && sb->buf[sb->len - 1] == ';')
strbuf_setlen(sb, sb->len - 1);
}
static int has_skip_worktree_entry(struct grep_opt *opt, const char **paths)
{
int nr;
for (nr = 0; nr < active_nr; nr++) {
struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
if (ce_skip_worktree(ce))
return 1;
}
return 0;
}
static int external_grep(struct grep_opt *opt, const char **paths, int cached)
{
int i, nr, argc, hit, len, status;
const char *argv[MAXARGS+1];
char randarg[ARGBUF];
char *argptr = randarg;
struct grep_pat *p;
if (opt->extended || (opt->relative && opt->prefix_length)
|| has_skip_worktree_entry(opt, paths))
return -1;
len = nr = 0;
push_arg("grep");
if (opt->fixed)
push_arg("-F");
if (opt->linenum)
push_arg("-n");
if (!opt->pathname)
push_arg("-h");
if (opt->regflags & REG_EXTENDED)
push_arg("-E");
if (opt->ignore_case)
push_arg("-i");
if (opt->binary == GREP_BINARY_NOMATCH)
push_arg("-I");
if (opt->word_regexp)
push_arg("-w");
if (opt->name_only)
push_arg("-l");
if (opt->unmatch_name_only)
push_arg("-L");
if (opt->null_following_name)
/* in GNU grep git's "-z" translates to "-Z" */
push_arg("-Z");
if (opt->count)
push_arg("-c");
if (opt->post_context || opt->pre_context) {
if (opt->post_context != opt->pre_context) {
if (opt->pre_context) {
push_arg("-B");
len += snprintf(argptr, sizeof(randarg)-len,
"%u", opt->pre_context) + 1;
if (sizeof(randarg) <= len)
die("maximum length of args exceeded");
push_arg(argptr);
argptr += len;
}
if (opt->post_context) {
push_arg("-A");
len += snprintf(argptr, sizeof(randarg)-len,
"%u", opt->post_context) + 1;
if (sizeof(randarg) <= len)
die("maximum length of args exceeded");
push_arg(argptr);
argptr += len;
}
}
else {
push_arg("-C");
len += snprintf(argptr, sizeof(randarg)-len,
"%u", opt->post_context) + 1;
if (sizeof(randarg) <= len)
die("maximum length of args exceeded");
push_arg(argptr);
argptr += len;
}
}
for (p = opt->pattern_list; p; p = p->next) {
push_arg("-e");
push_arg(p->pattern);
}
if (opt->color) {
struct strbuf sb = STRBUF_INIT;
grep_add_color(&sb, opt->color_match);
setenv("GREP_COLOR", sb.buf, 1);
strbuf_reset(&sb);
strbuf_addstr(&sb, "mt=");
grep_add_color(&sb, opt->color_match);
strbuf_addstr(&sb, ":sl=:cx=:fn=:ln=:bn=:se=");
setenv("GREP_COLORS", sb.buf, 1);
strbuf_release(&sb);
if (opt->color_external && strlen(opt->color_external) > 0)
push_arg(opt->color_external);
} else {
unsetenv("GREP_COLOR");
unsetenv("GREP_COLORS");
}
unsetenv("GREP_OPTIONS");
hit = 0;
argc = nr;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
char *name;
int kept;
if (!S_ISREG(ce->ce_mode))
continue;
if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
name = ce->name;
if (name[0] == '-') {
int len = ce_namelen(ce);
name = xmalloc(len + 3);
memcpy(name, "./", 2);
memcpy(name + 2, ce->name, len + 1);
}
argv[argc++] = name;
if (MAXARGS <= argc) {
status = flush_grep(opt, argc, nr, argv, &kept);
if (0 < status)
hit = 1;
argc = nr + kept;
}
if (ce_stage(ce)) {
do {
i++;
} while (i < active_nr &&
!strcmp(ce->name, active_cache[i]->name));
i--; /* compensate for loop control */
}
}
if (argc > nr) {
status = flush_grep(opt, argc, nr, argv, NULL);
if (0 < status)
hit = 1;
}
return hit;
}
#endif
static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
int external_grep_allowed)
static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
{
int hit = 0;
int nr;
read_cache();
#if !NO_EXTERNAL_GREP
/*
* Use the external "grep" command for the case where
* we grep through the checked-out files. It tends to
* be a lot more optimized
*/
if (!cached && external_grep_allowed) {
hit = external_grep(opt, paths, cached);
if (hit >= 0)
return hit;
hit = 0;
}
#endif
for (nr = 0; nr < active_nr; nr++) {
struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
@@ -625,6 +320,21 @@ static int grep_object(struct grep_opt *opt, const char **paths,
die("unable to grep from object of type %s", typename(obj->type));
}
static int grep_directory(struct grep_opt *opt, const char **paths)
{
struct dir_struct dir;
int i, hit = 0;
memset(&dir, 0, sizeof(dir));
setup_standard_excludes(&dir);
fill_directory(&dir, paths);
for (i = 0; i < dir.nr; i++)
hit |= grep_file(opt, dir.entries[i]->name);
free_grep_patterns(opt);
return hit;
}
static int context_callback(const struct option *opt, const char *arg,
int unset)
{
@@ -712,16 +422,19 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
{
int hit = 0;
int cached = 0;
int external_grep_allowed = 1;
int seen_dashdash = 0;
int external_grep_allowed__ignored;
struct grep_opt opt;
struct object_array list = { 0, 0, NULL };
const char **paths = NULL;
int i;
int dummy;
int nongit = 0, use_index = 1;
struct option options[] = {
OPT_BOOLEAN(0, "cached", &cached,
"search in index instead of in the work tree"),
OPT_BOOLEAN(0, "index", &use_index,
"--no-index finds in contents not managed by git"),
OPT_GROUP(""),
OPT_BOOLEAN('v', "invert-match", &opt.invert,
"show non-matching lines"),
@@ -795,18 +508,15 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "all-match", &opt.all_match,
"show only matches from files that match all patterns"),
OPT_GROUP(""),
#if NO_EXTERNAL_GREP
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
"allow calling of grep(1) (ignored by this build)"),
#else
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed,
"allow calling of grep(1) (default)"),
#endif
OPT_BOOLEAN(0, "ext-grep", &external_grep_allowed__ignored,
"allow calling of grep(1) (ignored by this build)"),
{ OPTION_CALLBACK, 0, "help-all", &options, NULL, "show usage",
PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
OPT_END()
};
prefix = setup_git_directory_gently(&nongit);
/*
* 'git grep -h', unlike 'git grep -h <pattern>', is a request
* to show usage information and exit.
@@ -844,6 +554,10 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
PARSE_OPT_STOP_AT_NON_OPTION |
PARSE_OPT_NO_INTERNAL_HELP);
if (use_index && nongit)
/* die the same way as if we did it at the beginning */
setup_git_directory();
/* First unrecognized non-option token */
if (argc > 0 && !opt.pattern_list) {
append_grep_pattern(&opt, argv[0], "command line", 0,
@@ -852,8 +566,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
argc--;
}
if ((opt.color && !opt.color_external) || opt.funcname)
external_grep_allowed = 0;
if (!opt.pattern_list)
die("no pattern given.");
if (!opt.fixed && opt.ignore_case)
@@ -896,10 +608,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
paths[1] = NULL;
}
if (!use_index) {
if (cached)
die("--cached cannot be used with --no-index.");
if (list.nr)
die("--no-index cannot be used with revs.");
return !grep_directory(&opt, paths);
}
if (!list.nr) {
if (!cached)
setup_work_tree();
return !grep_cache(&opt, paths, cached, external_grep_allowed);
return !grep_cache(&opt, paths, cached);
}
if (cached)

View File

@@ -11,6 +11,8 @@
#include "builtin.h"
#include "tree.h"
#include "parse-options.h"
#include "resolve-undo.h"
#include "string-list.h"
static int abbrev;
static int show_deleted;
@@ -18,6 +20,7 @@ static int show_cached;
static int show_others;
static int show_stage;
static int show_unmerged;
static int show_resolve_undo;
static int show_modified;
static int show_killed;
static int show_valid_bit;
@@ -38,6 +41,7 @@ static const char *tag_other = "";
static const char *tag_killed = "";
static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
@@ -156,6 +160,38 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
write_name_quoted(ce->name + offset, stdout, line_terminator);
}
static int show_one_ru(struct string_list_item *item, void *cbdata)
{
int offset = prefix_offset;
const char *path = item->string;
struct resolve_undo_info *ui = item->util;
int i, len;
len = strlen(path);
if (len < prefix_len)
return 0; /* outside of the prefix */
if (!match_pathspec(pathspec, path, len, prefix_len, ps_matched))
return 0; /* uninterested */
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
continue;
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
abbrev
? find_unique_abbrev(ui->sha1[i], abbrev)
: sha1_to_hex(ui->sha1[i]),
i + 1);
write_name_quoted(path + offset, stdout, line_terminator);
}
return 0;
}
static void show_ru_info(const char *prefix)
{
if (!the_index.resolve_undo)
return;
for_each_string_list(show_one_ru, the_index.resolve_undo, NULL);
}
static void show_files(struct dir_struct *dir, const char *prefix)
{
int i;
@@ -458,6 +494,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
DIR_HIDE_EMPTY_DIRECTORIES),
OPT_BOOLEAN('u', "unmerged", &show_unmerged,
"show unmerged files in the output"),
OPT_BOOLEAN(0, "resolve-undo", &show_resolve_undo,
"show resolve-undo information"),
{ OPTION_CALLBACK, 'x', "exclude", &dir.exclude_list[EXC_CMDL], "pattern",
"skip files matching pattern",
0, option_parse_exclude },
@@ -498,6 +536,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
tag_other = "? ";
tag_killed = "K ";
tag_skip_worktree = "S ";
tag_resolve_undo = "U ";
}
if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
require_work_tree = 1;
@@ -536,7 +575,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
/* With no flags, we default to showing the cached files */
if (!(show_stage | show_deleted | show_others | show_unmerged |
show_killed | show_modified))
show_killed | show_modified | show_resolve_undo))
show_cached = 1;
if (prefix)
@@ -551,6 +590,8 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
overlay_tree_on_cache(with_tree, prefix);
}
show_files(&dir, prefix);
if (show_resolve_undo)
show_ru_info(prefix);
if (ps_matched) {
int bad;

View File

@@ -24,6 +24,7 @@
#include "rerere.h"
#include "help.h"
#include "merge-recursive.h"
#include "resolve-undo.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
@@ -52,6 +53,7 @@ static struct strategy **use_strategies;
static size_t use_strategies_nr, use_strategies_alloc;
static const char *branch;
static int verbosity;
static int allow_rerere_auto;
static struct strategy all_strategy[] = {
{ "recursive", DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -170,6 +172,7 @@ static struct option builtin_merge_options[] = {
"allow fast-forward (default)"),
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
"abort if fast-forward is not possible"),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
"merge strategy to use", option_parse_strategy),
OPT_CALLBACK('m', "message", &merge_msg, "message",
@@ -604,6 +607,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
discard_cache();
if (read_cache() < 0)
die("failed to read the cache");
resolve_undo_clear();
return ret;
}
}
@@ -618,11 +622,10 @@ static void count_diff_files(struct diff_queue_struct *q,
static int count_unmerged_entries(void)
{
const struct index_state *state = &the_index;
int i, ret = 0;
for (i = 0; i < state->cache_nr; i++)
if (ce_stage(state->cache[i]))
for (i = 0; i < active_nr; i++)
if (ce_stage(active_cache[i]))
ret++;
return ret;
@@ -790,7 +793,7 @@ static int suggest_conflicts(void)
}
}
fclose(fp);
rerere();
rerere(allow_rerere_auto);
printf("Automatic merge failed; "
"fix conflicts and then commit the result.\n");
return 1;
@@ -847,12 +850,22 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
const char *best_strategy = NULL, *wt_strategy = NULL;
struct commit_list **remotes = &remoteheads;
if (file_exists(git_path("MERGE_HEAD")))
die("You have not concluded your merge. (MERGE_HEAD exists)");
if (read_cache_unmerged())
die("You are in the middle of a conflicted merge."
" (index unmerged)");
if (read_cache_unmerged()) {
die_resolve_conflict("merge");
}
if (file_exists(git_path("MERGE_HEAD"))) {
/*
* There is no unmerged entry, don't advise 'git
* add/rm <file>', just 'git commit'.
*/
if (advice_resolve_conflict)
die("You have not concluded your merge (MERGE_HEAD exists).\n"
"Please, commit your changes before you can merge.");
else
die("You have not concluded your merge (MERGE_HEAD exists).");
}
resolve_undo_clear();
/*
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.

View File

@@ -1256,15 +1256,15 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
#ifdef THREADED_DELTA_SEARCH
static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t read_mutex;
#define read_lock() pthread_mutex_lock(&read_mutex)
#define read_unlock() pthread_mutex_unlock(&read_mutex)
static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t cache_mutex;
#define cache_lock() pthread_mutex_lock(&cache_mutex)
#define cache_unlock() pthread_mutex_unlock(&cache_mutex)
static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t progress_mutex;
#define progress_lock() pthread_mutex_lock(&progress_mutex)
#define progress_unlock() pthread_mutex_unlock(&progress_mutex)
@@ -1591,7 +1591,26 @@ struct thread_params {
unsigned *processed;
};
static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER;
static pthread_cond_t progress_cond;
/*
* Mutex and conditional variable can't be statically-initialized on Windows.
*/
static void init_threaded_search(void)
{
pthread_mutex_init(&read_mutex, NULL);
pthread_mutex_init(&cache_mutex, NULL);
pthread_mutex_init(&progress_mutex, NULL);
pthread_cond_init(&progress_cond, NULL);
}
static void cleanup_threaded_search(void)
{
pthread_cond_destroy(&progress_cond);
pthread_mutex_destroy(&read_mutex);
pthread_mutex_destroy(&cache_mutex);
pthread_mutex_destroy(&progress_mutex);
}
static void *threaded_find_deltas(void *arg)
{
@@ -1630,10 +1649,13 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
struct thread_params *p;
int i, ret, active_threads = 0;
init_threaded_search();
if (!delta_search_threads) /* --threads=0 means autodetect */
delta_search_threads = online_cpus();
if (delta_search_threads <= 1) {
find_deltas(list, &list_size, window, depth, processed);
cleanup_threaded_search();
return;
}
if (progress > pack_to_stdout)
@@ -1748,6 +1770,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
active_threads--;
}
}
cleanup_threaded_search();
free(p);
}

View File

@@ -218,6 +218,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
OPT_STRING( 0 , "exec", &receivepack, "receive-pack", "receive pack program"),
OPT_BIT('u', "set-upstream", &flags, "set upstream for git pull/status",
TRANSPORT_PUSH_SET_UPSTREAM),
OPT_END()
};

View File

@@ -13,6 +13,7 @@
#include "dir.h"
#include "builtin.h"
#include "parse-options.h"
#include "resolve-undo.h"
static int nr_trees;
static struct tree *trees[MAX_UNPACK_TREES];
@@ -124,6 +125,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
die("You need to resolve your current index first");
stage = opts.merge = 1;
}
resolve_undo_clear();
for (i = 0; i < argc; i++) {
const char *arg = argv[i];

View File

@@ -103,15 +103,27 @@ static int diff_two(const char *file1, const char *label1,
int cmd_rerere(int argc, const char **argv, const char *prefix)
{
struct string_list merge_rr = { NULL, 0, 0, 1 };
int i, fd;
int i, fd, flags = 0;
if (2 < argc) {
if (!strcmp(argv[1], "-h"))
usage(git_rerere_usage);
if (!strcmp(argv[1], "--rerere-autoupdate"))
flags = RERERE_AUTOUPDATE;
else if (!strcmp(argv[1], "--no-rerere-autoupdate"))
flags = RERERE_NOAUTOUPDATE;
if (flags) {
argc--;
argv++;
}
}
if (argc < 2)
return rerere();
return rerere(flags);
if (!strcmp(argv[1], "-h"))
usage(git_rerere_usage);
if (!strcmp(argv[1], "forget"))
return rerere_forget(argv + 2);
fd = setup_rerere(&merge_rr);
fd = setup_rerere(&merge_rr, flags);
if (fd < 0)
return 0;

View File

@@ -253,7 +253,7 @@ static void print_var_int(const char *var, int val)
printf("%s=%d\n", var, val);
}
int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
{
int cnt, flags = info->bisect_show_flags;
char hex[41] = "";

View File

@@ -581,6 +581,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
for_each_remote_ref(show_reference, NULL);
continue;
}
if (!strcmp(arg, "--show-toplevel")) {
const char *work_tree = get_git_work_tree();
if (work_tree)
puts(work_tree);
continue;
}
if (!strcmp(arg, "--show-prefix")) {
if (prefix)
puts(prefix);

View File

@@ -38,6 +38,7 @@ static const char * const cherry_pick_usage[] = {
static int edit, no_replay, no_commit, mainline, signoff;
static enum { REVERT, CHERRY_PICK } action;
static struct commit *commit;
static int allow_rerere_auto;
static const char *me;
@@ -57,6 +58,7 @@ static void parse_args(int argc, const char **argv)
OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_INTEGER('m', "mainline", &mainline, "parent number"),
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
OPT_END(),
};
@@ -233,6 +235,19 @@ static struct tree *empty_tree(void)
return tree;
}
static NORETURN void die_dirty_index(const char *me)
{
if (read_cache_unmerged()) {
die_resolve_conflict(me);
} else {
if (advice_commit_before_merge)
die("Your local changes would be overwritten by %s.\n"
"Please, commit your changes or stash them to proceed.", me);
else
die("Your local changes would be overwritten by %s.\n", me);
}
}
static int revert_or_cherry_pick(int argc, const char **argv)
{
unsigned char head[20];
@@ -269,7 +284,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
if (get_sha1("HEAD", head))
die ("You do not have a valid HEAD");
if (index_differs_from("HEAD", 0))
die ("Dirty index: cannot %s", me);
die_dirty_index(me);
}
discard_cache();
@@ -395,7 +410,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
die ("Error wrapping up %s", defmsg);
fprintf(stderr, "Automatic %s failed.%s\n",
me, help_msg(commit->object.sha1));
rerere();
rerere(allow_rerere_auto);
exit(1);
}
if (commit_lock_file(&msg_file) < 0)

View File

@@ -406,52 +406,22 @@ int send_pack(struct send_pack_args *args,
*/
new_refs = 0;
for (ref = remote_refs; ref; ref = ref->next) {
if (ref->peer_ref)
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
else if (!args->send_mirror)
if (!ref->peer_ref && !args->send_mirror)
continue;
ref->deletion = is_null_sha1(ref->new_sha1);
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
case REF_STATUS_UPTODATE:
continue;
default:
; /* do nothing */
}
if (ref->deletion && !allow_deleting_refs) {
ref->status = REF_STATUS_REJECT_NODELETE;
continue;
}
if (!ref->deletion &&
!hashcmp(ref->old_sha1, ref->new_sha1)) {
ref->status = REF_STATUS_UPTODATE;
continue;
}
/* This part determines what can overwrite what.
* The rules are:
*
* (0) you can always use --force or +A:B notation to
* selectively force individual ref pairs.
*
* (1) if the old thing does not exist, it is OK.
*
* (2) if you do not have the old thing, you are not allowed
* to overwrite it; you would not know what you are losing
* otherwise.
*
* (3) if both new and old are commit-ish, and new is a
* descendant of old, it is OK.
*
* (4) regardless of all of the above, removing :B is
* always allowed.
*/
ref->nonfastforward =
!ref->deletion &&
!is_null_sha1(ref->old_sha1) &&
(!has_sha1_file(ref->old_sha1)
|| !ref_newer(ref->new_sha1, ref->old_sha1));
if (ref->nonfastforward && !ref->force && !args->force_update) {
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
continue;
}
if (!ref->deletion)
new_refs++;
@@ -673,6 +643,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
if (match_refs(local_refs, &remote_refs, nr_refspecs, refspecs, flags))
return -1;
set_ref_status_for_push(remote_refs, args.send_mirror,
args.force_update);
ret = send_pack(&args, fd, conn, remote_refs, &extra_have);
if (helper_status)

View File

@@ -9,6 +9,7 @@
#include "tree-walk.h"
#include "builtin.h"
#include "refs.h"
#include "resolve-undo.h"
/*
* Default to not allowing changes to the list of files. The
@@ -440,7 +441,18 @@ static int unresolve_one(const char *path)
/* See if there is such entry in the index. */
pos = cache_name_pos(path, namelen);
if (pos < 0) {
if (0 <= pos) {
/* already merged */
pos = unmerge_cache_entry_at(pos);
if (pos < active_nr) {
struct cache_entry *ce = active_cache[pos];
if (ce_stage(ce) &&
ce_namelen(ce) == namelen &&
!memcmp(ce->name, path, namelen))
return 0;
}
/* no resolve-undo information; fall back */
} else {
/* If there isn't, either it is unmerged, or
* resolved as "removed" by mistake. We do not
* want to do anything in the former case.
@@ -719,6 +731,10 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
verbose = 1;
continue;
}
if (!strcmp(path, "--clear-resolve-undo")) {
resolve_undo_clear();
continue;
}
if (!strcmp(path, "-h") || !strcmp(path, "--help"))
usage(update_index_usage);
die("unknown option %s", path);

16
cache.h
View File

@@ -288,6 +288,7 @@ static inline int ce_to_dtype(const struct cache_entry *ce)
struct index_state {
struct cache_entry **cache;
unsigned int cache_nr, cache_alloc, cache_changed;
struct string_list *resolve_undo;
struct cache_tree *cache_tree;
struct cache_time timestamp;
void *alloc;
@@ -342,6 +343,9 @@ static inline void remove_name_hash(struct cache_entry *ce)
#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))
#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
#define resolve_undo_clear() resolve_undo_clear_index(&the_index)
#define unmerge_cache_entry_at(at) unmerge_index_entry_at(&the_index, at)
#define unmerge_cache(pathspec) unmerge_index(&the_index, pathspec)
#endif
enum object_type {
@@ -451,7 +455,6 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
#define ADD_CACHE_JUST_APPEND 8 /* Append only; tree.c::read_tree() */
#define ADD_CACHE_NEW_ONLY 16 /* Do not replace existing ones */
extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
extern int remove_index_entry_at(struct index_state *, int pos);
extern void remove_marked_cache_entries(struct index_state *istate);
@@ -481,9 +484,6 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
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, int skiplen);
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
@@ -627,7 +627,6 @@ static inline void hashclr(unsigned char *hash)
{
memset(hash, 0, 20);
}
extern int is_empty_blob_sha1(const unsigned char *sha1);
#define EMPTY_TREE_SHA1_HEX \
"4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@@ -697,7 +696,6 @@ extern int has_sha1_pack(const unsigned char *sha1);
extern int has_sha1_file(const unsigned char *sha1);
extern int has_loose_object_nonlocal(const unsigned char *sha1);
extern int has_pack_file(const unsigned char *sha1);
extern int has_pack_index(const unsigned char *sha1);
extern const signed char hexval_table[256];
@@ -798,8 +796,6 @@ extern int has_symlink_leading_path(const char *name, int len);
extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
extern int has_symlink_or_noent_leading_path(const char *name, int len);
extern int has_dirs_only_path(const char *name, int len, int prefix_len);
extern void invalidate_lstat_cache(const char *name, int len);
extern void clear_lstat_cache(void);
extern void schedule_dir_for_removal(const char *name, int len);
extern void remove_scheduled_dirs(void);
@@ -939,7 +935,11 @@ extern const char *config_exclusive_filename;
#define MAX_GITNAME (1000)
extern char git_default_email[MAX_GITNAME];
extern char git_default_name[MAX_GITNAME];
#define IDENT_NAME_GIVEN 01
#define IDENT_MAIL_GIVEN 02
#define IDENT_ALL_GIVEN (IDENT_NAME_GIVEN|IDENT_MAIL_GIVEN)
extern int user_ident_explicitly_given;
extern int user_ident_sufficiently_given(void);
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;

View File

@@ -73,7 +73,6 @@ struct pretty_print_context
struct reflog_walk_info *reflog_info;
};
extern int non_ascii(int);
extern int has_non_ascii(const char *text);
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
extern char *reencode_commit_message(const struct commit *commit,

View File

@@ -3,9 +3,7 @@
#include <conio.h>
#include "../strbuf.h"
#include <shellapi.h>
static int err_win_to_posix(DWORD winerr)
int err_win_to_posix(DWORD winerr)
{
int error = ENOSYS;
switch(winerr) {
@@ -142,12 +140,20 @@ int mingw_open (const char *filename, int oflags, ...)
return fd;
}
static inline time_t filetime_to_time_t(const FILETIME *ft)
/*
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
*/
static inline long long filetime_to_hnsec(const FILETIME *ft)
{
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
winTime /= 10000000; /* Nano to seconds resolution */
return (time_t)winTime;
/* Windows to Unix Epoch conversion */
return winTime - 116444736000000000LL;
}
static inline time_t filetime_to_time_t(const FILETIME *ft)
{
return (time_t)(filetime_to_hnsec(ft) / 10000000);
}
/* We keep the do_lstat code in a separate function to avoid recursion.
@@ -283,64 +289,37 @@ int mkstemp(char *template)
int gettimeofday(struct timeval *tv, void *tz)
{
SYSTEMTIME st;
struct tm tm;
GetSystemTime(&st);
tm.tm_year = st.wYear-1900;
tm.tm_mon = st.wMonth-1;
tm.tm_mday = st.wDay;
tm.tm_hour = st.wHour;
tm.tm_min = st.wMinute;
tm.tm_sec = st.wSecond;
tv->tv_sec = tm_to_time_t(&tm);
if (tv->tv_sec < 0)
return -1;
tv->tv_usec = st.wMilliseconds*1000;
FILETIME ft;
long long hnsec;
GetSystemTimeAsFileTime(&ft);
hnsec = filetime_to_hnsec(&ft);
tv->tv_sec = hnsec / 10000000;
tv->tv_usec = (hnsec % 10000000) / 10;
return 0;
}
int pipe(int filedes[2])
{
int fd;
HANDLE h[2], parent;
HANDLE h[2];
if (_pipe(filedes, 8192, 0) < 0)
return -1;
parent = GetCurrentProcess();
if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[0]),
parent, &h[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
close(filedes[0]);
close(filedes[1]);
/* this creates non-inheritable handles */
if (!CreatePipe(&h[0], &h[1], NULL, 8192)) {
errno = err_win_to_posix(GetLastError());
return -1;
}
if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[1]),
parent, &h[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
close(filedes[0]);
close(filedes[1]);
CloseHandle(h[0]);
return -1;
}
fd = _open_osfhandle((int)h[0], O_NOINHERIT);
if (fd < 0) {
close(filedes[0]);
close(filedes[1]);
filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
if (filedes[0] < 0) {
CloseHandle(h[0]);
CloseHandle(h[1]);
return -1;
}
close(filedes[0]);
filedes[0] = fd;
fd = _open_osfhandle((int)h[1], O_NOINHERIT);
if (fd < 0) {
filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
if (filedes[0] < 0) {
close(filedes[0]);
close(filedes[1]);
CloseHandle(h[1]);
return -1;
}
close(filedes[1]);
filedes[1] = fd;
return 0;
}
@@ -638,8 +617,8 @@ static int env_compare(const void *a, const void *b)
return strcasecmp(*ea, *eb);
}
static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
int prepend_cmd)
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
int prepend_cmd, int fhin, int fhout, int fherr)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
@@ -675,9 +654,9 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = (HANDLE) _get_osfhandle(0);
si.hStdOutput = (HANDLE) _get_osfhandle(1);
si.hStdError = (HANDLE) _get_osfhandle(2);
si.hStdInput = (HANDLE) _get_osfhandle(fhin);
si.hStdOutput = (HANDLE) _get_osfhandle(fhout);
si.hStdError = (HANDLE) _get_osfhandle(fherr);
/* concatenate argv, quoting args as we go */
strbuf_init(&args, 0);
@@ -732,7 +711,14 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
return (pid_t)pi.hProcess;
}
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
int prepend_cmd)
{
return mingw_spawnve_fd(cmd, argv, env, prepend_cmd, 0, 1, 2);
}
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
int fhin, int fhout, int fherr)
{
pid_t pid;
char **path = get_path_split();
@@ -754,13 +740,15 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
pid = -1;
}
else {
pid = mingw_spawnve(iprog, argv, env, 1);
pid = mingw_spawnve_fd(iprog, argv, env, 1,
fhin, fhout, fherr);
free(iprog);
}
argv[0] = argv0;
}
else
pid = mingw_spawnve(prog, argv, env, 0);
pid = mingw_spawnve_fd(prog, argv, env, 0,
fhin, fhout, fherr);
free(prog);
}
free_path_split(path);
@@ -1338,8 +1326,22 @@ static const char *make_backslash_path(const char *path)
void mingw_open_html(const char *unixpath)
{
const char *htmlpath = make_backslash_path(unixpath);
typedef HINSTANCE (WINAPI *T)(HWND, const char *,
const char *, const char *, const char *, INT);
T ShellExecute;
HMODULE shell32;
shell32 = LoadLibrary("shell32.dll");
if (!shell32)
die("cannot load shell32.dll");
ShellExecute = (T)GetProcAddress(shell32, "ShellExecuteA");
if (!ShellExecute)
die("cannot run browser");
printf("Launching default browser to display HTML ...\n");
ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
FreeLibrary(shell32);
}
int link(const char *oldpath, const char *newpath)

View File

@@ -209,18 +209,21 @@ int mingw_getpagesize(void);
* mingw_fstat() instead of fstat() on Windows.
*/
#define off_t off64_t
#define stat _stati64
#define lseek _lseeki64
#ifndef ALREADY_DECLARED_STAT_FUNCS
#define stat _stati64
int mingw_lstat(const char *file_name, struct stat *buf);
int mingw_fstat(int fd, struct stat *buf);
#define fstat mingw_fstat
#define lstat mingw_lstat
#define _stati64(x,y) mingw_lstat(x,y)
#endif
int mingw_utime(const char *file_name, const struct utimbuf *times);
#define utime mingw_utime
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env);
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
int fhin, int fhout, int fherr);
void mingw_execvp(const char *cmd, char *const *argv);
#define execvp mingw_execvp
@@ -307,3 +310,8 @@ struct mingw_dirent
#define readdir(x) mingw_readdir(x)
struct dirent *mingw_readdir(DIR *dir);
#endif // !NO_MINGW_REPLACE_READDIR
/*
* Used by Pthread API implementation for Windows
*/
extern int err_win_to_posix(DWORD winerr);

View File

@@ -21,30 +21,22 @@ static __inline int strcasecmp (const char *s1, const char *s2)
}
#undef ERROR
#undef stat
#undef _stati64
#include "compat/mingw.h"
#undef stat
#define stat _stati64
#define _stat64(x,y) mingw_lstat(x,y)
/*
Even though _stati64 is normally just defined at _stat64
on Windows, we specify it here as a proper struct to avoid
compiler warnings about macro redefinition due to magic in
mingw.h. Struct taken from ReactOS (GNU GPL license).
*/
struct _stati64 {
_dev_t st_dev;
_ino_t st_ino;
unsigned short st_mode;
short st_nlink;
short st_uid;
short st_gid;
_dev_t st_rdev;
__int64 st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
};
/* Use mingw_lstat() instead of lstat()/stat() and mingw_fstat() instead
* of fstat(). We add the declaration of these functions here, suppressing
* the corresponding declarations in mingw.h, so that we can use the
* appropriate structure type (and function) names from the msvc headers.
*/
#define stat _stat64
int mingw_lstat(const char *file_name, struct stat *buf);
int mingw_fstat(int fd, struct stat *buf);
#define fstat mingw_fstat
#define lstat mingw_lstat
#define _stat64(x,y) mingw_lstat(x,y)
#define ALREADY_DECLARED_STAT_FUNCS
#include "compat/mingw.h"
#undef ALREADY_DECLARED_STAT_FUNCS
#endif

110
compat/win32/pthread.c Normal file
View File

@@ -0,0 +1,110 @@
/*
* Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
*
* DISCLAMER: The implementation is Git-specific, it is subset of original
* Pthreads API, without lots of other features that Git doesn't use.
* Git also makes sure that the passed arguments are valid, so there's
* no need for double-checking.
*/
#include "../../git-compat-util.h"
#include "pthread.h"
#include <errno.h>
#include <limits.h>
static unsigned __stdcall win32_start_routine(void *arg)
{
pthread_t *thread = arg;
thread->arg = thread->start_routine(thread->arg);
return 0;
}
int pthread_create(pthread_t *thread, const void *unused,
void *(*start_routine)(void*), void *arg)
{
thread->arg = arg;
thread->start_routine = start_routine;
thread->handle = (HANDLE)
_beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
if (!thread->handle)
return errno;
else
return 0;
}
int win32_pthread_join(pthread_t *thread, void **value_ptr)
{
DWORD result = WaitForSingleObject(thread->handle, INFINITE);
switch (result) {
case WAIT_OBJECT_0:
if (value_ptr)
*value_ptr = thread->arg;
return 0;
case WAIT_ABANDONED:
return EINVAL;
default:
return err_win_to_posix(GetLastError());
}
}
int pthread_cond_init(pthread_cond_t *cond, const void *unused)
{
cond->waiters = 0;
cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
if (!cond->sema)
die("CreateSemaphore() failed");
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
CloseHandle(cond->sema);
cond->sema = NULL;
return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
{
InterlockedIncrement(&cond->waiters);
/*
* Unlock external mutex and wait for signal.
* NOTE: we've held mutex locked long enough to increment
* waiters count above, so there's no problem with
* leaving mutex unlocked before we wait on semaphore.
*/
LeaveCriticalSection(mutex);
/* let's wait - ignore return value */
WaitForSingleObject(cond->sema, INFINITE);
/* we're done waiting, so make sure we decrease waiters count */
InterlockedDecrement(&cond->waiters);
/* lock external mutex again */
EnterCriticalSection(mutex);
return 0;
}
int pthread_cond_signal(pthread_cond_t *cond)
{
/*
* Access to waiters count is atomic; see "Interlocked Variable Access"
* http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
*/
int have_waiters = cond->waiters > 0;
/*
* Signal only when there are waiters
*/
if (have_waiters)
return ReleaseSemaphore(cond->sema, 1, NULL) ?
0 : err_win_to_posix(GetLastError());
else
return 0;
}

67
compat/win32/pthread.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* Header used to adapt pthread-based POSIX code to Windows API threads.
*
* Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
*/
#ifndef PTHREAD_H
#define PTHREAD_H
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
/*
* Defines that adapt Windows API threads to pthreads API
*/
#define pthread_mutex_t CRITICAL_SECTION
#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
#define pthread_mutex_lock EnterCriticalSection
#define pthread_mutex_unlock LeaveCriticalSection
/*
* Implement simple condition variable for Windows threads, based on ACE
* implementation.
*
* See original implementation: http://bit.ly/1vkDjo
* ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
* See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
*/
typedef struct {
volatile LONG waiters;
HANDLE sema;
} pthread_cond_t;
extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
extern int pthread_cond_destroy(pthread_cond_t *cond);
extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
extern int pthread_cond_signal(pthread_cond_t *cond);
/*
* Simple thread creation implementation using pthread API
*/
typedef struct {
HANDLE handle;
void *(*start_routine)(void*);
void *arg;
} pthread_t;
extern int pthread_create(pthread_t *thread, const void *unused,
void *(*start_routine)(void*), void *arg);
/*
* To avoid the need of copying a struct, we use small macro wrapper to pass
* pointer to win32_pthread_join instead.
*/
#define pthread_join(a, b) win32_pthread_join(&(a), (b))
extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
#endif /* PTHREAD_H */

View File

@@ -533,8 +533,7 @@ static int git_default_user_config(const char *var, const char *value)
if (!value)
return config_error_nonbool(var);
strlcpy(git_default_name, value, sizeof(git_default_name));
if (git_default_email[0])
user_ident_explicitly_given = 1;
user_ident_explicitly_given |= IDENT_NAME_GIVEN;
return 0;
}
@@ -542,8 +541,7 @@ static int git_default_user_config(const char *var, const char *value)
if (!value)
return config_error_nonbool(var);
strlcpy(git_default_email, value, sizeof(git_default_email));
if (git_default_name[0])
user_ident_explicitly_given = 1;
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
return 0;
}

View File

@@ -147,7 +147,6 @@ static char *path_ok(char *directory)
{ "IP", ip_address },
{ "P", tcp_port },
{ "D", directory },
{ "%", "%" },
{ NULL }
};

2
date.c
View File

@@ -9,7 +9,7 @@
/*
* This is like mktime, but without normalization of tm_wday and tm_yday.
*/
time_t tm_to_time_t(const struct tm *tm)
static time_t tm_to_time_t(const struct tm *tm)
{
static const int mdays[] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334

199
dir.c
View File

@@ -655,6 +655,92 @@ static int get_dtype(struct dirent *de, const char *path, int len)
return dtype;
}
enum path_treatment {
path_ignored,
path_handled,
path_recurse,
};
static enum path_treatment treat_one_path(struct dir_struct *dir,
char *path, int *len,
const struct path_simplify *simplify,
int dtype, struct dirent *de)
{
int exclude = excluded(dir, path, &dtype);
if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
&& in_pathspec(path, *len, simplify))
dir_add_ignored(dir, path, *len);
/*
* Excluded? If we don't explicitly want to show
* ignored files, ignore it
*/
if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
return path_ignored;
if (dtype == DT_UNKNOWN)
dtype = get_dtype(de, path, *len);
/*
* Do we want to see just the ignored files?
* We still need to recurse into directories,
* even if we don't ignore them, since the
* directory may contain files that we do..
*/
if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
if (dtype != DT_DIR)
return path_ignored;
}
switch (dtype) {
default:
return path_ignored;
case DT_DIR:
memcpy(path + *len, "/", 2);
(*len)++;
switch (treat_directory(dir, path, *len, simplify)) {
case show_directory:
if (exclude != !!(dir->flags
& DIR_SHOW_IGNORED))
return path_ignored;
break;
case recurse_into_directory:
return path_recurse;
case ignore_directory:
return path_ignored;
}
break;
case DT_REG:
case DT_LNK:
break;
}
return path_handled;
}
static enum path_treatment treat_path(struct dir_struct *dir,
struct dirent *de,
char *path, int path_max,
int baselen,
const struct path_simplify *simplify,
int *len)
{
int dtype;
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
return path_ignored;
*len = strlen(de->d_name);
/* Ignore overly long pathnames! */
if (*len + baselen + 8 > path_max)
return path_ignored;
memcpy(path + baselen, de->d_name, *len + 1);
*len += baselen;
if (simplify_away(path, *len, simplify))
return path_ignored;
dtype = DTYPE(de);
return treat_one_path(dir, path, len, simplify, dtype, de);
}
/*
* Read a directory tree. We currently ignore anything but
* directories, regular files and symlinks. That's because git
@@ -664,7 +750,10 @@ static int get_dtype(struct dirent *de, const char *path, int len)
* Also, we ignore the name ".git" (even if it is not a directory).
* That likely will not change.
*/
static int read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
static int read_directory_recursive(struct dir_struct *dir,
const char *base, int baselen,
int check_only,
const struct path_simplify *simplify)
{
DIR *fdir = opendir(*base ? base : ".");
int contents = 0;
@@ -675,70 +764,16 @@ static int read_directory_recursive(struct dir_struct *dir, const char *base, in
memcpy(path, base, baselen);
while ((de = readdir(fdir)) != NULL) {
int len, dtype;
int exclude;
if (is_dot_or_dotdot(de->d_name) ||
!strcmp(de->d_name, ".git"))
int len;
switch (treat_path(dir, de, path, sizeof(path),
baselen, simplify, &len)) {
case path_recurse:
contents += read_directory_recursive
(dir, path, len, 0, simplify);
continue;
len = strlen(de->d_name);
/* Ignore overly long pathnames! */
if (len + baselen + 8 > sizeof(path))
case path_ignored:
continue;
memcpy(path + baselen, de->d_name, len+1);
len = baselen + len;
if (simplify_away(path, len, simplify))
continue;
dtype = DTYPE(de);
exclude = excluded(dir, path, &dtype);
if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
&& in_pathspec(path, len, simplify))
dir_add_ignored(dir, path,len);
/*
* Excluded? If we don't explicitly want to show
* ignored files, ignore it
*/
if (exclude && !(dir->flags & DIR_SHOW_IGNORED))
continue;
if (dtype == DT_UNKNOWN)
dtype = get_dtype(de, path, len);
/*
* Do we want to see just the ignored files?
* We still need to recurse into directories,
* even if we don't ignore them, since the
* directory may contain files that we do..
*/
if (!exclude && (dir->flags & DIR_SHOW_IGNORED)) {
if (dtype != DT_DIR)
continue;
}
switch (dtype) {
default:
continue;
case DT_DIR:
memcpy(path + len, "/", 2);
len++;
switch (treat_directory(dir, path, len, simplify)) {
case show_directory:
if (exclude != !!(dir->flags
& DIR_SHOW_IGNORED))
continue;
break;
case recurse_into_directory:
contents += read_directory_recursive(dir,
path, len, 0, simplify);
continue;
case ignore_directory:
continue;
}
break;
case DT_REG:
case DT_LNK:
case path_handled:
break;
}
contents++;
@@ -808,6 +843,41 @@ static void free_simplify(struct path_simplify *simplify)
free(simplify);
}
static int treat_leading_path(struct dir_struct *dir,
const char *path, int len,
const struct path_simplify *simplify)
{
char pathbuf[PATH_MAX];
int baselen, blen;
const char *cp;
while (len && path[len - 1] == '/')
len--;
if (!len)
return 1;
baselen = 0;
while (1) {
cp = path + baselen + !!baselen;
cp = memchr(cp, '/', path + len - cp);
if (!cp)
baselen = len;
else
baselen = cp - path;
memcpy(pathbuf, path, baselen);
pathbuf[baselen] = '\0';
if (!is_directory(pathbuf))
return 0;
if (simplify_away(pathbuf, baselen, simplify))
return 0;
blen = baselen;
if (treat_one_path(dir, pathbuf, &blen, simplify,
DT_DIR, NULL) == path_ignored)
return 0; /* do not recurse into it */
if (len <= baselen)
return 1; /* finished checking */
}
}
int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
{
struct path_simplify *simplify;
@@ -816,7 +886,8 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const char
return dir->nr;
simplify = create_simplify(pathspec);
read_directory_recursive(dir, path, len, 0, simplify);
if (!len || treat_leading_path(dir, path, len, simplify))
read_directory_recursive(dir, path, len, 0, simplify);
free_simplify(simplify);
qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);

View File

@@ -179,7 +179,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
* This is like 'lstat()', except it refuses to follow symlinks
* in the path, after skipping "skiplen".
*/
int check_path(const char *path, int len, struct stat *st, int skiplen)
static int check_path(const char *path, int len, struct stat *st, int skiplen)
{
const char *slash = path + len;

View File

@@ -2356,6 +2356,7 @@ static void parse_new_tag(void)
struct tag *t;
uintmax_t from_mark = 0;
unsigned char sha1[20];
enum object_type type;
/* Obtain the new tag name from the rest of our command */
sp = strchr(command_buf.buf, ' ') + 1;
@@ -2376,19 +2377,18 @@ static void parse_new_tag(void)
s = lookup_branch(from);
if (s) {
hashcpy(sha1, s->sha1);
type = OBJ_COMMIT;
} else if (*from == ':') {
struct object_entry *oe;
from_mark = strtoumax(from + 1, NULL, 10);
oe = find_mark(from_mark);
if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", from_mark);
type = oe->type;
hashcpy(sha1, oe->sha1);
} else if (!get_sha1(from, sha1)) {
unsigned long size;
char *buf;
buf = read_object_with_reference(sha1,
commit_type, &size, sha1);
buf = read_sha1_file(sha1, &type, &size);
if (!buf || size < 46)
die("Not a valid commit: %s", from);
free(buf);
@@ -2413,7 +2413,7 @@ static void parse_new_tag(void)
"object %s\n"
"type %s\n"
"tag %s\n",
sha1_to_hex(sha1), commit_type, t->name);
sha1_to_hex(sha1), typename(type), t->name);
if (tagger)
strbuf_addf(&new_data,
"tagger %s\n", tagger);

View File

@@ -30,6 +30,7 @@ skip skip the current patch
abort restore the original branch and abort the patching operation.
committer-date-is-author-date lie about committer date
ignore-date use current timestamp for author date
rerere-autoupdate update the index with reused conflict resolution if possible
rebasing* (internal use for git-rebase)"
. git-sh-setup
@@ -135,7 +136,7 @@ It does not apply to blobs recorded in its index."
export GIT_MERGE_VERBOSITY=0
fi
git-merge-recursive $orig_tree -- HEAD $his_tree || {
git rerere
git rerere $allow_rerere_autoupdate
echo Failed to merge in the changes.
exit 1
}
@@ -293,6 +294,7 @@ resolvemsg= resume= scissors= no_inbody_headers=
git_apply_opt=
committer_date_is_author_date=
ignore_date=
allow_rerere_autoupdate=
while test $# != 0
do
@@ -340,6 +342,8 @@ do
committer_date_is_author_date=t ;;
--ignore-date)
ignore_date=t ;;
--rerere-autoupdate|--no-rerere-autoupdate)
allow_rerere_autoupdate="$1" ;;
-q|--quiet)
GIT_QUIET=t ;;
--)

View File

@@ -96,6 +96,7 @@
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <termios.h>
#ifndef NO_SYS_SELECT_H
#include <sys/select.h>
#endif
@@ -198,7 +199,6 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
extern int prefixcmp(const char *str, const char *prefix);
extern time_t tm_to_time_t(const struct tm *tm);
static inline const char *skip_prefix(const char *str, const char *prefix)
{

View File

@@ -3,9 +3,8 @@
# This script is typically launched by using the 'git difftool'
# convenience command.
#
# Copyright (c) 2009 David Aguilar
# Copyright (c) 2009, 2010 David Aguilar
# Load common functions from git-mergetool--lib
TOOL_MODE=diff
. git-mergetool--lib
@@ -20,7 +19,11 @@ should_prompt () {
fi
}
# Sets up shell variables and runs a merge tool
# Indicates that --extcmd=... was specified
use_ext_cmd () {
test -n "$GIT_DIFFTOOL_EXTCMD"
}
launch_merge_tool () {
# Merged is the filename as it appears in the work tree
# Local is the contents of a/filename
@@ -35,20 +38,28 @@ launch_merge_tool () {
# the user with the real $MERGED name before launching $merge_tool.
if should_prompt; then
printf "\nViewing: '$MERGED'\n"
printf "Hit return to launch '%s': " "$merge_tool"
if use_ext_cmd; then
printf "Hit return to launch '%s': " \
"$GIT_DIFFTOOL_EXTCMD"
else
printf "Hit return to launch '%s': " "$merge_tool"
fi
read ans
fi
# Run the appropriate merge tool command
run_merge_tool "$merge_tool"
if use_ext_cmd; then
eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
else
run_merge_tool "$merge_tool"
fi
}
# Allow GIT_DIFF_TOOL and GIT_MERGE_TOOL to provide default values
test -n "$GIT_MERGE_TOOL" && merge_tool="$GIT_MERGE_TOOL"
test -n "$GIT_DIFF_TOOL" && merge_tool="$GIT_DIFF_TOOL"
if test -z "$merge_tool"; then
merge_tool="$(get_merge_tool)" || exit
if ! use_ext_cmd; then
if test -n "$GIT_DIFF_TOOL"; then
merge_tool="$GIT_DIFF_TOOL"
else
merge_tool="$(get_merge_tool)" || exit
fi
fi
# Launch the merge tool on each path provided by 'git diff'

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env perl
# Copyright (c) 2009 David Aguilar
# Copyright (c) 2009, 2010 David Aguilar
#
# This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
# git-difftool--helper script.
@@ -15,13 +15,17 @@ use warnings;
use Cwd qw(abs_path);
use File::Basename qw(dirname);
require Git;
my $DIR = abs_path(dirname($0));
sub usage
{
print << 'USAGE';
usage: git difftool [--tool=<tool>] [-y|--no-prompt] ["git diff" options]
usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]
[-y|--no-prompt] [-g|--gui]
['git diff' options]
USAGE
exit 1;
}
@@ -63,6 +67,24 @@ sub generate_command
$ENV{GIT_DIFF_TOOL} = substr($arg, 7);
next;
}
if ($arg eq '-x' || $arg eq '--extcmd') {
usage() if $#ARGV <= $idx;
$ENV{GIT_DIFFTOOL_EXTCMD} = $ARGV[$idx + 1];
$skip_next = 1;
next;
}
if ($arg =~ /^--extcmd=/) {
$ENV{GIT_DIFFTOOL_EXTCMD} = substr($arg, 9);
next;
}
if ($arg eq '-g' || $arg eq '--gui') {
my $tool = Git::command_oneline('config',
'diff.guitool');
if (length($tool)) {
$ENV{GIT_DIFF_TOOL} = $tool;
}
next;
}
if ($arg eq '-y' || $arg eq '--no-prompt') {
$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
delete $ENV{GIT_DIFFTOOL_PROMPT};

View File

@@ -13,8 +13,29 @@ set_reflog_action "pull $*"
require_work_tree
cd_to_toplevel
test -z "$(git ls-files -u)" ||
die "You are in the middle of a conflicted merge."
die_conflict () {
git diff-index --cached --name-status -r --ignore-submodules HEAD --
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
die "Pull is not possible because you have unmerged files.
Please, fix them up in the work tree, and then use 'git add/rm <file>'
as appropriate to mark resolution, or use 'git commit -a'."
else
die "Pull is not possible because you have unmerged files."
fi
}
die_merge () {
if [ $(git config --bool --get advice.resolveConflict || echo true) = "true" ]; then
die "You have not concluded your merge (MERGE_HEAD exists).
Please, commit your changes before you can merge."
else
die "You have not concluded your merge (MERGE_HEAD exists)."
fi
}
test -z "$(git ls-files -u)" || die_conflict
test -f "$GIT_DIR/MERGE_HEAD" && die_merge
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
log_arg= verbosity=

View File

@@ -28,24 +28,81 @@ abort abort rebasing process and restore original branch
skip skip current patch and continue rebasing process
no-verify override pre-rebase hook from stopping the operation
root rebase all reachable commmits up to the root(s)
autosquash move commits that begin with squash!/fixup! under -i
"
. git-sh-setup
require_work_tree
DOTEST="$GIT_DIR/rebase-merge"
# The file containing rebase commands, comments, and empty lines.
# This file is created by "git rebase -i" then edited by the user. As
# the lines are processed, they are removed from the front of this
# file and written to the tail of $DONE.
TODO="$DOTEST"/git-rebase-todo
# The rebase command lines that have already been processed. A line
# is moved here when it is first handled, before any associated user
# actions.
DONE="$DOTEST"/done
# The commit message that is planned to be used for any changes that
# need to be committed following a user interaction.
MSG="$DOTEST"/message
# The file into which is accumulated the suggested commit message for
# squash/fixup commands. When the first of a series of squash/fixups
# is seen, the file is created and the commit message from the
# previous commit and from the first squash/fixup commit are written
# to it. The commit message for each subsequent squash/fixup commit
# is appended to the file as it is processed.
#
# The first line of the file is of the form
# # This is a combination of $COUNT commits.
# where $COUNT is the number of commits whose messages have been
# written to the file so far (including the initial "pick" commit).
# Each time that a commit message is processed, this line is read and
# updated. It is deleted just before the combined commit is made.
SQUASH_MSG="$DOTEST"/message-squash
# If the current series of squash/fixups has not yet included a squash
# command, then this file exists and holds the commit message of the
# original "pick" commit. (If the series ends without a "squash"
# command, then this can be used as the commit message of the combined
# commit without opening the editor.)
FIXUP_MSG="$DOTEST"/message-fixup
# $REWRITTEN is the name of a directory containing files for each
# commit that is reachable by at least one merge base of $HEAD and
# $UPSTREAM. They are not necessarily rewritten, but their children
# might be. This ensures that commits on merged, but otherwise
# unrelated side branches are left alone. (Think "X" in the man page's
# example.)
REWRITTEN="$DOTEST"/rewritten
DROPPED="$DOTEST"/dropped
# A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
# GIT_AUTHOR_DATE that will be used for the commit that is currently
# being rebased.
AUTHOR_SCRIPT="$DOTEST"/author-script
# When an "edit" rebase command is being processed, the SHA1 of the
# commit to be edited is recorded in this file. When "git rebase
# --continue" is executed, if there are any staged changes then they
# will be amended to the HEAD commit, but only provided the HEAD
# commit is still the commit to be edited. When any other rebase
# command is processed, this file is deleted.
AMEND="$DOTEST"/amend
PRESERVE_MERGES=
STRATEGY=
ONTO=
VERBOSE=
OK_TO_SKIP_PRE_REBASE=
REBASE_ROOT=
AUTOSQUASH=
GIT_CHERRY_PICK_HELP=" After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
@@ -70,6 +127,11 @@ output () {
esac
}
# Output the commit message for the specified commit.
commit_message () {
git cat-file commit "$1" | sed "1,/^$/d"
}
run_pre_rebase_hook () {
if test -z "$OK_TO_SKIP_PRE_REBASE" &&
test -x "$GIT_DIR/hooks/pre-rebase"
@@ -129,10 +191,10 @@ make_patch () {
echo "Root commit"
;;
esac > "$DOTEST"/patch
test -f "$DOTEST"/message ||
git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
test -f "$DOTEST"/author-script ||
get_author_ident_from_commit "$1" > "$DOTEST"/author-script
test -f "$MSG" ||
commit_message "$1" > "$MSG"
test -f "$AUTHOR_SCRIPT" ||
get_author_ident_from_commit "$1" > "$AUTHOR_SCRIPT"
}
die_with_patch () {
@@ -150,13 +212,22 @@ has_action () {
sane_grep '^[^#]' "$1" >/dev/null
}
# Run command with GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and
# GIT_AUTHOR_DATE exported from the current environment.
do_with_author () {
GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
"$@"
}
pick_one () {
no_ff=
case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
test -d "$REWRITTEN" &&
pick_one_preserving_merges "$@" && return
if test ! -z "$REBASE_ROOT"
if test -n "$REBASE_ROOT"
then
output git cherry-pick "$@"
return
@@ -164,11 +235,10 @@ pick_one () {
parent_sha1=$(git rev-parse --verify $sha1^) ||
die "Could not get the parent of $sha1"
current_sha1=$(git rev-parse --verify HEAD)
if test "$no_ff$current_sha1" = "$parent_sha1"; then
if test -z "$no_ff" -a "$current_sha1" = "$parent_sha1"
then
output git reset --hard $sha1
test "a$1" = a-n && output git reset --soft $current_sha1
sha1=$(git rev-parse --short $sha1)
output warn Fast-forward to $sha1
output warn Fast-forward to $(git rev-parse --short $sha1)
else
output git cherry-pick "$@"
fi
@@ -269,14 +339,11 @@ pick_one_preserving_merges () {
# redo merge
author_script=$(get_author_ident_from_commit $sha1)
eval "$author_script"
msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
msg="$(commit_message $sha1)"
# No point in merging the first parent, that's HEAD
new_parents=${new_parents# $first_parent}
if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
output git merge $STRATEGY -m "$msg" \
$new_parents
if ! do_with_author output \
git merge $STRATEGY -m "$msg" $new_parents
then
printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
die_with_patch $sha1 "Error redoing merge $sha1"
@@ -300,34 +367,66 @@ nth_string () {
esac
}
make_squash_message () {
update_squash_messages () {
if test -f "$SQUASH_MSG"; then
COUNT=$(($(sed -n "s/^# This is [^0-9]*\([1-9][0-9]*\).*/\1/p" \
< "$SQUASH_MSG" | sed -ne '$p')+1))
echo "# This is a combination of $COUNT commits."
sed -e 1d -e '2,/^./{
/^$/d
}' <"$SQUASH_MSG"
mv "$SQUASH_MSG" "$SQUASH_MSG".bak || exit
COUNT=$(($(sed -n \
-e "1s/^# This is a combination of \(.*\) commits\./\1/p" \
-e "q" < "$SQUASH_MSG".bak)+1))
{
echo "# This is a combination of $COUNT commits."
sed -e 1d -e '2,/^./{
/^$/d
}' <"$SQUASH_MSG".bak
} >$SQUASH_MSG
else
commit_message HEAD > "$FIXUP_MSG" || die "Cannot write $FIXUP_MSG"
COUNT=2
echo "# This is a combination of two commits."
echo "# The first commit's message is:"
echo
git cat-file commit HEAD | sed -e '1,/^$/d'
{
echo "# This is a combination of 2 commits."
echo "# The first commit's message is:"
echo
cat "$FIXUP_MSG"
} >$SQUASH_MSG
fi
echo
echo "# This is the $(nth_string $COUNT) commit message:"
echo
git cat-file commit $1 | sed -e '1,/^$/d'
case $1 in
squash)
rm -f "$FIXUP_MSG"
echo
echo "# This is the $(nth_string $COUNT) commit message:"
echo
commit_message $2
;;
fixup)
echo
echo "# The $(nth_string $COUNT) commit message will be skipped:"
echo
commit_message $2 | sed -e 's/^/# /'
;;
esac >>$SQUASH_MSG
}
peek_next_command () {
sed -n -e "/^#/d" -e "/^$/d" -e "s/ .*//p" -e "q" < "$TODO"
}
# A squash/fixup has failed. Prepare the long version of the squash
# commit message, then die_with_patch. This code path requires the
# user to edit the combined commit message for all commits that have
# been squashed/fixedup so far. So also erase the old squash
# messages, effectively causing the combined commit to be used as the
# new basis for any further squash/fixups. Args: sha1 rest
die_failed_squash() {
mv "$SQUASH_MSG" "$MSG" || exit
rm -f "$FIXUP_MSG"
cp "$MSG" "$GIT_DIR"/MERGE_MSG || exit
warn
warn "Could not apply $1... $2"
die_with_patch $1 ""
}
do_next () {
rm -f "$DOTEST"/message "$DOTEST"/author-script \
"$DOTEST"/amend || exit
rm -f "$MSG" "$AUTHOR_SCRIPT" "$AMEND" || exit
read command sha1 rest < "$TODO"
case "$command" in
'#'*|''|noop)
@@ -355,7 +454,7 @@ do_next () {
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
make_patch $sha1
git rev-parse --verify HEAD > "$DOTEST"/amend
git rev-parse --verify HEAD > "$AMEND"
warn "Stopped at $sha1... $rest"
warn "You can amend the commit now, with"
warn
@@ -367,52 +466,49 @@ do_next () {
warn
exit 0
;;
squash|s)
comment_for_reflog squash
test -f "$DONE" && has_action "$DONE" ||
die "Cannot 'squash' without a previous commit"
mark_action_done
make_squash_message $sha1 > "$MSG"
failed=f
author_script=$(get_author_ident_from_commit HEAD)
output git reset --soft HEAD^
pick_one -n $sha1 || failed=t
case "$(peek_next_command)" in
squash|s|fixup|f)
case "$command" in
squash|s)
USE_OUTPUT=output
MSG_OPT=-F
EDIT_OR_FILE="$MSG"
cp "$MSG" "$SQUASH_MSG"
squash_style=squash
;;
*)
USE_OUTPUT=
MSG_OPT=
EDIT_OR_FILE=-e
rm -f "$SQUASH_MSG" || exit
cp "$MSG" "$GIT_DIR"/SQUASH_MSG
rm -f "$GIT_DIR"/MERGE_MSG || exit
fixup|f)
squash_style=fixup
;;
esac
comment_for_reflog $squash_style
test -f "$DONE" && has_action "$DONE" ||
die "Cannot '$squash_style' without a previous commit"
mark_action_done
update_squash_messages $squash_style $sha1
author_script=$(get_author_ident_from_commit HEAD)
echo "$author_script" > "$AUTHOR_SCRIPT"
eval "$author_script"
output git reset --soft HEAD^
pick_one -n $sha1 || die_failed_squash $sha1 "$rest"
case "$(peek_next_command)" in
squash|s|fixup|f)
# This is an intermediate commit; its message will only be
# used in case of trouble. So use the long version:
do_with_author output git commit --no-verify -F "$SQUASH_MSG" ||
die_failed_squash $sha1 "$rest"
;;
*)
# This is the final command of this squash/fixup group
if test -f "$FIXUP_MSG"
then
do_with_author git commit --no-verify -F "$FIXUP_MSG" ||
die_failed_squash $sha1 "$rest"
else
cp "$SQUASH_MSG" "$GIT_DIR"/SQUASH_MSG || exit
rm -f "$GIT_DIR"/MERGE_MSG
do_with_author git commit --no-verify -e ||
die_failed_squash $sha1 "$rest"
fi
rm -f "$SQUASH_MSG" "$FIXUP_MSG"
;;
esac
echo "$author_script" > "$DOTEST"/author-script
if test $failed = f
then
# This is like --amend, but with a different message
eval "$author_script"
GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
$USE_OUTPUT git commit --no-verify \
$MSG_OPT "$EDIT_OR_FILE" || failed=t
fi
if test $failed = t
then
cp "$MSG" "$GIT_DIR"/MERGE_MSG
warn
warn "Could not apply $sha1... $rest"
die_with_patch $sha1 ""
fi
;;
*)
warn "Unknown command: $command $sha1 $rest"
@@ -495,6 +591,37 @@ get_saved_options () {
test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
}
# Rearrange the todo list that has both "pick sha1 msg" and
# "pick sha1 fixup!/squash! msg" appears in it so that the latter
# comes immediately after the former, and change "pick" to
# "fixup"/"squash".
rearrange_squash () {
sed -n -e 's/^pick \([0-9a-f]*\) \(squash\)! /\1 \2 /p' \
-e 's/^pick \([0-9a-f]*\) \(fixup\)! /\1 \2 /p' \
"$1" >"$1.sq"
test -s "$1.sq" || return
used=
while read pick sha1 message
do
case " $used" in
*" $sha1 "*) continue ;;
esac
echo "$pick $sha1 $message"
while read squash action msg
do
case "$message" in
"$msg"*)
echo "$action $squash $action! $msg"
used="$used$squash "
;;
esac
done <"$1.sq"
done >"$1.rearranged" <"$1"
cat "$1.rearranged" >"$1"
rm -f "$1.sq" "$1.rearranged"
}
LF='
'
parse_onto () {
@@ -541,21 +668,20 @@ do
then
: Nothing to commit -- skip this
else
. "$DOTEST"/author-script ||
. "$AUTHOR_SCRIPT" ||
die "Cannot find the author identity"
amend=
if test -f "$DOTEST"/amend
if test -f "$AMEND"
then
amend=$(git rev-parse --verify HEAD)
test "$amend" = $(cat "$DOTEST"/amend) ||
test "$amend" = $(cat "$AMEND") ||
die "\
You have uncommitted changes in your working tree. Please, commit them
first and then run 'git rebase --continue' again."
git reset --soft HEAD^ ||
die "Cannot rewind the HEAD"
fi
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
git commit --no-verify -F "$DOTEST"/message -e || {
do_with_author git commit --no-verify -F "$MSG" -e || {
test -n "$amend" && git reset --soft $amend
die "Could not commit staged changes."
}
@@ -619,6 +745,9 @@ first and then run 'git rebase --continue' again."
--root)
REBASE_ROOT=t
;;
--autosquash)
AUTOSQUASH=t
;;
--onto)
shift
ONTO=$(parse_onto "$1") ||
@@ -679,13 +808,6 @@ first and then run 'git rebase --continue' again."
test t = "$VERBOSE" && : > "$DOTEST"/verbose
if test t = "$PRESERVE_MERGES"
then
# $REWRITTEN contains files for each commit that is
# reachable by at least one merge base of $HEAD and
# $UPSTREAM. They are not necessarily rewritten, but
# their children might be.
# This ensures that commits on merged, but otherwise
# unrelated side branches are left alone. (Think "X"
# in the man page's example.)
if test -z "$REBASE_ROOT"
then
mkdir "$REWRITTEN" &&
@@ -778,6 +900,7 @@ first and then run 'git rebase --continue' again."
fi
test -s "$TODO" || echo noop >> "$TODO"
test -n "$AUTOSQUASH" && rearrange_squash "$TODO"
cat >> "$TODO" << EOF
# Rebase $SHORTREVISIONS onto $SHORTONTO
@@ -787,6 +910,7 @@ first and then run 'git rebase --continue' again."
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

View File

@@ -52,6 +52,7 @@ diffstat=$(git config --bool rebase.stat)
git_am_opt=
rebase_root=
force_rebase=
allow_rerere_autoupdate=
continue_merge () {
test -n "$prev_head" || die "prev_head must be defined"
@@ -120,7 +121,7 @@ call_merge () {
return
;;
1)
git rerere
git rerere $allow_rerere_autoupdate
die "$RESOLVEMSG"
;;
2)
@@ -351,6 +352,9 @@ do
-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
force_rebase=t
;;
--rerere-autoupdate|--no-rerere-autoupdate)
allow_rerere_autoupdate="$1"
;;
-*)
usage
;;

View File

@@ -120,20 +120,11 @@ is_bare_repository () {
}
cd_to_toplevel () {
cdup=$(git rev-parse --show-cdup)
if test ! -z "$cdup"
then
# The "-P" option says to follow "physical" directory
# structure instead of following symbolic links. When cdup is
# "../", this means following the ".." entry in the current
# directory instead textually removing a symlink path element
# from the PWD shell variable. The "-P" behavior is more
# consistent with the C-style chdir used by most of Git.
cd -P "$cdup" || {
echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
exit 1
}
fi
cdup=$(git rev-parse --show-toplevel) &&
cd "$cdup" || {
echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
exit 1
}
}
require_work_tree () {

2
git.c
View File

@@ -317,7 +317,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "fsck-objects", cmd_fsck, RUN_SETUP },
{ "gc", cmd_gc, RUN_SETUP },
{ "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
{ "grep", cmd_grep, USE_PAGER },
{ "help", cmd_help },
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },

84
grep.c
View File

@@ -29,13 +29,6 @@ void append_grep_pattern(struct grep_opt *opt, const char *pat,
p->next = NULL;
}
static int is_fixed(const char *s)
{
while (*s && !is_regex_special(*s))
s++;
return !*s;
}
static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
{
int err;
@@ -43,7 +36,7 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
p->word_regexp = opt->word_regexp;
p->ignore_case = opt->ignore_case;
if (opt->fixed || is_fixed(p->pattern))
if (opt->fixed)
p->fixed = 1;
if (opt->regflags & REG_ICASE)
p->fixed = 0;
@@ -615,6 +608,65 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
}
}
static int should_lookahead(struct grep_opt *opt)
{
struct grep_pat *p;
if (opt->extended)
return 0; /* punt for too complex stuff */
if (opt->invert)
return 0;
for (p = opt->pattern_list; p; p = p->next) {
if (p->token != GREP_PATTERN)
return 0; /* punt for "header only" and stuff */
}
return 1;
}
static int look_ahead(struct grep_opt *opt,
unsigned long *left_p,
unsigned *lno_p,
char **bol_p)
{
unsigned lno = *lno_p;
char *bol = *bol_p;
struct grep_pat *p;
char *sp, *last_bol;
regoff_t earliest = -1;
for (p = opt->pattern_list; p; p = p->next) {
int hit;
regmatch_t m;
if (p->fixed)
hit = !fixmatch(p->pattern, bol, p->ignore_case, &m);
else
hit = !regexec(&p->regexp, bol, 1, &m, 0);
if (!hit || m.rm_so < 0 || m.rm_eo < 0)
continue;
if (earliest < 0 || m.rm_so < earliest)
earliest = m.rm_so;
}
if (earliest < 0) {
*bol_p = bol + *left_p;
*left_p = 0;
return 1;
}
for (sp = bol + earliest; bol < sp && sp[-1] != '\n'; sp--)
; /* find the beginning of the line */
last_bol = sp;
for (sp = bol; sp < last_bol; sp++) {
if (*sp == '\n')
lno++;
}
*left_p -= last_bol - bol;
*bol_p = last_bol;
*lno_p = lno;
return 0;
}
static int grep_buffer_1(struct grep_opt *opt, const char *name,
char *buf, unsigned long size, int collect_hits)
{
@@ -624,6 +676,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
unsigned last_hit = 0;
int binary_match_only = 0;
unsigned count = 0;
int try_lookahead = 0;
enum grep_context ctx = GREP_CONTEXT_HEAD;
xdemitconf_t xecfg;
@@ -652,11 +705,26 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
opt->priv = &xecfg;
}
}
try_lookahead = should_lookahead(opt);
while (left) {
char *eol, ch;
int hit;
/*
* look_ahead() skips quicly to the line that possibly
* has the next hit; don't call it if we need to do
* something more than just skipping the current line
* in response to an unmatch for the current line. E.g.
* inside a post-context window, we will show the current
* line as a context around the previous hit when it
* doesn't hit.
*/
if (try_lookahead
&& !(last_hit
&& lno <= last_hit + opt->post_context)
&& look_ahead(opt, &left, &lno, &bol))
break;
eol = end_of_line(bol, &left);
ch = *eol;
*eol = 0;

1
grep.h
View File

@@ -85,7 +85,6 @@ struct grep_opt {
int max_depth;
int funcname;
char color_match[COLOR_MAXLEN];
const char *color_external;
int regflags;
unsigned pre_context;
unsigned post_context;

10
http.c
View File

@@ -651,7 +651,7 @@ static void closedown_active_slot(struct active_request_slot *slot)
slot->in_use = 0;
}
void release_active_slot(struct active_request_slot *slot)
static void release_active_slot(struct active_request_slot *slot)
{
closedown_active_slot(slot);
if (slot->curl && curl_session_count > min_curl_sessions) {
@@ -834,7 +834,13 @@ int http_get_strbuf(const char *url, struct strbuf *result, int options)
return http_request(url, result, HTTP_REQUEST_STRBUF, options);
}
int http_get_file(const char *url, const char *filename, int options)
/*
* Downloads an url and stores the result in the given file.
*
* If a previous interrupted download is detected (i.e. a previous temporary
* file is still around) the download is resumed.
*/
static int http_get_file(const char *url, const char *filename, int options)
{
int ret;
struct strbuf tmpfile = STRBUF_INIT;

9
http.h
View File

@@ -81,7 +81,6 @@ extern int start_active_slot(struct active_request_slot *slot);
extern void run_active_slot(struct active_request_slot *slot);
extern void finish_active_slot(struct active_request_slot *slot);
extern void finish_all_active_slots(void);
extern void release_active_slot(struct active_request_slot *slot);
#ifdef USE_CURL_MULTI
extern void fill_active_slots(void);
@@ -135,14 +134,6 @@ extern char *get_remote_object_url(const char *url, const char *hex,
*/
int http_get_strbuf(const char *url, struct strbuf *result, int options);
/*
* Downloads an url and stores the result in the given file.
*
* If a previous interrupted download is detected (i.e. a previous temporary
* file is still around) the download is resumed.
*/
int http_get_file(const char *url, const char *filename, int options);
/*
* Prints an error message using error() containing url and curl_errorstr,
* and returns ret.

25
ident.c
View File

@@ -85,10 +85,11 @@ static void setup_ident(void)
if (!git_default_email[0]) {
const char *email = getenv("EMAIL");
if (email && email[0])
if (email && email[0]) {
strlcpy(git_default_email, email,
sizeof(git_default_email));
else {
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
} else {
if (!pw)
pw = getpwuid(getuid());
if (!pw)
@@ -168,8 +169,6 @@ static int copy(char *buf, size_t size, int offset, const char *src)
return offset;
}
static const char au_env[] = "GIT_AUTHOR_NAME";
static const char co_env[] = "GIT_COMMITTER_NAME";
static const char *env_hint =
"\n"
"*** Please tell me who you are.\n"
@@ -204,7 +203,7 @@ const char *fmt_ident(const char *name, const char *email,
if ((warn_on_no_name || error_on_no_name) &&
name == git_default_name && env_hint) {
fprintf(stderr, env_hint, au_env, co_env);
fputs(env_hint, stderr);
env_hint = NULL; /* warn only once */
}
if (error_on_no_name)
@@ -251,11 +250,21 @@ const char *git_author_info(int flag)
const char *git_committer_info(int flag)
{
if (getenv("GIT_COMMITTER_NAME") &&
getenv("GIT_COMMITTER_EMAIL"))
user_ident_explicitly_given = 1;
if (getenv("GIT_COMMITTER_NAME"))
user_ident_explicitly_given |= IDENT_NAME_GIVEN;
if (getenv("GIT_COMMITTER_EMAIL"))
user_ident_explicitly_given |= IDENT_MAIL_GIVEN;
return fmt_ident(getenv("GIT_COMMITTER_NAME"),
getenv("GIT_COMMITTER_EMAIL"),
getenv("GIT_COMMITTER_DATE"),
flag);
}
int user_ident_sufficiently_given(void)
{
#ifndef WINDOWS
return (user_ident_explicitly_given & IDENT_MAIL_GIVEN);
#else
return (user_ident_explicitly_given == IDENT_ALL_GIVEN);
#endif
}

View File

@@ -243,8 +243,3 @@ int map_user(struct string_list *map,
debug_mm("map_user: --\n");
return 0;
}
int map_email(struct string_list *map, const char *email, char *name, int maxlen)
{
return map_user(map, (char *)email, 0, name, maxlen);
}

View File

@@ -4,7 +4,6 @@
int read_mailmap(struct string_list *map, char **repo_abbrev);
void clear_mailmap(struct string_list *map);
int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen);
int map_user(struct string_list *mailmap,
char *email, int maxlen_email, char *name, int maxlen_name);

View File

@@ -217,27 +217,6 @@ struct object_list *object_list_insert(struct object *item,
return new_list;
}
void object_list_append(struct object *item,
struct object_list **list_p)
{
while (*list_p) {
list_p = &((*list_p)->next);
}
*list_p = xmalloc(sizeof(struct object_list));
(*list_p)->next = NULL;
(*list_p)->item = item;
}
unsigned object_list_length(struct object_list *list)
{
unsigned ret = 0;
while (list) {
list = list->next;
ret++;
}
return ret;
}
int object_list_contains(struct object_list *list, struct object *obj)
{
while (list) {

View File

@@ -72,11 +72,6 @@ struct object *lookup_unknown_object(const unsigned char *sha1);
struct object_list *object_list_insert(struct object *item,
struct object_list **list_p);
void object_list_append(struct object *item,
struct object_list **list_p);
unsigned object_list_length(struct object_list *list);
int object_list_contains(struct object_list *list, struct object *obj);
/* Object array handling .. */

View File

@@ -3,6 +3,9 @@
#include "cache.h"
#include "commit.h"
static int parse_options_usage(const char * const *usagestr,
const struct option *opts);
#define OPT_SHORT 1
#define OPT_UNSET 2
@@ -560,8 +563,8 @@ void usage_msg_opt(const char *msg,
usage_with_options(usagestr, options);
}
int parse_options_usage(const char * const *usagestr,
const struct option *opts)
static int parse_options_usage(const char * const *usagestr,
const struct option *opts)
{
return usage_with_options_internal(usagestr, opts, 0);
}
@@ -633,3 +636,10 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
commit_list_insert(commit, opt->value);
return 0;
}
int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
{
int *target = opt->value;
*target = unset ? 2 : 1;
return 0;
}

View File

@@ -123,6 +123,8 @@ struct option {
(h), PARSE_OPT_NOARG, NULL, (p) }
#define OPT_INTEGER(s, l, v, h) { OPTION_INTEGER, (s), (l), (v), "n", (h) }
#define OPT_STRING(s, l, v, a, h) { OPTION_STRING, (s), (l), (v), (a), (h) }
#define OPT_UYN(s, l, v, h) { OPTION_CALLBACK, (s), (l), (v), NULL, \
(h), PARSE_OPT_NOARG, &parse_opt_tertiary }
#define OPT_DATE(s, l, v, h) \
{ OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
parse_opt_approxidate_cb }
@@ -171,9 +173,6 @@ struct parse_opt_ctx_t {
const char *prefix;
};
extern int parse_options_usage(const char * const *usagestr,
const struct option *opts);
extern void parse_options_start(struct parse_opt_ctx_t *ctx,
int argc, const char **argv, const char *prefix,
int flags);
@@ -190,6 +189,7 @@ extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
extern int parse_opt_with_commit(const struct option *, const char *, int);
extern int parse_opt_tertiary(const struct option *, const char *, int);
#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")

View File

@@ -83,7 +83,7 @@ static int get_one_line(const char *msg)
}
/* High bit set, or ISO-2022-INT */
int non_ascii(int ch)
static int non_ascii(int ch)
{
return !isascii(ch) || ch == '\033';
}

View File

@@ -72,7 +72,7 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
}
}
char *sq_dequote_step(char *arg, char **next)
static char *sq_dequote_step(char *arg, char **next)
{
char *dst = arg;
char *src = arg;

View File

@@ -45,7 +45,6 @@ extern char *sq_dequote(char *);
* next argument that should be passed as first parameter. When there
* is no more argument to be dequoted, "next" is updated to point to NULL.
*/
extern char *sq_dequote_step(char *arg, char **next);
extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);

View File

@@ -14,6 +14,9 @@
#include "diffcore.h"
#include "revision.h"
#include "blob.h"
#include "resolve-undo.h"
static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
/* Index extensions.
*
@@ -26,6 +29,7 @@
#define CACHE_EXT(s) ( (s[0]<<24)|(s[1]<<16)|(s[2]<<8)|(s[3]) )
#define CACHE_EXT_TREE 0x54524545 /* "TREE" */
#define CACHE_EXT_RESOLVE_UNDO 0x52455543 /* "REUN" */
struct index_state the_index;
@@ -156,7 +160,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
return 0;
}
int is_empty_blob_sha1(const unsigned char *sha1)
static int is_empty_blob_sha1(const unsigned char *sha1)
{
static const unsigned char empty_blob_sha1[20] = {
0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
@@ -454,6 +458,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
{
struct cache_entry *ce = istate->cache[pos];
record_resolve_undo(istate, ce);
remove_name_hash(ce);
istate->cache_changed = 1;
istate->cache_nr--;
@@ -1152,7 +1157,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
return has_errors;
}
struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
{
return refresh_cache_ent(&the_index, ce, really, NULL);
}
@@ -1181,6 +1186,9 @@ static int read_index_extension(struct index_state *istate,
case CACHE_EXT_TREE:
istate->cache_tree = cache_tree_read(data, sz);
break;
case CACHE_EXT_RESOLVE_UNDO:
istate->resolve_undo = resolve_undo_read(data, sz);
break;
default:
if (*ext < 'A' || 'Z' < *ext)
return error("index uses %.4s extension, which we do not understand",
@@ -1360,6 +1368,7 @@ int is_index_unborn(struct index_state *istate)
int discard_index(struct index_state *istate)
{
resolve_undo_clear_index(istate);
istate->cache_nr = 0;
istate->cache_changed = 0;
istate->timestamp.sec = 0;
@@ -1585,6 +1594,17 @@ int write_index(struct index_state *istate, int newfd)
if (err)
return -1;
}
if (istate->resolve_undo) {
struct strbuf sb = STRBUF_INIT;
resolve_undo_write(&sb, istate->resolve_undo);
err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,
sb.len) < 0
|| ce_write(&c, newfd, sb.buf, sb.len) < 0;
strbuf_release(&sb);
if (err)
return -1;
}
if (ce_flush(&c, newfd) || fstat(newfd, &st))
return -1;

View File

@@ -317,7 +317,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
}
#ifndef NO_CURL_IOCTL
curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
{
struct rpc_state *rpc = clientp;

View File

@@ -1247,6 +1247,56 @@ int match_refs(struct ref *src, struct ref **dst,
return 0;
}
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update)
{
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next) {
if (ref->peer_ref)
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
else if (!send_mirror)
continue;
ref->deletion = is_null_sha1(ref->new_sha1);
if (!ref->deletion &&
!hashcmp(ref->old_sha1, ref->new_sha1)) {
ref->status = REF_STATUS_UPTODATE;
continue;
}
/* This part determines what can overwrite what.
* The rules are:
*
* (0) you can always use --force or +A:B notation to
* selectively force individual ref pairs.
*
* (1) if the old thing does not exist, it is OK.
*
* (2) if you do not have the old thing, you are not allowed
* to overwrite it; you would not know what you are losing
* otherwise.
*
* (3) if both new and old are commit-ish, and new is a
* descendant of old, it is OK.
*
* (4) regardless of all of the above, removing :B is
* always allowed.
*/
ref->nonfastforward =
!ref->deletion &&
!is_null_sha1(ref->old_sha1) &&
(!has_sha1_file(ref->old_sha1)
|| !ref_newer(ref->new_sha1, ref->old_sha1));
if (ref->nonfastforward && !ref->force && !force_update) {
ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
continue;
}
}
}
struct branch *branch_get(const char *name)
{
struct branch *ret;

View File

@@ -98,6 +98,8 @@ char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
int match_refs(struct ref *src, struct ref **dst,
int nr_refspec, const char **refspec, int all);
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update);
/*
* Given a list of the remote refs and the specification of things to

267
rerere.c
View File

@@ -3,6 +3,9 @@
#include "rerere.h"
#include "xdiff/xdiff.h"
#include "xdiff-interface.h"
#include "dir.h"
#include "resolve-undo.h"
#include "ll-merge.h"
/* if rerere_enabled == -1, fall back to detection of .git/rr-cache */
static int rerere_enabled = -1;
@@ -83,61 +86,74 @@ static inline void ferr_puts(const char *s, FILE *fp, int *err)
ferr_write(s, strlen(s), fp, err);
}
static int handle_file(const char *path,
unsigned char *sha1, const char *output)
struct rerere_io {
int (*getline)(struct strbuf *, struct rerere_io *);
FILE *output;
int wrerror;
/* some more stuff */
};
static void rerere_io_putstr(const char *str, struct rerere_io *io)
{
if (io->output)
ferr_puts(str, io->output, &io->wrerror);
}
static void rerere_io_putmem(const char *mem, size_t sz, struct rerere_io *io)
{
if (io->output)
ferr_write(mem, sz, io->output, &io->wrerror);
}
struct rerere_io_file {
struct rerere_io io;
FILE *input;
};
static int rerere_file_getline(struct strbuf *sb, struct rerere_io *io_)
{
struct rerere_io_file *io = (struct rerere_io_file *)io_;
return strbuf_getwholeline(sb, io->input, '\n');
}
static int handle_path(unsigned char *sha1, struct rerere_io *io)
{
git_SHA_CTX ctx;
char buf[1024];
int hunk_no = 0;
enum {
RR_CONTEXT = 0, RR_SIDE_1, RR_SIDE_2, RR_ORIGINAL,
} hunk = RR_CONTEXT;
struct strbuf one = STRBUF_INIT, two = STRBUF_INIT;
FILE *f = fopen(path, "r");
FILE *out = NULL;
int wrerror = 0;
if (!f)
return error("Could not open %s", path);
if (output) {
out = fopen(output, "w");
if (!out) {
fclose(f);
return error("Could not write %s", output);
}
}
struct strbuf buf = STRBUF_INIT;
if (sha1)
git_SHA1_Init(&ctx);
while (fgets(buf, sizeof(buf), f)) {
if (!prefixcmp(buf, "<<<<<<< ")) {
while (!io->getline(&buf, io)) {
if (!prefixcmp(buf.buf, "<<<<<<< ")) {
if (hunk != RR_CONTEXT)
goto bad;
hunk = RR_SIDE_1;
} else if (!prefixcmp(buf, "|||||||") && isspace(buf[7])) {
} else if (!prefixcmp(buf.buf, "|||||||") && isspace(buf.buf[7])) {
if (hunk != RR_SIDE_1)
goto bad;
hunk = RR_ORIGINAL;
} else if (!prefixcmp(buf, "=======") && isspace(buf[7])) {
} else if (!prefixcmp(buf.buf, "=======") && isspace(buf.buf[7])) {
if (hunk != RR_SIDE_1 && hunk != RR_ORIGINAL)
goto bad;
hunk = RR_SIDE_2;
} else if (!prefixcmp(buf, ">>>>>>> ")) {
} else if (!prefixcmp(buf.buf, ">>>>>>> ")) {
if (hunk != RR_SIDE_2)
goto bad;
if (strbuf_cmp(&one, &two) > 0)
strbuf_swap(&one, &two);
hunk_no++;
hunk = RR_CONTEXT;
if (out) {
ferr_puts("<<<<<<<\n", out, &wrerror);
ferr_write(one.buf, one.len, out, &wrerror);
ferr_puts("=======\n", out, &wrerror);
ferr_write(two.buf, two.len, out, &wrerror);
ferr_puts(">>>>>>>\n", out, &wrerror);
}
rerere_io_putstr("<<<<<<<\n", io);
rerere_io_putmem(one.buf, one.len, io);
rerere_io_putstr("=======\n", io);
rerere_io_putmem(two.buf, two.len, io);
rerere_io_putstr(">>>>>>>\n", io);
if (sha1) {
git_SHA1_Update(&ctx, one.buf ? one.buf : "",
one.len + 1);
@@ -147,13 +163,13 @@ static int handle_file(const char *path,
strbuf_reset(&one);
strbuf_reset(&two);
} else if (hunk == RR_SIDE_1)
strbuf_addstr(&one, buf);
strbuf_addstr(&one, buf.buf);
else if (hunk == RR_ORIGINAL)
; /* discard */
else if (hunk == RR_SIDE_2)
strbuf_addstr(&two, buf);
else if (out)
ferr_puts(buf, out, &wrerror);
strbuf_addstr(&two, buf.buf);
else
rerere_io_putstr(buf.buf, io);
continue;
bad:
hunk = 99; /* force error exit */
@@ -161,26 +177,136 @@ static int handle_file(const char *path,
}
strbuf_release(&one);
strbuf_release(&two);
strbuf_release(&buf);
fclose(f);
if (wrerror)
error("There were errors while writing %s (%s)",
path, strerror(wrerror));
if (out && fclose(out))
wrerror = error("Failed to flush %s: %s",
path, strerror(errno));
if (sha1)
git_SHA1_Final(sha1, &ctx);
if (hunk != RR_CONTEXT) {
if (hunk != RR_CONTEXT)
return -1;
return hunk_no;
}
static int handle_file(const char *path, unsigned char *sha1, const char *output)
{
int hunk_no = 0;
struct rerere_io_file io;
memset(&io, 0, sizeof(io));
io.io.getline = rerere_file_getline;
io.input = fopen(path, "r");
io.io.wrerror = 0;
if (!io.input)
return error("Could not open %s", path);
if (output) {
io.io.output = fopen(output, "w");
if (!io.io.output) {
fclose(io.input);
return error("Could not write %s", output);
}
}
hunk_no = handle_path(sha1, (struct rerere_io *)&io);
fclose(io.input);
if (io.io.wrerror)
error("There were errors while writing %s (%s)",
path, strerror(io.io.wrerror));
if (io.io.output && fclose(io.io.output))
io.io.wrerror = error("Failed to flush %s: %s",
path, strerror(errno));
if (hunk_no < 0) {
if (output)
unlink_or_warn(output);
return error("Could not parse conflict hunks in %s", path);
}
if (wrerror)
if (io.io.wrerror)
return -1;
return hunk_no;
}
struct rerere_io_mem {
struct rerere_io io;
struct strbuf input;
};
static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
{
struct rerere_io_mem *io = (struct rerere_io_mem *)io_;
char *ep;
size_t len;
strbuf_release(sb);
if (!io->input.len)
return -1;
ep = strchrnul(io->input.buf, '\n');
if (*ep == '\n')
ep++;
len = ep - io->input.buf;
strbuf_add(sb, io->input.buf, len);
strbuf_remove(&io->input, 0, len);
return 0;
}
static int handle_cache(const char *path, unsigned char *sha1, const char *output)
{
mmfile_t mmfile[3];
mmbuffer_t result = {NULL, 0};
struct cache_entry *ce;
int pos, len, i, hunk_no;
struct rerere_io_mem io;
/*
* Reproduce the conflicted merge in-core
*/
len = strlen(path);
pos = cache_name_pos(path, len);
if (0 <= pos)
return -1;
pos = -pos - 1;
for (i = 0; i < 3; i++) {
enum object_type type;
unsigned long size;
mmfile[i].size = 0;
mmfile[i].ptr = NULL;
if (active_nr <= pos)
break;
ce = active_cache[pos++];
if (ce_namelen(ce) != len || memcmp(ce->name, path, len)
|| ce_stage(ce) != i + 1)
break;
mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size);
mmfile[i].size = size;
}
for (i = 0; i < 3; i++) {
if (!mmfile[i].ptr && !mmfile[i].size)
mmfile[i].ptr = xstrdup("");
}
ll_merge(&result, path, &mmfile[0],
&mmfile[1], "ours",
&mmfile[2], "theirs", 0);
for (i = 0; i < 3; i++)
free(mmfile[i].ptr);
memset(&io, 0, sizeof(&io));
io.io.getline = rerere_mem_getline;
if (output)
io.io.output = fopen(output, "w");
else
io.io.output = NULL;
strbuf_init(&io.input, 0);
strbuf_attach(&io.input, result.ptr, result.size, result.size);
hunk_no = handle_path(sha1, (struct rerere_io *)&io);
strbuf_release(&io.input);
if (io.io.output)
fclose(io.io.output);
return hunk_no;
}
static int find_conflict(struct string_list *conflict)
{
int i;
@@ -367,7 +493,7 @@ static int is_rerere_enabled(void)
return 1;
}
int setup_rerere(struct string_list *merge_rr)
int setup_rerere(struct string_list *merge_rr, int flags)
{
int fd;
@@ -375,6 +501,8 @@ int setup_rerere(struct string_list *merge_rr)
if (!is_rerere_enabled())
return -1;
if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
merge_rr_path = git_pathdup("MERGE_RR");
fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
LOCK_DIE_ON_ERROR);
@@ -382,13 +510,62 @@ int setup_rerere(struct string_list *merge_rr)
return fd;
}
int rerere(void)
int rerere(int flags)
{
struct string_list merge_rr = { NULL, 0, 0, 1 };
int fd;
fd = setup_rerere(&merge_rr);
fd = setup_rerere(&merge_rr, flags);
if (fd < 0)
return 0;
return do_plain_rerere(&merge_rr, fd);
}
static int rerere_forget_one_path(const char *path, struct string_list *rr)
{
const char *filename;
char *hex;
unsigned char sha1[20];
int ret;
ret = handle_cache(path, sha1, NULL);
if (ret < 1)
return error("Could not parse conflict hunks in '%s'", path);
hex = xstrdup(sha1_to_hex(sha1));
filename = rerere_path(hex, "postimage");
if (unlink(filename))
return (errno == ENOENT
? error("no remembered resolution for %s", path)
: error("cannot unlink %s: %s", filename, strerror(errno)));
handle_cache(path, sha1, rerere_path(hex, "preimage"));
fprintf(stderr, "Updated preimage for '%s'\n", path);
string_list_insert(path, rr)->util = hex;
fprintf(stderr, "Forgot resolution for %s\n", path);
return 0;
}
int rerere_forget(const char **pathspec)
{
int i, fd;
struct string_list conflict = { NULL, 0, 0, 1 };
struct string_list merge_rr = { NULL, 0, 0, 1 };
if (read_cache() < 0)
return error("Could not read index");
fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
unmerge_cache(pathspec);
find_conflict(&conflict);
for (i = 0; i < conflict.nr; i++) {
struct string_list_item *it = &conflict.items[i];
if (!match_pathspec(pathspec, it->string, strlen(it->string),
0, NULL))
continue;
rerere_forget_one_path(it->string, &merge_rr);
}
return write_rr(&merge_rr, fd);
}

View File

@@ -3,9 +3,16 @@
#include "string-list.h"
extern int setup_rerere(struct string_list *);
extern int rerere(void);
#define RERERE_AUTOUPDATE 01
#define RERERE_NOAUTOUPDATE 02
extern int setup_rerere(struct string_list *, int);
extern int rerere(int);
extern const char *rerere_path(const char *hex, const char *file);
extern int has_rerere_resolution(const char *hex);
extern int rerere_forget(const char **);
#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
"update the index with reused conflict resolution if possible")
#endif

176
resolve-undo.c Normal file
View File

@@ -0,0 +1,176 @@
#include "cache.h"
#include "dir.h"
#include "resolve-undo.h"
#include "string-list.h"
/* The only error case is to run out of memory in string-list */
void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
{
struct string_list_item *lost;
struct resolve_undo_info *ui;
struct string_list *resolve_undo;
int stage = ce_stage(ce);
if (!stage)
return;
if (!istate->resolve_undo) {
resolve_undo = xcalloc(1, sizeof(*resolve_undo));
resolve_undo->strdup_strings = 1;
istate->resolve_undo = resolve_undo;
}
resolve_undo = istate->resolve_undo;
lost = string_list_insert(ce->name, resolve_undo);
if (!lost->util)
lost->util = xcalloc(1, sizeof(*ui));
ui = lost->util;
hashcpy(ui->sha1[stage - 1], ce->sha1);
ui->mode[stage - 1] = ce->ce_mode;
}
static int write_one(struct string_list_item *item, void *cbdata)
{
struct strbuf *sb = cbdata;
struct resolve_undo_info *ui = item->util;
int i;
if (!ui)
return 0;
strbuf_addstr(sb, item->string);
strbuf_addch(sb, 0);
for (i = 0; i < 3; i++)
strbuf_addf(sb, "%o%c", ui->mode[i], 0);
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
continue;
strbuf_add(sb, ui->sha1[i], 20);
}
return 0;
}
void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
{
for_each_string_list(write_one, resolve_undo, sb);
}
struct string_list *resolve_undo_read(void *data, unsigned long size)
{
struct string_list *resolve_undo;
size_t len;
char *endptr;
int i;
resolve_undo = xcalloc(1, sizeof(*resolve_undo));
resolve_undo->strdup_strings = 1;
while (size) {
struct string_list_item *lost;
struct resolve_undo_info *ui;
len = strlen(data) + 1;
if (size <= len)
goto error;
lost = string_list_insert(data, resolve_undo);
if (!lost->util)
lost->util = xcalloc(1, sizeof(*ui));
ui = lost->util;
size -= len;
data += len;
for (i = 0; i < 3; i++) {
ui->mode[i] = strtoul(data, &endptr, 8);
if (!endptr || endptr == data || *endptr)
goto error;
len = (endptr + 1) - (char*)data;
if (size <= len)
goto error;
size -= len;
data += len;
}
for (i = 0; i < 3; i++) {
if (!ui->mode[i])
continue;
if (size < 20)
goto error;
hashcpy(ui->sha1[i], data);
size -= 20;
data += 20;
}
}
return resolve_undo;
error:
string_list_clear(resolve_undo, 1);
error("Index records invalid resolve-undo information");
return NULL;
}
void resolve_undo_clear_index(struct index_state *istate)
{
struct string_list *resolve_undo = istate->resolve_undo;
if (!resolve_undo)
return;
string_list_clear(resolve_undo, 1);
free(resolve_undo);
istate->resolve_undo = NULL;
istate->cache_changed = 1;
}
int unmerge_index_entry_at(struct index_state *istate, int pos)
{
struct cache_entry *ce;
struct string_list_item *item;
struct resolve_undo_info *ru;
int i, err = 0;
if (!istate->resolve_undo)
return pos;
ce = istate->cache[pos];
if (ce_stage(ce)) {
/* already unmerged */
while ((pos < istate->cache_nr) &&
! strcmp(istate->cache[pos]->name, ce->name))
pos++;
return pos - 1; /* return the last entry processed */
}
item = string_list_lookup(ce->name, istate->resolve_undo);
if (!item)
return pos;
ru = item->util;
if (!ru)
return pos;
remove_index_entry_at(istate, pos);
for (i = 0; i < 3; i++) {
struct cache_entry *nce;
if (!ru->mode[i])
continue;
nce = make_cache_entry(ru->mode[i], ru->sha1[i],
ce->name, i + 1, 0);
if (add_index_entry(istate, nce, ADD_CACHE_OK_TO_ADD)) {
err = 1;
error("cannot unmerge '%s'", ce->name);
}
}
if (err)
return pos;
free(ru);
item->util = NULL;
return unmerge_index_entry_at(istate, pos);
}
void unmerge_index(struct index_state *istate, const char **pathspec)
{
int i;
if (!istate->resolve_undo)
return;
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce = istate->cache[i];
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
continue;
i = unmerge_index_entry_at(istate, i);
}
}

16
resolve-undo.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef RESOLVE_UNDO_H
#define RESOLVE_UNDO_H
struct resolve_undo_info {
unsigned int mode[3];
unsigned char sha1[3][20];
};
extern void record_resolve_undo(struct index_state *, struct cache_entry *);
extern void resolve_undo_write(struct strbuf *, struct string_list *);
extern struct string_list *resolve_undo_read(void *, unsigned long);
extern void resolve_undo_clear_index(struct index_state *);
extern int unmerge_index_entry_at(struct index_state *, int);
extern void unmerge_index(struct index_state *, const char **);
#endif

View File

@@ -8,12 +8,14 @@ static inline void close_pair(int fd[2])
close(fd[1]);
}
#ifndef WIN32
static inline void dup_devnull(int to)
{
int fd = open("/dev/null", O_RDWR);
dup2(fd, to);
close(fd);
}
#endif
static const char **prepare_shell_cmd(const char **argv)
{
@@ -59,6 +61,78 @@ static int execv_shell_cmd(const char **argv)
}
#endif
#ifndef WIN32
static int child_err = 2;
static int child_notifier = -1;
static void notify_parent(void)
{
write(child_notifier, "", 1);
}
static NORETURN void die_child(const char *err, va_list params)
{
char msg[4096];
int len = vsnprintf(msg, sizeof(msg), err, params);
if (len > sizeof(msg))
len = sizeof(msg);
write(child_err, "fatal: ", 7);
write(child_err, msg, len);
write(child_err, "\n", 1);
exit(128);
}
static inline void set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
if (flags >= 0)
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
#endif
static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{
int status, code = -1;
pid_t waiting;
int failed_errno = 0;
while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
; /* nothing */
if (waiting < 0) {
failed_errno = errno;
error("waitpid for %s failed: %s", argv0, strerror(errno));
} else if (waiting != pid) {
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
code = WTERMSIG(status);
error("%s died of signal %d", argv0, code);
/*
* This return value is chosen so that code & 0xff
* mimics the exit code that a POSIX shell would report for
* a program that died from this signal.
*/
code -= 128;
} else if (WIFEXITED(status)) {
code = WEXITSTATUS(status);
/*
* Convert special exit code when execvp failed.
*/
if (code == 127) {
code = -1;
failed_errno = ENOENT;
if (!silent_exec_failure)
error("cannot run %s: %s", argv0,
strerror(ENOENT));
}
} else {
error("waitpid is confused (%s)", argv0);
}
errno = failed_errno;
return code;
}
int start_command(struct child_process *cmd)
{
int need_in, need_out, need_err;
@@ -120,9 +194,30 @@ fail_pipe:
trace_argv_printf(cmd->argv, "trace: run_command:");
#ifndef WIN32
{
int notify_pipe[2];
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
fflush(NULL);
cmd->pid = fork();
if (!cmd->pid) {
/*
* Redirect the channel to write syscall error messages to
* before redirecting the process's stderr so that all die()
* in subsequent call paths use the parent's stderr.
*/
if (cmd->no_stderr || need_err) {
child_err = dup(2);
set_cloexec(child_err);
}
set_die_routine(die_child);
close(notify_pipe[0]);
set_cloexec(notify_pipe[1]);
child_notifier = notify_pipe[1];
atexit(notify_parent);
if (cmd->no_stdin)
dup_devnull(0);
else if (need_in) {
@@ -163,8 +258,16 @@ fail_pipe:
unsetenv(*cmd->env);
}
}
if (cmd->preexec_cb)
if (cmd->preexec_cb) {
/*
* We cannot predict what the pre-exec callback does.
* Forgo parent notification.
*/
close(child_notifier);
child_notifier = -1;
cmd->preexec_cb();
}
if (cmd->git_cmd) {
execv_git_cmd(cmd->argv);
} else if (cmd->use_shell) {
@@ -172,51 +275,65 @@ fail_pipe:
} else {
execvp(cmd->argv[0], (char *const*) cmd->argv);
}
trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
strerror(errno));
exit(127);
/*
* Do not check for cmd->silent_exec_failure; the parent
* process will check it when it sees this exit code.
*/
if (errno == ENOENT)
exit(127);
else
die_errno("cannot exec '%s'", cmd->argv[0]);
}
if (cmd->pid < 0)
error("cannot fork() for %s: %s", cmd->argv[0],
strerror(failed_errno = errno));
/*
* Wait for child's execvp. If the execvp succeeds (or if fork()
* failed), EOF is seen immediately by the parent. Otherwise, the
* child process sends a single byte.
* Note that use of this infrastructure is completely advisory,
* therefore, we keep error checks minimal.
*/
close(notify_pipe[1]);
if (read(notify_pipe[0], &notify_pipe[1], 1) == 1) {
/*
* At this point we know that fork() succeeded, but execvp()
* failed. Errors have been reported to our stderr.
*/
wait_or_whine(cmd->pid, cmd->argv[0],
cmd->silent_exec_failure);
failed_errno = errno;
cmd->pid = -1;
}
close(notify_pipe[0]);
}
#else
{
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
int fhin = 0, fhout = 1, fherr = 2;
const char **sargv = cmd->argv;
char **env = environ;
if (cmd->no_stdin) {
s0 = dup(0);
dup_devnull(0);
} else if (need_in) {
s0 = dup(0);
dup2(fdin[0], 0);
} else if (cmd->in) {
s0 = dup(0);
dup2(cmd->in, 0);
}
if (cmd->no_stdin)
fhin = open("/dev/null", O_RDWR);
else if (need_in)
fhin = dup(fdin[0]);
else if (cmd->in)
fhin = dup(cmd->in);
if (cmd->no_stderr) {
s2 = dup(2);
dup_devnull(2);
} else if (need_err) {
s2 = dup(2);
dup2(fderr[1], 2);
}
if (cmd->no_stderr)
fherr = open("/dev/null", O_RDWR);
else if (need_err)
fherr = dup(fderr[1]);
if (cmd->no_stdout) {
s1 = dup(1);
dup_devnull(1);
} else if (cmd->stdout_to_stderr) {
s1 = dup(1);
dup2(2, 1);
} else if (need_out) {
s1 = dup(1);
dup2(fdout[1], 1);
} else if (cmd->out > 1) {
s1 = dup(1);
dup2(cmd->out, 1);
}
if (cmd->no_stdout)
fhout = open("/dev/null", O_RDWR);
else if (cmd->stdout_to_stderr)
fhout = dup(fherr);
else if (need_out)
fhout = dup(fdout[1]);
else if (cmd->out > 1)
fhout = dup(cmd->out);
if (cmd->dir)
die("chdir in start_command() not implemented");
@@ -229,7 +346,8 @@ fail_pipe:
cmd->argv = prepare_shell_cmd(cmd->argv);
}
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env,
fhin, fhout, fherr);
failed_errno = errno;
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
@@ -240,12 +358,12 @@ fail_pipe:
free(cmd->argv);
cmd->argv = sargv;
if (s0 >= 0)
dup2(s0, 0), close(s0);
if (s1 >= 0)
dup2(s1, 1), close(s1);
if (s2 >= 0)
dup2(s2, 2), close(s2);
if (fhin != 0)
close(fhin);
if (fhout != 1)
close(fhout);
if (fherr != 2)
close(fherr);
}
#endif
@@ -280,48 +398,6 @@ fail_pipe:
return 0;
}
static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{
int status, code = -1;
pid_t waiting;
int failed_errno = 0;
while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
; /* nothing */
if (waiting < 0) {
failed_errno = errno;
error("waitpid for %s failed: %s", argv0, strerror(errno));
} else if (waiting != pid) {
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
code = WTERMSIG(status);
error("%s died of signal %d", argv0, code);
/*
* This return value is chosen so that code & 0xff
* mimics the exit code that a POSIX shell would report for
* a program that died from this signal.
*/
code -= 128;
} else if (WIFEXITED(status)) {
code = WEXITSTATUS(status);
/*
* Convert special exit code when execvp failed.
*/
if (code == 127) {
code = -1;
failed_errno = ENOENT;
if (!silent_exec_failure)
error("cannot run %s: %s", argv0,
strerror(ENOENT));
}
} else {
error("waitpid is confused (%s)", argv0);
}
errno = failed_errno;
return code;
}
int finish_command(struct child_process *cmd)
{
return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);

22
setup.c
View File

@@ -263,6 +263,8 @@ static int check_repository_format_gently(int *nongit_ok)
const char *read_gitfile_gently(const char *path)
{
char *buf;
char *dir;
const char *slash;
struct stat st;
int fd;
size_t len;
@@ -287,9 +289,23 @@ const char *read_gitfile_gently(const char *path)
if (len < 9)
die("No path in gitfile: %s", path);
buf[len] = '\0';
if (!is_git_directory(buf + 8))
die("Not a git repository: %s", buf + 8);
path = make_absolute_path(buf + 8);
dir = buf + 8;
if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
size_t pathlen = slash+1 - path;
size_t dirlen = pathlen + len - 8;
dir = xmalloc(dirlen + 1);
strncpy(dir, path, pathlen);
strncpy(dir + pathlen, buf + 8, len - 8);
dir[dirlen] = '\0';
free(buf);
buf = dir;
}
if (!is_git_directory(dir))
die("Not a git repository: %s", dir);
path = make_absolute_path(dir);
free(buf);
return path;
}

View File

@@ -2458,14 +2458,6 @@ int has_pack_index(const unsigned char *sha1)
return 1;
}
int has_pack_file(const unsigned char *sha1)
{
struct stat st;
if (stat(sha1_pack_name(sha1), &st))
return 0;
return 1;
}
int has_sha1_pack(const unsigned char *sha1)
{
struct pack_entry e;

View File

@@ -91,13 +91,6 @@ void strbuf_ltrim(struct strbuf *sb)
sb->buf[sb->len] = '\0';
}
void strbuf_tolower(struct strbuf *sb)
{
int i;
for (i = 0; i < sb->len; i++)
sb->buf[i] = tolower(sb->buf[i]);
}
struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
{
int alloc = 2, pos = 0;
@@ -227,6 +220,12 @@ void strbuf_expand(struct strbuf *sb, const char *format, expand_fn_t fn,
break;
format = percent + 1;
if (*format == '%') {
strbuf_addch(sb, '%');
format++;
continue;
}
consumed = fn(sb, format, context);
if (consumed)
format += consumed;
@@ -251,6 +250,17 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
return 0;
}
void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
{
int i, len = src->len;
for (i = 0; i < len; i++) {
if (src->buf[i] == '%')
strbuf_addch(dst, '%');
strbuf_addch(dst, src->buf[i]);
}
}
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
{
size_t res;

View File

@@ -81,7 +81,6 @@ extern void strbuf_trim(struct strbuf *);
extern void strbuf_rtrim(struct strbuf *);
extern void strbuf_ltrim(struct strbuf *);
extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
extern void strbuf_tolower(struct strbuf *);
extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
extern void strbuf_list_free(struct strbuf **);
@@ -105,6 +104,7 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s) {
strbuf_add(sb, s, strlen(s));
}
static inline void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2) {
strbuf_grow(sb, sb2->len);
strbuf_add(sb, sb2->buf, sb2->len);
}
extern void strbuf_adddup(struct strbuf *sb, size_t pos, size_t len);
@@ -116,6 +116,7 @@ struct strbuf_expand_dict_entry {
const char *value;
};
extern size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder, void *context);
extern void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
__attribute__((format (printf,2,3)))
extern void strbuf_addf(struct strbuf *sb, const char *fmt, ...);

View File

@@ -5,7 +5,7 @@
#include "commit.h"
#include "revision.h"
int add_submodule_odb(const char *path)
static int add_submodule_odb(const char *path)
{
struct strbuf objects_directory = STRBUF_INIT;
struct alternate_object_database *alt_odb;

View File

@@ -179,37 +179,6 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
return ret_flags;
}
/*
* Invalidate the given 'name' from the cache, if 'name' matches
* completely with the cache.
*/
void invalidate_lstat_cache(const char *name, int len)
{
int match_len, previous_slash;
struct cache_def *cache = &default_cache; /* FIXME */
match_len = longest_path_match(name, len, cache->path, cache->len,
&previous_slash);
if (len == match_len) {
if ((cache->track_flags & FL_DIR) && previous_slash > 0) {
cache->path[previous_slash] = '\0';
cache->len = previous_slash;
cache->flags = FL_DIR;
} else {
reset_lstat_cache(cache);
}
}
}
/*
* Completely clear the contents of the cache
*/
void clear_lstat_cache(void)
{
struct cache_def *cache = &default_cache; /* FIXME */
reset_lstat_cache(cache);
}
#define USE_ONLY_LSTAT 0
/*

View File

@@ -2,21 +2,33 @@
# After setting the fake editor with this function, you can
#
# - override the commit message with $FAKE_COMMIT_MESSAGE,
# - override the commit message with $FAKE_COMMIT_MESSAGE
# - amend the commit message with $FAKE_COMMIT_AMEND
# - check that non-commit messages have a certain line count with $EXPECT_COUNT
# - rewrite a rebase -i script with $FAKE_LINES in the form
# - check the commit count in the commit message header with $EXPECT_HEADER_COUNT
# - rewrite a rebase -i script as directed by $FAKE_LINES.
# $FAKE_LINES consists of a sequence of words separated by spaces.
# The following word combinations are possible:
#
# "[<lineno1>] [<lineno2>]..."
# "<lineno>" -- add a "pick" line with the SHA1 taken from the
# specified line.
#
# If a line number is prefixed with "squash", "edit", or "reword", the
# respective line's command will be replaced with the specified one.
# "<cmd> <lineno>" -- add a line with the specified command
# ("squash", "fixup", "edit", or "reword") and the SHA1 taken
# from the specified line.
#
# "#" -- Add a comment line.
#
# ">" -- Add a blank line.
set_fake_editor () {
echo "#!$SHELL_PATH" >fake-editor.sh
cat >> fake-editor.sh <<\EOF
case "$1" in
*/COMMIT_EDITMSG)
test -z "$EXPECT_HEADER_COUNT" ||
test "$EXPECT_HEADER_COUNT" = $(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1") ||
exit
test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
exit
@@ -28,19 +40,24 @@ test -z "$EXPECT_COUNT" ||
test -z "$FAKE_LINES" && exit
grep -v '^#' < "$1" > "$1".tmp
rm -f "$1"
echo 'rebase -i script before editing:'
cat "$1".tmp
action=pick
for line in $FAKE_LINES; do
case $line in
squash|edit|reword)
squash|fixup|edit|reword)
action="$line";;
"#")
echo '# comment' >> "$1";;
">")
echo >> "$1";;
*)
echo sed -n "${line}s/^pick/$action/p"
sed -n "${line}p" < "$1".tmp
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
action=pick;;
esac
done
echo 'rebase -i script after editing:'
cat "$1"
EOF
test_set_editor "$(pwd)/fake-editor.sh"

14
t/t0061-run-command.sh Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
#
# Copyright (c) 2009 Ilari Liusvaara
#
test_description='Test run command'
. ./test-lib.sh
test_expect_success 'start_command reports ENOENT' '
test-run-command start-command-ENOENT ./does-not-exist
'
test_done

143
t/t2030-unresolve-info.sh Executable file
View File

@@ -0,0 +1,143 @@
#!/bin/sh
test_description='undoing resolution'
. ./test-lib.sh
check_resolve_undo () {
msg=$1
shift
while case $# in
0) break ;;
1|2|3) die "Bug in check-resolve-undo test" ;;
esac
do
path=$1
shift
for stage in 1 2 3
do
sha1=$1
shift
case "$sha1" in
'') continue ;;
esac
sha1=$(git rev-parse --verify "$sha1")
printf "100644 %s %s\t%s\n" $sha1 $stage $path
done
done >"$msg.expect" &&
git ls-files --resolve-undo >"$msg.actual" &&
test_cmp "$msg.expect" "$msg.actual"
}
prime_resolve_undo () {
git reset --hard &&
git checkout second^0 &&
test_tick &&
test_must_fail git merge third^0 &&
echo merge does not leave anything &&
check_resolve_undo empty &&
echo different >file &&
git add file &&
echo resolving records &&
check_resolve_undo recorded file initial:file second:file third:file
}
test_expect_success setup '
test_commit initial file first &&
git branch side &&
git branch another &&
test_commit second file second &&
git checkout side &&
test_commit third file third &&
git checkout another &&
test_commit fourth file fourth &&
git checkout master
'
test_expect_success 'add records switch clears' '
prime_resolve_undo &&
test_tick &&
git commit -m merged &&
echo committing keeps &&
check_resolve_undo kept file initial:file second:file third:file &&
git checkout second^0 &&
echo switching clears &&
check_resolve_undo cleared
'
test_expect_success 'rm records reset clears' '
prime_resolve_undo &&
test_tick &&
git commit -m merged &&
echo committing keeps &&
check_resolve_undo kept file initial:file second:file third:file &&
echo merge clears upfront &&
test_must_fail git merge fourth^0 &&
check_resolve_undo nuked &&
git rm -f file &&
echo resolving records &&
check_resolve_undo recorded file initial:file HEAD:file fourth:file &&
git reset --hard &&
echo resetting discards &&
check_resolve_undo discarded
'
test_expect_success 'plumbing clears' '
prime_resolve_undo &&
test_tick &&
git commit -m merged &&
echo committing keeps &&
check_resolve_undo kept file initial:file second:file third:file &&
echo plumbing clear &&
git update-index --clear-resolve-undo &&
check_resolve_undo cleared
'
test_expect_success 'add records checkout -m undoes' '
prime_resolve_undo &&
git diff HEAD &&
git checkout --conflict=merge file &&
echo checkout used the record and removed it &&
check_resolve_undo removed &&
echo the index and the work tree is unmerged again &&
git diff >actual &&
grep "^++<<<<<<<" actual
'
test_expect_success 'unmerge with plumbing' '
prime_resolve_undo &&
git update-index --unresolve file &&
git ls-files -u >actual &&
test $(wc -l <actual) = 3
'
test_expect_success 'rerere and rerere --forget' '
mkdir .git/rr-cache &&
prime_resolve_undo &&
echo record the resolution &&
git rerere &&
rerere_id=$(cd .git/rr-cache && echo */postimage) &&
rerere_id=${rerere_id%/postimage} &&
test -f .git/rr-cache/$rerere_id/postimage &&
git checkout -m file &&
echo resurrect the conflict &&
grep "^=======" file &&
echo reresolve the conflict &&
git rerere &&
test "z$(cat file)" = zdifferent &&
echo register the resolution again &&
git add file &&
check_resolve_undo kept file initial:file second:file third:file &&
test -z "$(git ls-files -u)" &&
git rerere forget file &&
! test -f .git/rr-cache/$rerere_id/postimage &&
tr "\0" "\n" <.git/MERGE_RR >actual &&
echo "$rerere_id file" >expect &&
test_cmp expect actual
'
test_done

38
t/t2104-update-index-gitfile.sh Executable file
View File

@@ -0,0 +1,38 @@
#!/bin/sh
#
# Copyright (c) 2010 Brad King
#
test_description='git update-index for gitlink to .git file.
'
. ./test-lib.sh
test_expect_success 'submodule with absolute .git file' '
mkdir sub1 &&
(cd sub1 &&
git init &&
REAL="$(pwd)/.real" &&
mv .git "$REAL"
echo "gitdir: $REAL" >.git &&
test_commit first)
'
test_expect_success 'add gitlink to absolute .git file' '
git update-index --add -- sub1
'
test_expect_success 'submodule with relative .git file' '
mkdir sub2 &&
(cd sub2 &&
git init &&
mv .git .real &&
echo "gitdir: .real" >.git &&
test_commit first)
'
test_expect_success 'add gitlink to relative .git file' '
git update-index --add -- sub2
'
test_done

View File

@@ -175,4 +175,43 @@ test_expect_success 'negated exclude matches can override previous ones' '
grep "^a.1" output
'
test_expect_success 'subdirectory ignore (setup)' '
mkdir -p top/l1/l2 &&
(
cd top &&
git init &&
echo /.gitignore >.gitignore &&
echo l1 >>.gitignore &&
echo l2 >l1/.gitignore &&
>l1/l2/l1
)
'
test_expect_success 'subdirectory ignore (toplevel)' '
(
cd top &&
git ls-files -o --exclude-standard
) >actual &&
>expect &&
test_cmp expect actual
'
test_expect_success 'subdirectory ignore (l1/l2)' '
(
cd top/l1/l2 &&
git ls-files -o --exclude-standard
) >actual &&
>expect &&
test_cmp expect actual
'
test_expect_success 'subdirectory ignore (l1)' '
(
cd top/l1 &&
git ls-files -o --exclude-standard
) >actual &&
>expect &&
test_cmp expect actual
'
test_done

View File

@@ -276,11 +276,13 @@ test_expect_success 'fail if the index has unresolved entries' '
test_must_fail git merge "$c5" &&
test_must_fail git merge "$c5" 2> out &&
grep "not possible because you have unmerged files" out &&
git add -u &&
test_must_fail git merge "$c5" 2> out &&
grep "You have not concluded your merge" out &&
rm -f .git/MERGE_HEAD &&
test_must_fail git merge "$c5" 2> out &&
grep "You are in the middle of a conflicted merge" out
grep "Your local changes to .* would be overwritten by merge." out
'
test_expect_success 'merge-recursive remove conflict' '

View File

@@ -14,58 +14,47 @@ that the result still makes sense.
set_fake_editor
# set up two branches like this:
# Set up the repository like this:
#
# A - B - C - D - E
# one - two - three - four (conflict-branch)
# /
# A - B - C - D - E (master)
# | \
# | F - G - H (branch1)
# | \
# \ I (branch2)
# \
# F - G - H
# \
# I
# J - K - L - M (no-conflict-branch)
#
# where B, D and G touch the same file.
# where A, B, D and G all touch file1, and one, two, three, four all
# touch file "conflict".
test_expect_success 'setup' '
: > file1 &&
git add file1 &&
test_tick &&
git commit -m A &&
git tag A &&
echo 1 > file1 &&
test_tick &&
git commit -m B file1 &&
: > file2 &&
git add file2 &&
test_tick &&
git commit -m C &&
echo 2 > file1 &&
test_tick &&
git commit -m D file1 &&
: > file3 &&
git add file3 &&
test_tick &&
git commit -m E &&
test_commit A file1 &&
test_commit B file1 &&
test_commit C file2 &&
test_commit D file1 &&
test_commit E file3 &&
git checkout -b branch1 A &&
: > file4 &&
git add file4 &&
test_tick &&
git commit -m F &&
git tag F &&
echo 3 > file1 &&
test_tick &&
git commit -m G file1 &&
: > file5 &&
git add file5 &&
test_tick &&
git commit -m H &&
test_commit F file4 &&
test_commit G file1 &&
test_commit H file5 &&
git checkout -b branch2 F &&
: > file6 &&
git add file6 &&
test_tick &&
git commit -m I &&
git tag I
test_commit I file6
git checkout -b conflict-branch A &&
for n in one two three four
do
test_commit $n conflict
done &&
git checkout -b no-conflict-branch A &&
for n in J K L M
do
test_commit $n file$n
done
'
test_expect_success 'no changes are a nop' '
git checkout branch2 &&
git rebase -i F &&
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
test $(git rev-parse I) = $(git rev-parse HEAD)
@@ -111,19 +100,20 @@ test_expect_success 'exchange two commits' '
cat > expect << EOF
diff --git a/file1 b/file1
index e69de29..00750ed 100644
index f70f10e..fd79235 100644
--- a/file1
+++ b/file1
@@ -0,0 +1 @@
+3
@@ -1 +1 @@
-A
+G
EOF
cat > expect2 << EOF
<<<<<<< HEAD
2
D
=======
3
>>>>>>> b7ca976... G
G
>>>>>>> 51047de... G
EOF
test_expect_success 'stop on conflicting pick' '
@@ -161,7 +151,8 @@ test_expect_success 'squash' '
test_tick &&
GIT_AUTHOR_NAME="Nitfol" git commit -m "nitfol" file7 &&
echo "******************************" &&
FAKE_LINES="1 squash 2" git rebase -i --onto master HEAD~2 &&
FAKE_LINES="1 squash 2" EXPECT_HEADER_COUNT=2 \
git rebase -i --onto master HEAD~2 &&
test B = $(cat file7) &&
test $(git rev-parse HEAD^) = $(git rev-parse master)
'
@@ -256,30 +247,113 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
test_expect_success 'multi-squash only fires up editor once' '
base=$(git rev-parse HEAD~4) &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
test $base = $(git rev-parse HEAD^) &&
test 1 = $(git show | grep ONCE | wc -l)
'
test_expect_success 'multi-fixup does not fire up editor' '
git checkout -b multi-fixup E &&
base=$(git rev-parse HEAD~4) &&
FAKE_COMMIT_AMEND="NEVER" FAKE_LINES="1 fixup 2 fixup 3 fixup 4" \
git rebase -i $base &&
test $base = $(git rev-parse HEAD^) &&
test 0 = $(git show | grep NEVER | wc -l) &&
git checkout to-be-rebased &&
git branch -D multi-fixup
'
test_expect_success 'commit message used after conflict' '
git checkout -b conflict-fixup conflict-branch &&
base=$(git rev-parse HEAD~4) &&
(
FAKE_LINES="1 fixup 3 fixup 4" &&
export FAKE_LINES &&
test_must_fail git rebase -i $base
) &&
echo three > conflict &&
git add conflict &&
FAKE_COMMIT_AMEND="ONCE" EXPECT_HEADER_COUNT=2 \
git rebase --continue &&
test $base = $(git rev-parse HEAD^) &&
test 1 = $(git show | grep ONCE | wc -l) &&
git checkout to-be-rebased &&
git branch -D conflict-fixup
'
test_expect_success 'commit message retained after conflict' '
git checkout -b conflict-squash conflict-branch &&
base=$(git rev-parse HEAD~4) &&
(
FAKE_LINES="1 fixup 3 squash 4" &&
export FAKE_LINES &&
test_must_fail git rebase -i $base
) &&
echo three > conflict &&
git add conflict &&
FAKE_COMMIT_AMEND="TWICE" EXPECT_HEADER_COUNT=2 \
git rebase --continue &&
test $base = $(git rev-parse HEAD^) &&
test 2 = $(git show | grep TWICE | wc -l) &&
git checkout to-be-rebased &&
git branch -D conflict-squash
'
cat > expect-squash-fixup << EOF
B
D
ONCE
EOF
test_expect_success 'squash and fixup generate correct log messages' '
git checkout -b squash-fixup E &&
base=$(git rev-parse HEAD~4) &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 fixup 2 squash 3 fixup 4" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
git cat-file commit HEAD | sed -e 1,/^\$/d > actual-squash-fixup &&
test_cmp expect-squash-fixup actual-squash-fixup &&
git checkout to-be-rebased &&
git branch -D squash-fixup
'
test_expect_success 'squash ignores comments' '
git checkout -b skip-comments E &&
base=$(git rev-parse HEAD~4) &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="# 1 # squash 2 # squash 3 # squash 4 #" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
test $base = $(git rev-parse HEAD^) &&
test 1 = $(git show | grep ONCE | wc -l) &&
git checkout to-be-rebased &&
git branch -D skip-comments
'
test_expect_success 'squash ignores blank lines' '
git checkout -b skip-blank-lines E &&
base=$(git rev-parse HEAD~4) &&
FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="> 1 > squash 2 > squash 3 > squash 4 >" \
EXPECT_HEADER_COUNT=4 \
git rebase -i $base &&
test $base = $(git rev-parse HEAD^) &&
test 1 = $(git show | grep ONCE | wc -l) &&
git checkout to-be-rebased &&
git branch -D skip-blank-lines
'
test_expect_success 'squash works as expected' '
for n in one two three four
do
echo $n >> file$n &&
git add file$n &&
git commit -m $n
done &&
git checkout -b squash-works no-conflict-branch &&
one=$(git rev-parse HEAD~3) &&
FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
FAKE_LINES="1 squash 3 2" EXPECT_HEADER_COUNT=2 \
git rebase -i HEAD~3 &&
test $one = $(git rev-parse HEAD~2)
'
test_expect_success 'interrupted squash works as expected' '
for n in one two three four
do
echo $n >> conflict &&
git add conflict &&
git commit -m $n
done &&
git checkout -b interrupted-squash conflict-branch &&
one=$(git rev-parse HEAD~3) &&
(
FAKE_LINES="1 squash 3 2" &&
@@ -296,12 +370,7 @@ test_expect_success 'interrupted squash works as expected' '
'
test_expect_success 'interrupted squash works as expected (case 2)' '
for n in one two three four
do
echo $n >> conflict &&
git add conflict &&
git commit -m $n
done &&
git checkout -b interrupted-squash2 conflict-branch &&
one=$(git rev-parse HEAD~3) &&
(
FAKE_LINES="3 squash 1 2" &&

73
t/t3415-rebase-autosquash.sh Executable file
View File

@@ -0,0 +1,73 @@
#!/bin/sh
test_description='auto squash'
. ./test-lib.sh
test_expect_success setup '
echo 0 >file0 &&
git add . &&
test_tick &&
git commit -m "initial commit" &&
echo 0 >file1 &&
echo 2 >file2 &&
git add . &&
test_tick &&
git commit -m "first commit" &&
echo 3 >file3 &&
git add . &&
test_tick &&
git commit -m "second commit" &&
git tag base
'
test_expect_success 'auto fixup' '
git reset --hard base &&
echo 1 >file1 &&
git add -u &&
test_tick &&
git commit -m "fixup! first"
git tag final-fixup &&
test_tick &&
git rebase --autosquash -i HEAD^^^ &&
git log --oneline >actual &&
test 3 = $(wc -l <actual) &&
git diff --exit-code final-fixup &&
test 1 = "$(git cat-file blob HEAD^:file1)" &&
test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
'
test_expect_success 'auto squash' '
git reset --hard base &&
echo 1 >file1 &&
git add -u &&
test_tick &&
git commit -m "squash! first"
git tag final-squash &&
test_tick &&
git rebase --autosquash -i HEAD^^^ &&
git log --oneline >actual &&
test 3 = $(wc -l <actual) &&
git diff --exit-code final-squash &&
test 1 = "$(git cat-file blob HEAD^:file1)" &&
test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
'
test_expect_success 'misspelled auto squash' '
git reset --hard base &&
echo 1 >file1 &&
git add -u &&
test_tick &&
git commit -m "squash! forst"
git tag final-missquash &&
test_tick &&
git rebase --autosquash -i HEAD^^^ &&
git log --oneline >actual &&
test 4 = $(wc -l <actual) &&
git diff --exit-code final-missquash &&
test 0 = $(git rev-list final-missquash...HEAD | wc -l)
'
test_done

View File

@@ -66,7 +66,7 @@ test_expect_success 'revert forbidden on dirty working tree' '
echo content >extra_file &&
git add extra_file &&
test_must_fail git revert HEAD 2>errors &&
grep "Dirty index" errors
grep "Your local changes would be overwritten by " errors
'

View File

@@ -217,7 +217,22 @@ test_expect_success 'rerere.autoupdate' '
git checkout version2 &&
test_must_fail git merge fifth &&
test 0 = $(git ls-files -u | wc -l)
'
test_expect_success 'merge --rerere-autoupdate' '
git config --unset rerere.autoupdate
git reset --hard &&
git checkout version2 &&
test_must_fail git merge --rerere-autoupdate fifth &&
test 0 = $(git ls-files -u | wc -l)
'
test_expect_success 'merge --no-rerere-autoupdate' '
git config rerere.autoupdate true
git reset --hard &&
git checkout version2 &&
test_must_fail git merge --no-rerere-autoupdate fifth &&
test 2 = $(git ls-files -u | wc -l)
'
test_done

69
t/t5523-push-upstream.sh Executable file
View File

@@ -0,0 +1,69 @@
#!/bin/sh
test_description='push with --set-upstream'
. ./test-lib.sh
test_expect_success 'setup bare parent' '
git init --bare parent &&
git remote add upstream parent
'
test_expect_success 'setup local commit' '
echo content >file &&
git add file &&
git commit -m one
'
check_config() {
(echo $2; echo $3) >expect.$1
(git config branch.$1.remote
git config branch.$1.merge) >actual.$1
test_cmp expect.$1 actual.$1
}
test_expect_success 'push -u master:master' '
git push -u upstream master:master &&
check_config master upstream refs/heads/master
'
test_expect_success 'push -u master:other' '
git push -u upstream master:other &&
check_config master upstream refs/heads/other
'
test_expect_success 'push -u --dry-run master:otherX' '
git push -u --dry-run upstream master:otherX &&
check_config master upstream refs/heads/other
'
test_expect_success 'push -u master2:master2' '
git branch master2 &&
git push -u upstream master2:master2 &&
check_config master2 upstream refs/heads/master2
'
test_expect_success 'push -u master2:other2' '
git push -u upstream master2:other2 &&
check_config master2 upstream refs/heads/other2
'
test_expect_success 'push -u :master2' '
git push -u upstream :master2 &&
check_config master2 upstream refs/heads/other2
'
test_expect_success 'push -u --all' '
git branch all1 &&
git branch all2 &&
git push -u --all &&
check_config all1 upstream refs/heads/all1 &&
check_config all2 upstream refs/heads/all2
'
test_expect_success 'push -u HEAD' '
git checkout -b headbranch &&
git push -u upstream HEAD &&
check_config headbranch upstream refs/heads/headbranch
'
test_done

View File

@@ -88,5 +88,49 @@ test_expect_success 'used receive-pack service' '
test_cmp exp act
'
test_expect_success 'non-fast-forward push fails' '
cd "$ROOT_PATH"/test_repo_clone &&
git checkout master &&
echo "changed" > path2 &&
git commit -a -m path2 --amend &&
HEAD=$(git rev-parse --verify HEAD) &&
!(git push -v origin >output 2>&1) &&
(cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
test $HEAD != $(git rev-parse --verify HEAD))
'
test_expect_success 'non-fast-forward push show ref status' '
grep "^ ! \[rejected\][ ]*master -> master (non-fast-forward)$" output
'
test_expect_success 'non-fast-forward push shows help message' '
grep \
"To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again. See the '"'non-fast-forward'"'
section of '"'git push --help'"' for details." output
'
test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
# create a dissimilarly-named remote ref so that git is unable to match the
# two refs (viz. local, remote) unless an explicit refspec is provided.
git push origin master:retsam
echo "change changed" > path2 &&
git commit -a -m path2 --amend &&
# push master too; this ensures there is at least one '"'push'"' command to
# the remote helper and triggers interaction with the helper.
!(git push -v origin +master master:retsam >output 2>&1) &&
grep "^ + [a-f0-9]*\.\.\.[a-f0-9]* *master -> master (forced update)$" output &&
grep "^ ! \[rejected\] *master -> retsam (non-fast-forward)$" output &&
grep \
"To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again. See the '"'non-fast-forward'"'
section of '"'git push --help'"' for details." output
'
stop_httpd
test_done

View File

@@ -19,6 +19,13 @@ test_cmp expect.$1 output.$1
"
}
test_format percent %%h <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
%h
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
%h
EOF
test_format hash %H%n%h <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
131a310eb913d107dd3c09a65d1651175898735d

View File

@@ -8,18 +8,6 @@ test_description='git grep various.
. ./test-lib.sh
test_expect_success 'Check for external grep support' '
case "$(git grep -h 2>&1|grep ext-grep)" in
*"(default)"*)
test_set_prereq EXTGREP
true;;
*"(ignored by this build)"*)
true;;
*)
false;;
esac
'
cat >hello.c <<EOF
#include <stdio.h>
int main(int argc, const char **argv)
@@ -314,8 +302,8 @@ test_expect_success 'grep -C1, hunk mark between files' '
test_cmp expected actual
'
test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '
git grep -C1 --no-ext-grep "^[yz]" >actual &&
test_expect_success 'grep -C1 hunk mark between files' '
git grep -C1 "^[yz]" >actual &&
test_cmp expected actual
'
@@ -371,7 +359,7 @@ test_expect_success 'log grep (6)' '
test_expect_success 'grep with CE_VALID file' '
git update-index --assume-unchanged t/t &&
rm t/t &&
test "$(git grep --no-ext-grep test)" = "t/t:test" &&
test "$(git grep test)" = "t/t:test" &&
git update-index --no-assume-unchanged t/t &&
git checkout t/t
'
@@ -438,16 +426,56 @@ test_expect_success 'grep -Fi' '
test_cmp expected actual
'
test_expect_success EXTGREP 'external grep is called' '
GIT_TRACE=2 git grep foo >/dev/null 2>actual &&
grep "trace: grep:.*foo" actual >/dev/null
test_expect_success 'outside of git repository' '
rm -fr non &&
mkdir -p non/git/sub &&
echo hello >non/git/file1 &&
echo world >non/git/sub/file2 &&
echo ".*o*" >non/git/.gitignore &&
{
echo file1:hello &&
echo sub/file2:world
} >non/expect.full &&
echo file2:world >non/expect.sub
(
GIT_CEILING_DIRECTORIES="$(pwd)/non/git" &&
export GIT_CEILING_DIRECTORIES &&
cd non/git &&
test_must_fail git grep o &&
git grep --no-index o >../actual.full &&
test_cmp ../expect.full ../actual.full
cd sub &&
test_must_fail git grep o &&
git grep --no-index o >../../actual.sub &&
test_cmp ../../expect.sub ../../actual.sub
)
'
test_expect_success EXTGREP 'no external grep when skip-worktree entries exist' '
git update-index --skip-worktree file &&
GIT_TRACE=2 git grep foo >/dev/null 2>actual &&
! grep "trace: grep:" actual >/dev/null &&
git update-index --no-skip-worktree file
test_expect_success 'inside git repository but with --no-index' '
rm -fr is &&
mkdir -p is/git/sub &&
echo hello >is/git/file1 &&
echo world >is/git/sub/file2 &&
echo ".*o*" >is/git/.gitignore &&
{
echo file1:hello &&
echo sub/file2:world
} >is/expect.full &&
: >is/expect.empty &&
echo file2:world >is/expect.sub
(
cd is/git &&
git init &&
test_must_fail git grep o >../actual.full &&
test_cmp ../expect.empty ../actual.full &&
git grep --no-index o >../actual.full &&
test_cmp ../expect.full ../actual.full &&
cd sub &&
test_must_fail git grep o >../../actual.sub &&
test_cmp ../../expect.empty ../../actual.sub &&
git grep --no-index o >../../actual.sub &&
test_cmp ../../expect.sub ../../actual.sub
)
'
test_done

Some files were not shown because too many files have changed in this diff Show More