mirror of
https://github.com/git/git.git
synced 2026-04-01 12:30:09 +02:00
Merge branch 'master' of git://repo.or.cz/alt-git
This commit is contained in:
28
Documentation/RelNotes-1.6.5.8.txt
Normal file
28
Documentation/RelNotes-1.6.5.8.txt
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
------
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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.
|
||||
|
||||
44
Makefile
44
Makefile
@@ -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
|
||||
|
||||
18
advice.c
18
advice.c
@@ -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);
|
||||
}
|
||||
|
||||
6
advice.h
6
advice.h
@@ -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 */
|
||||
|
||||
10
bisect.c
10
bisect.c
@@ -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);
|
||||
}
|
||||
|
||||
2
bisect.h
2
bisect.h
@@ -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
21
blob.c
@@ -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
9
blob.h
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
358
builtin-grep.c
358
builtin-grep.c
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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] = "";
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
16
cache.h
@@ -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;
|
||||
|
||||
1
commit.h
1
commit.h
@@ -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,
|
||||
|
||||
116
compat/mingw.c
116
compat/mingw.c
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
110
compat/win32/pthread.c
Normal 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
67
compat/win32/pthread.h
Normal 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 */
|
||||
6
config.c
6
config.c
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
1
daemon.c
1
daemon.c
@@ -147,7 +147,6 @@ static char *path_ok(char *directory)
|
||||
{ "IP", ip_address },
|
||||
{ "P", tcp_port },
|
||||
{ "D", directory },
|
||||
{ "%", "%" },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
2
date.c
2
date.c
@@ -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
199
dir.c
@@ -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);
|
||||
|
||||
2
entry.c
2
entry.c
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ;;
|
||||
--)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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};
|
||||
|
||||
25
git-pull.sh
25
git-pull.sh
@@ -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=
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
;;
|
||||
|
||||
@@ -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
2
git.c
@@ -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
84
grep.c
@@ -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
1
grep.h
@@ -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
10
http.c
@@ -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
9
http.h
@@ -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
25
ident.c
@@ -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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
21
object.c
21
object.c
@@ -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) {
|
||||
|
||||
5
object.h
5
object.h
@@ -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 .. */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
2
pretty.c
2
pretty.c
@@ -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';
|
||||
}
|
||||
|
||||
2
quote.c
2
quote.c
@@ -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;
|
||||
|
||||
1
quote.h
1
quote.h
@@ -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);
|
||||
|
||||
24
read-cache.c
24
read-cache.c
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
50
remote.c
50
remote.c
@@ -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;
|
||||
|
||||
2
remote.h
2
remote.h
@@ -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
267
rerere.c
@@ -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);
|
||||
}
|
||||
|
||||
11
rerere.h
11
rerere.h
@@ -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
176
resolve-undo.c
Normal 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
16
resolve-undo.h
Normal 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
|
||||
244
run-command.c
244
run-command.c
@@ -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], ¬ify_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
22
setup.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
24
strbuf.c
24
strbuf.c
@@ -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;
|
||||
|
||||
3
strbuf.h
3
strbuf.h
@@ -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, ...);
|
||||
|
||||
@@ -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;
|
||||
|
||||
31
symlinks.c
31
symlinks.c
@@ -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
|
||||
|
||||
/*
|
||||
|
||||
@@ -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
14
t/t0061-run-command.sh
Executable 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
143
t/t2030-unresolve-info.sh
Executable 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
38
t/t2104-update-index-gitfile.sh
Executable 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
|
||||
@@ -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
|
||||
|
||||
@@ -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' '
|
||||
|
||||
@@ -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
73
t/t3415-rebase-autosquash.sh
Executable 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
|
||||
@@ -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
|
||||
|
||||
'
|
||||
|
||||
|
||||
@@ -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
69
t/t5523-push-upstream.sh
Executable 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user