gitk-wish
gitweb/gitweb.cgi
test-chmtime
+test-ctype
test-date
test-delta
test-dump-cache-tree
test-parse-options
test-path-utils
test-sha1
+test-sigchain
common-cmds.h
*.tar.gz
*.dsc
As for more concrete guidelines, just imitate the existing code
(this is a good guideline, no matter which project you are
-contributing to). But if you must have a list of rules,
-here they are.
+contributing to). It is always preferable to match the _local_
+convention. New code added to git suite is expected to match
+the overall style of existing code. Modifications to existing
+code is expected to match the style the surrounding code already
+uses (even if it doesn't match the overall style of existing code).
+
+But if you must have a list of rules, here they are.
For shell scripts specifically (not exhaustive):
Fixes since v1.6.1
------------------
+* "git add frotz/nitfol" when "frotz" is a submodule should have errored
+ out, but it didn't.
+
* "git apply" took file modes from the patch text and updated the mode
bits of the target tree even when the patch was not about mode changes.
+* "git bisect view" on Cygwin did not launch gitk
+
* "git checkout $tree" did not trigger an error.
* "git commit" tried to remove COMMIT_EDITMSG from the work tree by mistake.
* "git describe --all" complained when a commit is described with a tag,
which was nonsense.
+* "git diff --no-index --" did not trigger no-index (aka "use git-diff as
+ a replacement of diff on untracked files") behaviour.
+
+* "git format-patch -1 HEAD" on a root commit failed to produce patch
+ text.
+
* "git fsck branch" did not work as advertised; instead it behaved the same
way as "git fsck".
* "git mv -k" with more than one errorneous paths misbehaved.
+* "git read-tree -m -u" hence branch switching incorrectly lost a
+ subdirectory in rare cases.
+
* "git rebase -i" issued an unnecessary error message upon a user error of
marking the first commit to be "squash"ed.
-Other documentation updates.
-
----
-exec >/var/tmp/1
-O=v1.6.1-60-g78f111e
-echo O=$(git describe maint)
-git shortlog --no-merges $O..maint
+* "git shortlog" did not format a commit message with multi-line
+ subject correctly.
+Many documentation updates.
--- /dev/null
+GIT v1.6.1.2 Release Notes
+==========================
+
+Fixes since v1.6.1.1
+--------------------
+
+* The logic for rename detectin in internal diff used by commands like
+ "git diff" and "git blame" have been optimized to avoid loading the same
+ blob repeatedly.
+
+* We did not allow writing out a blob that is larger than 2GB for no good
+ reason.
+
+* "git format-patch -o $dir", when $dir is a relative directory, used it
+ as relative to the root of the work tree, not relative to the current
+ directory.
+
+* v1.6.1 introduced an optimization for "git push" into a repository (A)
+ that borrows its objects from another repository (B) to avoid sending
+ objects that are available in repository B, when they are not yet used
+ by repository A. However the code on the "git push" sender side was
+ buggy and did not work when repository B had new objects that are not
+ known by the sender. This caused pushing into a "forked" repository
+ served by v1.6.1 software using "git push" from v1.6.1 sometimes did not
+ work. The bug was purely on the "git push" sender side, and has been
+ corrected.
+
+* "git status -v" did not paint its diff output in colour even when
+ color.ui configuration was set.
+
+* "git ls-tree" learned --full-tree option to help Porcelain scripts that
+ want to always see the full path regardless of the current working
+ directory.
+
+* "git grep" incorrectly searched in work tree paths even when they are
+ marked as assume-unchanged. It now searches in the index entries.
+
+* "git gc" with no grace period needlessly ejected packed but unreachable
+ objects in their loose form, only to delete them right away.
(subsystems)
+* git-svn updates.
+
+* gitweb updates, including a new patch view and RSS/Atom feed
+ improvements.
+
(portability)
(performance)
(usability, bells and whistles)
-* "git-add -p" learned 'g'oto action to jump directly to a hunk.
+* automatic typo correction works on aliases as well
+
+* @{-1} is a way to refer to the last branch you were on. This is
+ accepted not only where an object name is expected, but anywhere
+ a branch name is expected. E.g. "git branch --track mybranch @{-1}"
+ "git rev-parse --symbolic-full-name @{-1}".
+
+* "git add -p" learned 'g'oto action to jump directly to a hunk.
+
+* when "git am" stops upon a patch that does not apply, it shows the
+ title of the offending patch.
+
+* "git am --directory=<dir>" and "git am --reject" passes these options
+ to underlying "git apply".
+
+* "git clone" now makes its best effort when cloning from an empty
+ repository to set up configuration variables to refer to the remote
+ repository.
+
+* "git checkout -" is a shorthand for "git checkout @{-1}".
-* git-cherry defaults to HEAD when the <upstream> argument is not given.
+* "git cherry" defaults to whatever the current branch is tracking (if
+ exists) when the <upstream> argument is not given.
-* git-cvsserver can be told not to add extra "via git-CVS emulator" to the
- commit log message it serves via gitcvs.commitmsgannotation configuration.
+* "git cvsserver" can be told not to add extra "via git-CVS emulator" to
+ the commit log message it serves via gitcvs.commitmsgannotation
+ configuration.
-* git-diff learned a new option --inter-hunk-context to coalesce close
+* "git diff" learned a new option --inter-hunk-context to coalesce close
hunks together and show context between them.
-* git-filter-branch learned --prune-empty option that discards commits
+* The definition of what constitutes a word for "git diff --color-words"
+ can be customized via gitattributes, command line or a configuration.
+
+* "git diff" learned --patience to run "patience diff" algorithm.
+
+* Some combinations of -b/-w/--ignore-space-at-eol to "git diff" did
+ not work as expected.
+
+* "git filter-branch" learned --prune-empty option that discards commits
that do not change the contents.
-* git-ls-tree learned --full-tree option that shows the path in full
+* "git grep -w" and "git grep" for fixed strings have been optimized.
+
+* "git log" and friends include HEAD to the set of starting points
+ when --all is given. This makes a difference when you are not on
+ any branch.
+
+* "git ls-tree" learned --full-tree option that shows the path in full
regardless of where in the work tree hierarchy the command was started.
-* git-mergetool learned -y(--no-prompt) option to disable prompting.
+* "git mergetool" learned -y(--no-prompt) option to disable prompting.
+
+* "git rebase -i" can transplant a history down to root to elsewhere
+ with --root option.
-* "git-reset --merge" is a new mode that works similar to the way
+* "git reset --merge" is a new mode that works similar to the way
"git checkout" switches branches, taking the local changes while
switching to another commit.
* git-bundle did not exclude annotated tags even when a range given from the
command line wanted to.
-* git-grep did not work correctly for index entries with assume-unchanged bit.
-
* branch switching and merges had a silly bug that did not validate
the correct directory when making sure an existing subdirectory is
clean.
--
exec >/var/tmp/1
-O=v1.6.1-134-ge98c6a1
+O=v1.6.1.2-252-g8c95d3c
echo O=$(git describe master)
git shortlog --no-merges $O..master ^maint
A boolean to inhibit the standard behavior of printing a space
before each empty output line. Defaults to false.
+diff.wordRegex::
+ A POSIX Extended Regular Expression used to determine what is a "word"
+ when performing word-by-word difference calculations. Character
+ sequences that match the regular expression are "words", all other
+ characters are *ignorable* whitespace.
+
fetch.unpackLimit::
If the number of objects fetched over the git native
transfer is below this
--patch-with-raw::
Synonym for "-p --raw".
---patience:
+--patience::
Generate a diff using the "patience diff" algorithm.
--stat[=width[,name-width]]::
Turn off colored diff, even when the configuration file
gives the default to color output.
---color-words::
- Show colored word diff, i.e. color words which have changed.
+--color-words[=<regex>]::
+ Show colored word diff, i.e., color words which have changed.
+ By default, words are separated by whitespace.
++
+When a <regex> is specified, every non-overlapping match of the
+<regex> is considered a word. Anything between these matches is
+considered whitespace and ignored(!) for the purposes of finding
+differences. You may want to append `|[^[:space:]]` to your regular
+expression to make sure that it matches all non-whitespace characters.
+A match that contains a newline is silently truncated(!) at the
+newline.
++
+The regex can also be set via a diff driver or configuration option, see
+linkgit:gitattributes[1] or linkgit:git-config[1]. Giving it explicitly
+overrides any diff driver or configuration setting. Diff drivers
+override configuration settings.
--no-renames::
Turn off rename detection, even when the configuration
+
When this parameter names a non-branch (but still a valid commit object),
your HEAD becomes 'detached'.
++
+As a special case, the "`@\{-N\}`" syntax for the N-th last branch
+checks out the branch (instead of detaching). You may also specify
+"`-`" which is synonymous with "`@\{-1\}`".
Detached HEAD
of a remote (see the section <<REMOTES,REMOTES>> below).
<refspec>...::
- The canonical format of a <refspec> parameter is
- `+?<src>:<dst>`; that is, an optional plus `{plus}`, followed
- by the source ref, followed by a colon `:`, followed by
- the destination ref.
+ The format of a <refspec> parameter is an optional plus
+ `{plus}`, followed by the source ref <src>, followed
+ by a colon `:`, followed by the destination ref <dst>.
+ It is used to specify with what <src> object the <dst> ref
+ in the remote repository is to be updated.
+
-The <src> side represents the source branch (or arbitrary
-"SHA1 expression", such as `master~4` (four parents before the
-tip of `master` branch); see linkgit:git-rev-parse[1]) that you
-want to push. The <dst> side represents the destination location.
+The <src> is often the name of the branch you would want to push, but
+it can be any arbitrary "SHA-1 expression", such as `master~4` or
+`HEAD` (see linkgit:git-rev-parse[1]).
+
-The local ref that matches <src> is used
-to fast forward the remote ref that matches <dst>. If
-the optional leading plus `+` is used, the remote ref is updated
-even if it does not result in a fast forward update.
+The <dst> tells which ref on the remote side is updated with this
+push. Arbitrary expressions cannot be used here, an actual ref must
+be named. If `:`<dst> is omitted, the same ref as <src> will be
+updated.
+
-`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+The object referenced by <src> is used to fast forward the ref <dst>
+on the remote side. If the optional leading plus `{plus}` is used, the
+remote ref is updated even if it does not result in a fast forward
+update.
+
-A lonely <src> parameter (without a colon and a destination) pushes
-the <src> to the same name in the destination repository.
+`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
+
Pushing an empty <src> allows you to delete the <dst> ref from
the remote repository.
with it. If `master` did not exist remotely, it would be
created.
-git push origin :experimental::
- Find a ref that matches `experimental` in the `origin` repository
- (e.g. `refs/heads/experimental`), and delete it.
+git push origin HEAD::
+ A handy way to push the current branch to the same name on the
+ remote.
git push origin master:satellite/master dev:satellite/dev::
Use the source ref that matches `master` (e.g. `refs/heads/master`)
`refs/remotes/satellite/master`) in the `origin` repository, then
do the same for `dev` and `satellite/dev`.
+git push origin HEAD:master::
+ Push the current branch to the remote ref matching `master` in the
+ `origin` repository. This form is convenient to push the current
+ branch without thinking about its local name.
+
git push origin master:refs/heads/experimental::
Create the branch `experimental` in the `origin` repository
by copying the current `master` branch. This form is only
the local name and the remote name are different; otherwise,
the ref name on its own will work.
+git push origin :experimental::
+ Find a ref that matches `experimental` in the `origin` repository
+ (e.g. `refs/heads/experimental`), and delete it.
+
+
Author
------
Written by Junio C Hamano <gitster@pobox.com>, later rewritten in C
reflog of the current branch. For example, if you are on the
branch 'blabla', then '@\{1\}' means the same as 'blabla@\{1\}'.
+* The special construct '@\{-<n>\}' means the <n>th branch checked out
+ before the current one.
+
* A suffix '{caret}' to a revision parameter means the first parent of
that commit object. '{caret}<n>' means the <n>th parent (i.e.
'rev{caret}'
# Note how we don't need an entry for <jane@laptop.(none)>, because the
# real name of that author is correct already, and coalesced directly.
Jane Doe <jane@desktop.(none)>
-Joe R. Developer <joe@random.com>
+Joe R. Developer <joe@example.com>
------------
Author
repository, either don't use this option or you should both use it in
the same local timezone.
+--ignore-paths=<regex>;;
+ This allows one to specify Perl regular expression that will
+ cause skipping of all matching paths from checkout from SVN.
+ Examples:
+
+ --ignore-paths="^doc" - skip "doc*" directory for every fetch.
+
+ --ignore-paths="^[^/]+/(?:branches|tags)" - skip "branches"
+ and "tags" of first level directories.
+
+ Regular expression is not persistent, you should specify
+ it every time when fetching.
+
'clone'::
Runs 'init' and 'fetch'. It will automatically create a
directory based on the basename of the URL passed to it;
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
<name> [<commit> | <object>]
'git tag' -d <name>...
-'git tag' [-n[<num>]] -l [<pattern>]
+'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
'git tag' -v <name>...
DESCRIPTION
List tags with names that match the given pattern (or all if no pattern is given).
Typing "git tag" without arguments, also lists all tags.
+--contains <commit>::
+ Only list tags which contain the specified commit.
+
-m <msg>::
Use the given tag message (instead of prompting).
If multiple `-m` options are given, their values are
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.6.1/git.html[documentation for release 1.6.1]
+* link:v1.6.1.1/git.html[documentation for release 1.6.1.1]
* release notes for
+ link:RelNotes-1.6.1.1.txt[1.6.1.1],
link:RelNotes-1.6.1.txt[1.6.1].
* link:v1.6.0.6/git.html[documentation for release 1.6.0.6]
- `bibtex` suitable for files with BibTeX coded references.
+- `cpp` suitable for source code in the C and C++ languages.
+
- `html` suitable for HTML/XHTML documents.
- `java` suitable for source code in the Java language.
- `tex` suitable for source code for LaTeX documents.
+Customizing word diff
+^^^^^^^^^^^^^^^^^^^^^
+
+You can customize the rules that `git diff --color-words` uses to
+split words in a line, by specifying an appropriate regular expression
+in the "diff.*.wordRegex" configuration variable. For example, in TeX
+a backslash followed by a sequence of letters forms a command, but
+several such commands can be run together without intervening
+whitespace. To separate them, use a regular expression such as
+
+------------------------
+[diff "tex"]
+ wordRegex = "\\\\[a-zA-Z]+|[{}]|\\\\.|[^\\{}[:space:]]+"
+------------------------
+
+A built-in pattern is provided for all languages listed in the
+previous section.
+
+
Performing text diffs of binary files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This merges the changes from Bob's "master" branch into Alice's
current branch. If Alice has made her own changes in the meantime,
-then she may need to manually fix any conflicts. (Note that the
-"master" argument in the above command is actually unnecessary, as it
-is the default.)
+then she may need to manually fix any conflicts.
The "pull" command thus performs two operations: it fetches changes
from a remote branch, then merges them into the current branch.
of a remote (see the section <<REMOTES,REMOTES>> below).
<refspec>::
- The canonical format of a <refspec> parameter is
- `+?<src>:<dst>`; that is, an optional plus `{plus}`, followed
- by the source ref, followed by a colon `:`, followed by
- the destination ref.
+ The format of a <refspec> parameter is an optional plus
+ `{plus}`, followed by the source ref <src>, followed
+ by a colon `:`, followed by the destination ref <dst>.
+
The remote ref that matches <src>
is fetched, and if <dst> is not empty string, the local
ref that matches it is fast forwarded using <src>.
-Again, if the optional plus `+` is used, the local ref
+If the optional plus `+` is used, the local ref
is updated even if it does not result in a fast forward
update.
+
buffer from its strbuf shell in a safe way. That is the sole supported
way. This will give you a malloced buffer that you can later `free()`.
+
-However, it it totally safe to modify anything in the string pointed by
+However, it is totally safe to modify anything in the string pointed by
the `buf` member, between the indices `0` and `len-1` (inclusive).
. The `buf` member is a byte array that has at least `len + 1` bytes
work-in-progress changes.
------------------------------------------------
-$ git stash "work in progress for foo feature"
+$ git stash save "work in progress for foo feature"
------------------------------------------------
This command will save your changes away to the `stash`, and
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.0.2.GIT
+DEF_VER=v1.6.1.GIT
LF='
'
# Define NO_EXPAT if you do not have expat installed. git-http-push is
# not built, and you cannot push using http:// and https:// transports.
#
+# Define EXPATDIR=/foo/bar if your expat header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
#
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
# Among the variables below, these:
# gitexecdir
# template_dir
+# mandir
+# infodir
# htmldir
# ETC_GITCONFIG (but not sysconfdir)
-# can be specified as a relative path ../some/where/else (which must begin
-# with ../); this is interpreted as relative to $(bindir) and "git" at
+# can be specified as a relative path some/where/else;
+# this is interpreted as relative to $(prefix) and "git" at
# runtime figures out where they are based on the path to the executable.
# This can help installing the suite in a relocatable way.
prefix = $(HOME)
-bindir = $(prefix)/bin
-mandir = $(prefix)/share/man
-infodir = $(prefix)/share/info
-gitexecdir = $(prefix)/libexec/git-core
+bindir_relative = bin
+bindir = $(prefix)/$(bindir_relative)
+mandir = share/man
+infodir = share/info
+gitexecdir = libexec/git-core
sharedir = $(prefix)/share
-template_dir = $(sharedir)/git-core/templates
-htmldir=$(sharedir)/doc/git-doc
+template_dir = share/git-core/templates
+htmldir = share/doc/git-doc
ifeq ($(prefix),/usr)
sysconfdir = /etc
+ETC_GITCONFIG = $(sysconfdir)/gitconfig
else
sysconfdir = $(prefix)/etc
+ETC_GITCONFIG = etc/gitconfig
endif
lib = lib
-ETC_GITCONFIG = $(sysconfdir)/gitconfig
# DESTDIR=
# default configuration for gitweb
LIB_H += run-command.h
LIB_H += sha1-lookup.h
LIB_H += sideband.h
+LIB_H += sigchain.h
LIB_H += strbuf.h
LIB_H += tag.h
LIB_H += transport.h
LIB_OBJS += sha1_name.o
LIB_OBJS += shallow.o
LIB_OBJS += sideband.o
+LIB_OBJS += sigchain.o
LIB_OBJS += strbuf.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
SNPRINTF_RETURNS_BOGUS = YesPlease
NO_SVN_TESTS = YesPlease
NO_PERL_MAKEMAKER = YesPlease
+ RUNTIME_PREFIX = YesPlease
NO_POSIX_ONLY_PROGRAMS = YesPlease
NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
EXTLIBS += -lws2_32
X = .exe
- gitexecdir = ../libexec/git-core
- template_dir = ../share/git-core/templates/
- ETC_GITCONFIG = ../etc/gitconfig
endif
ifneq (,$(findstring arm,$(uname_M)))
ARM_SHA1 = YesPlease
BASIC_LDFLAGS += -L/opt/local/lib
endif
endif
+ PTHREAD_LIBS =
endif
ifndef CC_LD_DYNPATH
endif
endif
ifndef NO_EXPAT
- EXPAT_LIBEXPAT = -lexpat
+ ifdef EXPATDIR
+ BASIC_CFLAGS += -I$(EXPATDIR)/include
+ EXPAT_LIBEXPAT = -L$(EXPATDIR)/$(lib) $(CC_LD_DYNPATH)$(EXPATDIR)/$(lib) -lexpat
+ else
+ EXPAT_LIBEXPAT = -lexpat
+ endif
endif
endif
COMPAT_CFLAGS += -DINTERNAL_QSORT
COMPAT_OBJS += compat/qsort.o
endif
+ifdef RUNTIME_PREFIX
+ COMPAT_CFLAGS += -DRUNTIME_PREFIX
+endif
ifdef NO_PTHREADS
THREADED_DELTA_SEARCH =
DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
bindir_SQ = $(subst ','\'',$(bindir))
+bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
mandir_SQ = $(subst ','\'',$(mandir))
infodir_SQ = $(subst ','\'',$(infodir))
gitexecdir_SQ = $(subst ','\'',$(gitexecdir))
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
exec_cmd.o: exec_cmd.c GIT-CFLAGS
- $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' $<
+ $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
+ '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
+ '-DBINDIR="$(bindir_relative_SQ)"' \
+ '-DPREFIX="$(prefix_SQ)"' \
+ $<
+
builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
TEST_PROGRAMS += test-ctype$X
TEST_PROGRAMS += test-date$X
TEST_PROGRAMS += test-delta$X
+TEST_PROGRAMS += test-dump-cache-tree$X
TEST_PROGRAMS += test-genrandom$X
TEST_PROGRAMS += test-match-trees$X
TEST_PROGRAMS += test-parse-options$X
TEST_PROGRAMS += test-path-utils$X
TEST_PROGRAMS += test-sha1$X
+TEST_PROGRAMS += test-sigchain$X
all:: $(TEST_PROGRAMS)
### Installation rules
-ifeq ($(firstword $(subst /, ,$(template_dir))),..)
-template_instdir = $(bindir)/$(template_dir)
-else
+ifeq ($(abspath $(template_dir)),$(template_dir))
template_instdir = $(template_dir)
+else
+template_instdir = $(prefix)/$(template_dir)
endif
export template_instdir
-ifeq ($(firstword $(subst /, ,$(gitexecdir))),..)
-gitexec_instdir = $(bindir)/$(gitexecdir)
-else
+ifeq ($(abspath $(gitexecdir)),$(gitexecdir))
gitexec_instdir = $(gitexecdir)
+else
+gitexec_instdir = $(prefix)/$(gitexecdir)
endif
gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
export gitexec_instdir
return error("%s: %s", old_name, strerror(errno));
}
- if (!cached)
+ if (!cached && !tpatch)
st_mode = ce_mode_from_stat(*ce, st->st_mode);
if (patch->is_new < 0)
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st_mode, patch->old_mode);
- if (!patch->new_mode)
+ if (!patch->new_mode && !patch->is_delete)
patch->new_mode = st_mode;
return 0;
int kinds;
};
-static int has_commit(struct commit *commit, struct commit_list *with_commit)
-{
- if (!with_commit)
- return 1;
- while (with_commit) {
- struct commit *other;
-
- other = with_commit->item;
- with_commit = with_commit->next;
- if (in_merge_bases(other, &commit, 1))
- return 1;
- }
- return 0;
-}
-
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct ref_list *ref_list = (struct ref_list*)(cb_data);
return error("branch '%s' does not point at a commit", refname);
/* Filter with with_commit if specified */
- if (!has_commit(commit, ref_list->with_commit))
+ if (!is_descendant_of(commit, ref_list->with_commit))
return 0;
/* Don't add types the caller doesn't want */
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached && head_commit && has_commit(head_commit, with_commit)) {
+ if (detached && head_commit &&
+ is_descendant_of(head_commit, with_commit)) {
struct ref_item item;
item.name = xstrdup("(no branch)");
item.kind = REF_LOCAL_BRANCH;
strbuf_release(&newsection);
}
-static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
-{
- unsigned char sha1[20];
- struct commit *commit;
-
- if (!arg)
- return -1;
- if (get_sha1(arg, sha1))
- die("malformed object name %s", arg);
- commit = lookup_commit_reference(sha1);
- if (!commit)
- die("no such commit %s", arg);
- commit_list_insert(commit, opt->value);
- return 0;
-}
-
static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset)
{
merge_filter = ((opt->long_name[0] == 'n')
OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
"print only branches that contain the commit",
PARSE_OPT_LASTARG_DEFAULT,
- opt_parse_with_commit, (intptr_t)"HEAD",
+ parse_opt_with_commit, (intptr_t)"HEAD",
},
{
OPTION_CALLBACK, 0, "with", &with_commit, "commit",
"print only branches that contain the commit",
PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
- opt_parse_with_commit, (intptr_t) "HEAD",
+ parse_opt_with_commit, (intptr_t) "HEAD",
},
OPT__ABBREV(&abbrev),
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
- pathspec_match(pathspec, ps_matched, ce->name, 0);
+ match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, ps_matched);
}
if (report_path_error(ps_matched, pathspec, 0))
/* Any unmerged paths? */
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
- if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+ if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
if (!ce_stage(ce))
continue;
if (opts->force) {
state.refresh_cache = 1;
for (pos = 0; pos < active_nr; pos++) {
struct cache_entry *ce = active_cache[pos];
- if (pathspec_match(pathspec, NULL, ce->name, 0)) {
+ if (match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL)) {
if (!ce_stage(ce)) {
errs |= checkout_entry(ce, &state, NULL);
continue;
static void setup_branch_path(struct branch_info *branch)
{
struct strbuf buf = STRBUF_INIT;
- strbuf_addstr(&buf, "refs/heads/");
- strbuf_addstr(&buf, branch->name);
+ int ret;
+
+ if ((ret = interpret_nth_last_branch(branch->name, &buf))
+ && ret == strlen(branch->name)) {
+ branch->name = xstrdup(buf.buf);
+ strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
+ } else {
+ strbuf_addstr(&buf, "refs/heads/");
+ strbuf_addstr(&buf, branch->name);
+ }
branch->path = strbuf_detach(&buf, NULL);
}
arg = argv[0];
has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+ if (!strcmp(arg, "-"))
+ arg = "@{-1}";
+
if (get_sha1(arg, rev)) {
if (has_dash_dash) /* case (1) */
die("invalid reference: %s", arg);
#include "strbuf.h"
#include "dir.h"
#include "pack-refs.h"
+#include "sigchain.h"
/*
* Overall FIXMEs:
static void remove_junk_on_signal(int signo)
{
remove_junk();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
}
junk_git_dir = git_dir;
atexit(remove_junk);
- signal(SIGINT, remove_junk_on_signal);
+ sigchain_push_common(remove_junk_on_signal);
setenv(CONFIG_ENVIRONMENT, xstrdup(mkpath("%s/config", git_dir)), 1);
option_upload_pack);
refs = transport_get_remote_refs(transport);
- transport_fetch_refs(transport, refs);
+ if(refs)
+ transport_fetch_refs(transport, refs);
}
- clear_extra_refs();
+ if (refs) {
+ clear_extra_refs();
- mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
+ mapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);
- head_points_at = locate_head(refs, mapped_refs, &remote_head);
+ head_points_at = locate_head(refs, mapped_refs, &remote_head);
+ }
+ else {
+ warning("You appear to have cloned an empty repository.");
+ head_points_at = NULL;
+ remote_head = NULL;
+ option_no_checkout = 1;
+ }
if (head_points_at) {
/* Local default branch link */
struct cache_entry *ce = active_cache[i];
if (ce->ce_flags & CE_UPDATE)
continue;
- if (!pathspec_match(pattern, m, ce->name, 0))
+ if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
continue;
string_list_insert(ce->name, list);
}
#include "cache.h"
#include "refs.h"
#include "commit.h"
+#include "sigchain.h"
static char *get_stdin(void)
{
static void remove_keep_on_signal(int signo)
{
remove_keep();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
char buffer[1024];
int err = 0;
- signal(SIGINT, remove_keep_on_signal);
+ sigchain_push_common(remove_keep_on_signal);
atexit(remove_keep);
while (fgets(buffer, sizeof(buffer), stdin)) {
#include "transport.h"
#include "run-command.h"
#include "parse-options.h"
+#include "sigchain.h"
static const char * const builtin_fetch_usage[] = {
"git fetch [options] [<repository> <refspec>...]",
static void unlock_pack_on_signal(int signo)
{
unlock_pack();
- signal(SIGINT, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
ref_nr = j;
}
- signal(SIGINT, unlock_pack_on_signal);
+ sigchain_push_common(unlock_pack_on_signal);
atexit(unlock_pack);
exit_code = do_fetch(transport,
parse_fetch_refspec(ref_nr, refs), ref_nr);
push_arg("-E");
if (opt->regflags & REG_ICASE)
push_arg("-i");
+ if (opt->binary == GREP_BINARY_NOMATCH)
+ push_arg("-I");
if (opt->word_regexp)
push_arg("-w");
if (opt->name_only)
* old_path, the ':' at the end will let 'man' to try
* system-wide paths after ours to find the manual page. If
* there is old_path, we need ':' as delimiter. */
- strbuf_addstr(&new_path, GIT_MAN_PATH);
+ strbuf_addstr(&new_path, system_path(GIT_MAN_PATH));
strbuf_addch(&new_path, ':');
if (old_path)
strbuf_addstr(&new_path, old_path);
static void show_info_page(const char *git_cmd)
{
const char *page = cmd_to_page(git_cmd);
- setenv("INFOPATH", GIT_INFO_PATH, 1);
+ setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
execlp("info", "info", "gitman", page, NULL);
}
static const char *tag_killed = "";
static const char *tag_modified = "";
-
-/*
- * Match a pathspec against a filename. The first "skiplen" characters
- * are the common prefix
- */
-int pathspec_match(const char **spec, char *ps_matched,
- const char *filename, int skiplen)
-{
- const char *m;
-
- while ((m = *spec++) != NULL) {
- int matchlen = strlen(m + skiplen);
-
- if (!matchlen)
- goto matched;
- if (!strncmp(m + skiplen, filename + skiplen, matchlen)) {
- if (m[skiplen + matchlen - 1] == '/')
- goto matched;
- switch (filename[skiplen + matchlen]) {
- case '/': case '\0':
- goto matched;
- }
- }
- if (!fnmatch(m + skiplen, filename + skiplen, 0))
- goto matched;
- if (ps_matched)
- ps_matched++;
- continue;
- matched:
- if (ps_matched)
- *ps_matched = 1;
- return 1;
- }
- return 0;
-}
-
static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = prefix_len;
if (len >= ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
- if (pathspec && !pathspec_match(pathspec, ps_matched, ent->name, len))
+ if (!match_pathspec(pathspec, ent->name, ent->len, len, ps_matched))
return;
fputs(tag, stdout);
if (len >= ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
- if (pathspec && !pathspec_match(pathspec, ps_matched, ce->name, len))
+ if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), len, ps_matched))
return;
if (tag && *tag && show_valid_bit &&
#define MAX_HDR_PARSED 10
#define MAX_BOUNDARIES 5
+static void cleanup_space(struct strbuf *sb);
+
+
static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
{
struct strbuf *src = name;
strbuf_add(&email, at, el);
strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0));
- /* The remainder is name. It could be "John Doe <john.doe@xz>"
- * or "john.doe@xz (John Doe)", but we have removed the
- * email part, so trim from both ends, possibly removing
- * the () pair at the end.
+ /* The remainder is name. It could be
+ *
+ * - "John Doe <john.doe@xz>" (a), or
+ * - "john.doe@xz (John Doe)" (b), or
+ * - "John (zzz) Doe <john.doe@xz> (Comment)" (c)
+ *
+ * but we have removed the email part, so
+ *
+ * - remove extra spaces which could stay after email (case 'c'), and
+ * - trim from both ends, possibly removing the () pair at the end
+ * (cases 'a' and 'b').
*/
+ cleanup_space(&f);
strbuf_trim(&f);
if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') {
strbuf_remove(&f, 0, 1);
c -= 'a' - 26;
else if ('0' <= c && c <= '9')
c -= '0' - 52;
- else if (c == '=') {
- /* padding is almost like (c == 0), except we do
- * not output NUL resulting only from it;
- * for now we just trust the data.
- */
- c = 0;
- }
else
continue; /* garbage */
switch (pos++) {
rfc2047 = 1;
if (in != ep) {
- strbuf_add(&outbuf, in, ep - in);
+ /*
+ * We are about to process an encoded-word
+ * that begins at ep, but there is something
+ * before the encoded word.
+ */
+ char *scan;
+ for (scan = in; scan < ep; scan++)
+ if (!isspace(*scan))
+ break;
+
+ if (scan != ep || in == it->buf) {
+ /*
+ * We should not lose that "something",
+ * unless we have just processed an
+ * encoded-word, and there is only LWS
+ * before the one we are about to process.
+ */
+ strbuf_add(&outbuf, in, ep - in);
+ }
in = ep;
}
/* E.g.
}
output_header_lines(fout, "Subject", hdr);
} else if (!memcmp(header[i], "From", 4)) {
+ cleanup_space(hdr);
handle_from(hdr);
fprintf(fout, "Author: %s\n", name.buf);
fprintf(fout, "Email: %s\n", email.buf);
/* .receivepack = */ "git-receive-pack",
};
+static int feed_object(const unsigned char *sha1, int fd, int negative)
+{
+ char buf[42];
+
+ if (negative && !has_sha1_file(sha1))
+ return 1;
+
+ memcpy(buf + negative, sha1_to_hex(sha1), 40);
+ if (negative)
+ buf[0] = '^';
+ buf[40 + negative] = '\n';
+ return write_or_whine(fd, buf, 41 + negative, "send-pack: send refs");
+}
+
/*
* Make a pack stream and spit it out into file descriptor fd
*/
};
struct child_process po;
int i;
- char buf[42];
if (args.use_thin_pack)
argv[4] = "--thin";
* We feed the pack-objects we just spawned with revision
* parameters by writing to the pipe.
*/
- for (i = 0; i < extra->nr; i++) {
- memcpy(buf + 1, sha1_to_hex(&extra->array[i][0]), 40);
- buf[0] = '^';
- buf[41] = '\n';
- if (!write_or_whine(po.in, buf, 42, "send-pack: send refs"))
+ for (i = 0; i < extra->nr; i++)
+ if (!feed_object(extra->array[i], po.in, 1))
break;
- }
while (refs) {
if (!is_null_sha1(refs->old_sha1) &&
- has_sha1_file(refs->old_sha1)) {
- memcpy(buf + 1, sha1_to_hex(refs->old_sha1), 40);
- buf[0] = '^';
- buf[41] = '\n';
- if (!write_or_whine(po.in, buf, 42,
- "send-pack: send refs"))
- break;
- }
- if (!is_null_sha1(refs->new_sha1)) {
- memcpy(buf, sha1_to_hex(refs->new_sha1), 40);
- buf[40] = '\n';
- if (!write_or_whine(po.in, buf, 41,
- "send-pack: send refs"))
- break;
- }
+ !feed_object(refs->old_sha1, po.in, 1))
+ break;
+ if (!is_null_sha1(refs->new_sha1) &&
+ !feed_object(refs->new_sha1, po.in, 0))
+ break;
refs = refs->next;
}
struct tag_filter {
const char *pattern;
int lines;
+ struct commit_list *with_commit;
};
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
char *buf, *sp, *eol;
size_t len;
+ if (filter->with_commit) {
+ struct commit *commit;
+
+ commit = lookup_commit_reference_gently(sha1, 1);
+ if (!commit)
+ return 0;
+ if (!is_descendant_of(commit, filter->with_commit))
+ return 0;
+ }
+
if (!filter->lines) {
printf("%s\n", refname);
return 0;
return 0;
}
-static int list_tags(const char *pattern, int lines)
+static int list_tags(const char *pattern, int lines,
+ struct commit_list *with_commit)
{
struct tag_filter filter;
filter.pattern = pattern;
filter.lines = lines;
+ filter.with_commit = with_commit;
for_each_tag_ref(show_reference, (void *) &filter);
list = 0, delete = 0, verify = 0;
const char *msgfile = NULL, *keyid = NULL;
struct msg_arg msg = { 0, STRBUF_INIT };
+ struct commit_list *with_commit = NULL;
struct option options[] = {
OPT_BOOLEAN('l', NULL, &list, "list tag names"),
{ OPTION_INTEGER, 'n', NULL, &lines, NULL,
OPT_STRING('u', NULL, &keyid, "key-id",
"use another key to sign the tag"),
OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+
+ OPT_GROUP("Tag listing options"),
+ {
+ OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
+ "print only tags that contain the commit",
+ PARSE_OPT_LASTARG_DEFAULT,
+ parse_opt_with_commit, (intptr_t)"HEAD",
+ },
OPT_END()
};
if (list + delete + verify > 1)
usage_with_options(git_tag_usage, options);
if (list)
- return list_tags(argv[0], lines == -1 ? 0 : lines);
+ return list_tags(argv[0], lines == -1 ? 0 : lines,
+ with_commit);
if (lines != -1)
die("-n option is only allowed with -l.");
+ if (with_commit)
+ die("--contains option is only allowed with -l.");
if (delete)
return for_each_tag_name(argv, delete_tag);
if (verify)
return error("unrecognized argument: %s'", argv[i]);
}
+ object_array_remove_duplicates(&revs.pending);
+
for (i = 0; i < revs.pending.nr; i++) {
struct object_array_entry *e = revs.pending.objects + i;
unsigned char sha1[20];
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
+extern int interpret_nth_last_branch(const char *str, struct strbuf *);
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
extern const char *ref_rev_parse_rules[];
extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
extern int has_symlink_leading_path(int len, const char *name);
+extern int has_symlink_or_noent_leading_path(int len, const char *name);
+extern int has_dirs_only_path(int len, const char *name, int prefix_len);
+extern void invalidate_lstat_cache(int len, const char *name);
+extern void clear_lstat_cache(void);
extern struct alternate_object_database {
struct alternate_object_database *next;
extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
/* ls-files */
-int pathspec_match(const char **spec, char *matched, const char *filename, int skiplen);
int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
void overlay_tree_on_cache(const char *tree_name, const char *prefix);
va_end(args);
return r;
}
+
+/*
+ * This function splits the buffer by newlines and colors the lines individually.
+ *
+ * Returns 0 on success.
+ */
+int color_fwrite_lines(FILE *fp, const char *color,
+ size_t count, const char *buf)
+{
+ if (!*color)
+ return fwrite(buf, count, 1, fp) != 1;
+ while (count) {
+ char *p = memchr(buf, '\n', count);
+ if (p != buf && (fputs(color, fp) < 0 ||
+ fwrite(buf, p ? p - buf : count, 1, fp) != 1 ||
+ fputs(COLOR_RESET, fp) < 0))
+ return -1;
+ if (!p)
+ return 0;
+ if (fputc('\n', fp) < 0)
+ return -1;
+ count -= p + 1 - buf;
+ buf = p + 1;
+ }
+ return 0;
+}
+
+
void color_parse_mem(const char *value, int len, const char *var, char *dst);
int color_fprintf(FILE *fp, const char *color, const char *fmt, ...);
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...);
+int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);
#endif /* COLOR_H */
return get_merge_bases_many(one, 1, &two, cleanup);
}
+int is_descendant_of(struct commit *commit, struct commit_list *with_commit)
+{
+ if (!with_commit)
+ return 1;
+ while (with_commit) {
+ struct commit *other;
+
+ other = with_commit->item;
+ with_commit = with_commit->next;
+ if (in_merge_bases(other, &commit, 1))
+ return 1;
+ }
+ return 0;
+}
+
int in_merge_bases(struct commit *commit, struct commit **reference, int num)
{
struct commit_list *bases, *b;
extern struct commit_list *get_shallow_commits(struct object_array *heads,
int depth, int shallow_flag, int not_shallow_flag);
+int is_descendant_of(struct commit *, struct commit_list *);
int in_merge_bases(struct commit *, struct commit **, int);
extern int interactive_add(int argc, const char **argv, const char *prefix);
#define WEXITSTATUS(x) ((x) & 0xff)
#define WIFSIGNALED(x) ((unsigned)(x) > 259)
-#define SIGKILL 0
-#define SIGCHLD 0
-#define SIGPIPE 0
-#define SIGHUP 0
-#define SIGQUIT 0
-#define SIGALRM 100
+#define SIGHUP 1
+#define SIGQUIT 3
+#define SIGKILL 9
+#define SIGPIPE 13
+#define SIGALRM 14
+#define SIGCHLD 17
#define F_GETFD 1
#define F_SETFD 2
# are currently in a git repository. The %s token will be
# the name of the current branch.
#
+# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
+# value, unstaged (*) and staged (+) changes will be shown next
+# to the branch name. You can configure this per-repository
+# with the bash.showDirtyState variable, which defaults to true
+# once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#
# To submit patches:
#
# *) Read Documentation/SubmittingPatches
fi
fi
+ local w
+ local i
+
+ if test -n "$GIT_PS1_SHOWDIRTYSTATE"; then
+ if test "$(git config --bool bash.showDirtyState)" != "false"; then
+ git diff --no-ext-diff --ignore-submodules \
+ --quiet --exit-code || w="*"
+ if git rev-parse --quiet --verify HEAD >/dev/null; then
+ git diff-index --cached --quiet \
+ --ignore-submodules HEAD -- || i="+"
+ else
+ i="#"
+ fi
+ fi
+ fi
+
if [ -n "${1-}" ]; then
- printf "$1" "${b##refs/heads/}$r"
+ printf "$1" "${b##refs/heads/}$w$i$r"
else
- printf " (%s)" "${b##refs/heads/}$r"
+ printf " (%s)" "${b##refs/heads/}$w$i$r"
fi
fi
}
#!/bin/sh
# git-difftool-helper is a GIT_EXTERNAL_DIFF-compatible diff tool launcher.
-# It supports kdiff3, tkdiff, xxdiff, meld, opendiff, emerge, ecmerge,
-# vimdiff, gvimdiff, and custom user-configurable tools.
+# It supports kdiff3, kompare, tkdiff, xxdiff, meld, opendiff,
+# emerge, ecmerge, vimdiff, gvimdiff, and custom user-configurable tools.
# This script is typically launched by using the 'git difftool'
# convenience command.
#
> /dev/null 2>&1
;;
+ kompare)
+ "$merge_tool_path" "$LOCAL" "$REMOTE"
+ ;;
+
tkdiff)
"$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"
;;
# Built-in merge tools are always valid.
valid_tool() {
case "$1" in
- kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
+ kdiff3 | kompare | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | gvimdiff | ecmerge)
;; # happy
*)
if ! valid_custom_tool "$1"
# Try to guess an appropriate merge tool if no tool has been set.
if test -z "$merge_tool"; then
-
# We have a $DISPLAY so try some common UNIX merge tools
if test -n "$DISPLAY"; then
- merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
- # If gnome then prefer meld
- if test -n "$GNOME_DESKTOP_SESSION_ID"; then
- merge_tool_candidates="meld $merge_tool_candidates"
- fi
- # If KDE then prefer kdiff3
- if test "$KDE_FULL_SESSION" = "true"; then
- merge_tool_candidates="kdiff3 $merge_tool_candidates"
+ # If gnome then prefer meld, otherwise, prefer kdiff3 or kompare
+ if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
+ merge_tool_candidates="meld kdiff3 kompare tkdiff xxdiff gvimdiff"
+ else
+ merge_tool_candidates="kdiff3 kompare tkdiff xxdiff meld gvimdiff"
fi
fi
-
- # $EDITOR is emacs so add emerge as a candidate
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
- merge_tool_candidates="$merge_tool_candidates emerge"
+ # $EDITOR is emacs so add emerge as a candidate
+ merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
+ elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
+ # $EDITOR is vim so add vimdiff as a candidate
+ merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
+ else
+ merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
fi
-
- # $EDITOR is vim so add vimdiff as a candidate
- if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
- merge_tool_candidates="$merge_tool_candidates vimdiff"
- fi
-
- merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
echo "merge tool candidates: $merge_tool_candidates"
# Loop over each candidate and stop when a valid merge tool is found.
--tool=<tool>::
Use the merge resolution program specified by <tool>.
Valid merge tools are:
- kdiff3, tkdiff, meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, and opendiff
+ kdiff3, kompare, tkdiff, meld, xxdiff, emerge,
+ vimdiff, gvimdiff, ecmerge, and opendiff
+
If a merge resolution program is not specified, 'git-difftool'
will use the configuration variable `merge.tool`. If the
gid_t gid = 0;
int i;
+ git_extract_argv0_path(argv[0]);
+
for (i = 1; i < argc; i++) {
char *arg = argv[i];
*mode = 0;
else if (!strcmp(path, "-"))
*mode = create_ce_mode(0666);
- else if (stat(path, &st))
+ else if (lstat(path, &st))
return error("Could not access '%s'", path);
else
*mode = st.st_mode;
#include "run-command.h"
#include "utf8.h"
#include "userdiff.h"
+#include "sigchain.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
static int diff_rename_limit_default = 200;
static int diff_suppress_blank_empty;
int diff_use_color_default = -1;
+static const char *diff_word_regex_cfg;
static const char *external_diff_cmd_cfg;
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
}
if (!strcmp(var, "diff.external"))
return git_config_string(&external_diff_cmd_cfg, var, value);
+ if (!strcmp(var, "diff.wordregex"))
+ return git_config_string(&diff_word_regex_cfg, var, value);
return git_diff_basic_config(var, value, cb);
}
char tmp_path[PATH_MAX];
} diff_temp[2];
+static struct diff_tempfile *claim_diff_tempfile(void) {
+ int i;
+ for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
+ if (!diff_temp[i].name)
+ return diff_temp + i;
+ die("BUG: diff is failing to clean up its tempfiles");
+}
+
+static int remove_tempfile_installed;
+
+static void remove_tempfile(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
+ if (diff_temp[i].name == diff_temp[i].tmp_path) {
+ unlink(diff_temp[i].name);
+ diff_temp[i].name = NULL;
+ }
+}
+
+static void remove_tempfile_on_signal(int signo)
+{
+ remove_tempfile();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
static int count_lines(const char *data, int size)
{
int count, ch, completely_empty = 1, nl_just_seen = 0;
struct diff_words_buffer {
mmfile_t text;
long alloc;
- long current; /* output pointer */
- int suppressed_newline;
+ struct diff_words_orig {
+ const char *begin, *end;
+ } *orig;
+ int orig_nr, orig_alloc;
};
static void diff_words_append(char *line, unsigned long len,
struct diff_words_buffer *buffer)
{
- if (buffer->text.size + len > buffer->alloc) {
- buffer->alloc = (buffer->text.size + len) * 3 / 2;
- buffer->text.ptr = xrealloc(buffer->text.ptr, buffer->alloc);
- }
+ ALLOC_GROW(buffer->text.ptr, buffer->text.size + len, buffer->alloc);
line++;
len--;
memcpy(buffer->text.ptr + buffer->text.size, line, len);
buffer->text.size += len;
+ buffer->text.ptr[buffer->text.size] = '\0';
}
struct diff_words_data {
struct diff_words_buffer minus, plus;
+ const char *current_plus;
FILE *file;
+ regex_t *word_regex;
};
-static void print_word(FILE *file, struct diff_words_buffer *buffer, int len, int color,
- int suppress_newline)
+static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
{
- const char *ptr;
- int eol = 0;
+ struct diff_words_data *diff_words = priv;
+ int minus_first, minus_len, plus_first, plus_len;
+ const char *minus_begin, *minus_end, *plus_begin, *plus_end;
- if (len == 0)
+ if (line[0] != '@' || parse_hunk_header(line, len,
+ &minus_first, &minus_len, &plus_first, &plus_len))
return;
- ptr = buffer->text.ptr + buffer->current;
- buffer->current += len;
+ /* POSIX requires that first be decremented by one if len == 0... */
+ if (minus_len) {
+ minus_begin = diff_words->minus.orig[minus_first].begin;
+ minus_end =
+ diff_words->minus.orig[minus_first + minus_len - 1].end;
+ } else
+ minus_begin = minus_end =
+ diff_words->minus.orig[minus_first].end;
- if (ptr[len - 1] == '\n') {
- eol = 1;
- len--;
+ if (plus_len) {
+ plus_begin = diff_words->plus.orig[plus_first].begin;
+ plus_end = diff_words->plus.orig[plus_first + plus_len - 1].end;
+ } else
+ plus_begin = plus_end = diff_words->plus.orig[plus_first].end;
+
+ if (diff_words->current_plus != plus_begin)
+ fwrite(diff_words->current_plus,
+ plus_begin - diff_words->current_plus, 1,
+ diff_words->file);
+ if (minus_begin != minus_end)
+ color_fwrite_lines(diff_words->file,
+ diff_get_color(1, DIFF_FILE_OLD),
+ minus_end - minus_begin, minus_begin);
+ if (plus_begin != plus_end)
+ color_fwrite_lines(diff_words->file,
+ diff_get_color(1, DIFF_FILE_NEW),
+ plus_end - plus_begin, plus_begin);
+
+ diff_words->current_plus = plus_end;
+}
+
+/* This function starts looking at *begin, and returns 0 iff a word was found. */
+static int find_word_boundaries(mmfile_t *buffer, regex_t *word_regex,
+ int *begin, int *end)
+{
+ if (word_regex && *begin < buffer->size) {
+ regmatch_t match[1];
+ if (!regexec(word_regex, buffer->ptr + *begin, 1, match, 0)) {
+ char *p = memchr(buffer->ptr + *begin + match[0].rm_so,
+ '\n', match[0].rm_eo - match[0].rm_so);
+ *end = p ? p - buffer->ptr : match[0].rm_eo + *begin;
+ *begin += match[0].rm_so;
+ return *begin >= *end;
+ }
+ return -1;
}
- fputs(diff_get_color(1, color), file);
- fwrite(ptr, len, 1, file);
- fputs(diff_get_color(1, DIFF_RESET), file);
+ /* find the next word */
+ while (*begin < buffer->size && isspace(buffer->ptr[*begin]))
+ (*begin)++;
+ if (*begin >= buffer->size)
+ return -1;
- if (eol) {
- if (suppress_newline)
- buffer->suppressed_newline = 1;
- else
- putc('\n', file);
- }
+ /* find the end of the word */
+ *end = *begin + 1;
+ while (*end < buffer->size && !isspace(buffer->ptr[*end]))
+ (*end)++;
+
+ return 0;
}
-static void fn_out_diff_words_aux(void *priv, char *line, unsigned long len)
+/*
+ * This function splits the words in buffer->text, stores the list with
+ * newline separator into out, and saves the offsets of the original words
+ * in buffer->orig.
+ */
+static void diff_words_fill(struct diff_words_buffer *buffer, mmfile_t *out,
+ regex_t *word_regex)
{
- struct diff_words_data *diff_words = priv;
+ int i, j;
+ long alloc = 0;
- if (diff_words->minus.suppressed_newline) {
- if (line[0] != '+')
- putc('\n', diff_words->file);
- diff_words->minus.suppressed_newline = 0;
- }
+ out->size = 0;
+ out->ptr = NULL;
- len--;
- switch (line[0]) {
- case '-':
- print_word(diff_words->file,
- &diff_words->minus, len, DIFF_FILE_OLD, 1);
- break;
- case '+':
- print_word(diff_words->file,
- &diff_words->plus, len, DIFF_FILE_NEW, 0);
- break;
- case ' ':
- print_word(diff_words->file,
- &diff_words->plus, len, DIFF_PLAIN, 0);
- diff_words->minus.current += len;
- break;
+ /* fake an empty "0th" word */
+ ALLOC_GROW(buffer->orig, 1, buffer->orig_alloc);
+ buffer->orig[0].begin = buffer->orig[0].end = buffer->text.ptr;
+ buffer->orig_nr = 1;
+
+ for (i = 0; i < buffer->text.size; i++) {
+ if (find_word_boundaries(&buffer->text, word_regex, &i, &j))
+ return;
+
+ /* store original boundaries */
+ ALLOC_GROW(buffer->orig, buffer->orig_nr + 1,
+ buffer->orig_alloc);
+ buffer->orig[buffer->orig_nr].begin = buffer->text.ptr + i;
+ buffer->orig[buffer->orig_nr].end = buffer->text.ptr + j;
+ buffer->orig_nr++;
+
+ /* store one word */
+ ALLOC_GROW(out->ptr, out->size + j - i + 1, alloc);
+ memcpy(out->ptr + out->size, buffer->text.ptr + i, j - i);
+ out->ptr[out->size + j - i] = '\n';
+ out->size += j - i + 1;
+
+ i = j - 1;
}
}
xdemitconf_t xecfg;
xdemitcb_t ecb;
mmfile_t minus, plus;
- int i;
+
+ /* special case: only removal */
+ if (!diff_words->plus.text.size) {
+ color_fwrite_lines(diff_words->file,
+ diff_get_color(1, DIFF_FILE_OLD),
+ diff_words->minus.text.size, diff_words->minus.text.ptr);
+ diff_words->minus.text.size = 0;
+ return;
+ }
+
+ diff_words->current_plus = diff_words->plus.text.ptr;
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
- minus.size = diff_words->minus.text.size;
- minus.ptr = xmalloc(minus.size);
- memcpy(minus.ptr, diff_words->minus.text.ptr, minus.size);
- for (i = 0; i < minus.size; i++)
- if (isspace(minus.ptr[i]))
- minus.ptr[i] = '\n';
- diff_words->minus.current = 0;
-
- plus.size = diff_words->plus.text.size;
- plus.ptr = xmalloc(plus.size);
- memcpy(plus.ptr, diff_words->plus.text.ptr, plus.size);
- for (i = 0; i < plus.size; i++)
- if (isspace(plus.ptr[i]))
- plus.ptr[i] = '\n';
- diff_words->plus.current = 0;
-
+ diff_words_fill(&diff_words->minus, &minus, diff_words->word_regex);
+ diff_words_fill(&diff_words->plus, &plus, diff_words->word_regex);
xpp.flags = XDF_NEED_MINIMAL;
- xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
+ /* as only the hunk header will be parsed, we need a 0-context */
+ xecfg.ctxlen = 0;
xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
&xpp, &xecfg, &ecb);
free(minus.ptr);
free(plus.ptr);
+ if (diff_words->current_plus != diff_words->plus.text.ptr +
+ diff_words->plus.text.size)
+ fwrite(diff_words->current_plus,
+ diff_words->plus.text.ptr + diff_words->plus.text.size
+ - diff_words->current_plus, 1,
+ diff_words->file);
diff_words->minus.text.size = diff_words->plus.text.size = 0;
-
- if (diff_words->minus.suppressed_newline) {
- putc('\n', diff_words->file);
- diff_words->minus.suppressed_newline = 0;
- }
}
typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
diff_words_show(ecbdata->diff_words);
free (ecbdata->diff_words->minus.text.ptr);
+ free (ecbdata->diff_words->minus.orig);
free (ecbdata->diff_words->plus.text.ptr);
+ free (ecbdata->diff_words->plus.orig);
+ free(ecbdata->diff_words->word_regex);
free(ecbdata->diff_words);
ecbdata->diff_words = NULL;
}
return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
}
+static const char *userdiff_word_regex(struct diff_filespec *one)
+{
+ diff_filespec_load_driver(one);
+ return one->driver->word_regex;
+}
+
void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
{
if (!options->a_prefix)
ecbdata.diff_words =
xcalloc(1, sizeof(struct diff_words_data));
ecbdata.diff_words->file = o->file;
+ if (!o->word_regex)
+ o->word_regex = userdiff_word_regex(one);
+ if (!o->word_regex)
+ o->word_regex = userdiff_word_regex(two);
+ if (!o->word_regex)
+ o->word_regex = diff_word_regex_cfg;
+ if (o->word_regex) {
+ ecbdata.diff_words->word_regex = (regex_t *)
+ xmalloc(sizeof(regex_t));
+ if (regcomp(ecbdata.diff_words->word_regex,
+ o->word_regex,
+ REG_EXTENDED | REG_NEWLINE))
+ die ("Invalid regular expression: %s",
+ o->word_regex);
+ }
}
xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
&xpp, &xecfg, &ecb);
sprintf(temp->mode, "%06o", mode);
}
-static void prepare_temp_file(const char *name,
- struct diff_tempfile *temp,
- struct diff_filespec *one)
+static struct diff_tempfile *prepare_temp_file(const char *name,
+ struct diff_filespec *one)
{
+ struct diff_tempfile *temp = claim_diff_tempfile();
+
if (!DIFF_FILE_VALID(one)) {
not_a_valid_file:
/* A '-' entry produces this for file-2, and
temp->name = "/dev/null";
strcpy(temp->hex, ".");
strcpy(temp->mode, ".");
- return;
+ return temp;
+ }
+
+ if (!remove_tempfile_installed) {
+ atexit(remove_tempfile);
+ sigchain_push_common(remove_tempfile_on_signal);
+ remove_tempfile_installed = 1;
}
if (!one->sha1_valid ||
*/
sprintf(temp->mode, "%06o", one->mode);
}
- return;
+ return temp;
}
else {
if (diff_populate_filespec(one, 0))
prep_temp_blob(temp, one->data, one->size,
one->sha1, one->mode);
}
-}
-
-static void remove_tempfile(void)
-{
- int i;
-
- for (i = 0; i < 2; i++)
- if (diff_temp[i].name == diff_temp[i].tmp_path) {
- unlink(diff_temp[i].name);
- diff_temp[i].name = NULL;
- }
-}
-
-static void remove_tempfile_on_signal(int signo)
-{
- remove_tempfile();
- signal(SIGINT, SIG_DFL);
- raise(signo);
+ return temp;
}
/* An external diff command takes:
int complete_rewrite)
{
const char *spawn_arg[10];
- struct diff_tempfile *temp = diff_temp;
int retval;
- static int atexit_asked = 0;
- const char *othername;
const char **arg = &spawn_arg[0];
- othername = (other? other : name);
- if (one && two) {
- prepare_temp_file(name, &temp[0], one);
- prepare_temp_file(othername, &temp[1], two);
- if (! atexit_asked &&
- (temp[0].name == temp[0].tmp_path ||
- temp[1].name == temp[1].tmp_path)) {
- atexit_asked = 1;
- atexit(remove_tempfile);
- }
- signal(SIGINT, remove_tempfile_on_signal);
- }
-
if (one && two) {
+ struct diff_tempfile *temp_one, *temp_two;
+ const char *othername = (other ? other : name);
+ temp_one = prepare_temp_file(name, one);
+ temp_two = prepare_temp_file(othername, two);
*arg++ = pgm;
*arg++ = name;
- *arg++ = temp[0].name;
- *arg++ = temp[0].hex;
- *arg++ = temp[0].mode;
- *arg++ = temp[1].name;
- *arg++ = temp[1].hex;
- *arg++ = temp[1].mode;
+ *arg++ = temp_one->name;
+ *arg++ = temp_one->hex;
+ *arg++ = temp_one->mode;
+ *arg++ = temp_two->name;
+ *arg++ = temp_two->hex;
+ *arg++ = temp_two->mode;
if (other) {
*arg++ = other;
*arg++ = xfrm_msg;
}
}
+static int similarity_index(struct diff_filepair *p)
+{
+ return p->score * 100 / MAX_SCORE;
+}
+
+static void fill_metainfo(struct strbuf *msg,
+ const char *name,
+ const char *other,
+ struct diff_filespec *one,
+ struct diff_filespec *two,
+ struct diff_options *o,
+ struct diff_filepair *p)
+{
+ strbuf_init(msg, PATH_MAX * 2 + 300);
+ switch (p->status) {
+ case DIFF_STATUS_COPIED:
+ strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
+ strbuf_addstr(msg, "\ncopy from ");
+ quote_c_style(name, msg, NULL, 0);
+ strbuf_addstr(msg, "\ncopy to ");
+ quote_c_style(other, msg, NULL, 0);
+ strbuf_addch(msg, '\n');
+ break;
+ case DIFF_STATUS_RENAMED:
+ strbuf_addf(msg, "similarity index %d%%", similarity_index(p));
+ strbuf_addstr(msg, "\nrename from ");
+ quote_c_style(name, msg, NULL, 0);
+ strbuf_addstr(msg, "\nrename to ");
+ quote_c_style(other, msg, NULL, 0);
+ strbuf_addch(msg, '\n');
+ break;
+ case DIFF_STATUS_MODIFIED:
+ if (p->score) {
+ strbuf_addf(msg, "dissimilarity index %d%%\n",
+ similarity_index(p));
+ break;
+ }
+ /* fallthru */
+ default:
+ /* nothing */
+ ;
+ }
+ if (one && two && hashcmp(one->sha1, two->sha1)) {
+ int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+
+ if (DIFF_OPT_TST(o, BINARY)) {
+ mmfile_t mf;
+ if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
+ (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
+ abbrev = 40;
+ }
+ strbuf_addf(msg, "index %.*s..%.*s",
+ abbrev, sha1_to_hex(one->sha1),
+ abbrev, sha1_to_hex(two->sha1));
+ if (one->mode == two->mode)
+ strbuf_addf(msg, " %06o", one->mode);
+ strbuf_addch(msg, '\n');
+ }
+ if (msg->len)
+ strbuf_setlen(msg, msg->len - 1);
+}
+
static void run_diff_cmd(const char *pgm,
const char *name,
const char *other,
const char *attr_path,
struct diff_filespec *one,
struct diff_filespec *two,
- const char *xfrm_msg,
+ struct strbuf *msg,
struct diff_options *o,
- int complete_rewrite)
+ struct diff_filepair *p)
{
+ const char *xfrm_msg = NULL;
+ int complete_rewrite = (p->status == DIFF_STATUS_MODIFIED) && p->score;
+
+ if (msg) {
+ fill_metainfo(msg, name, other, one, two, o, p);
+ xfrm_msg = msg->len ? msg->buf : NULL;
+ }
+
if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
pgm = NULL;
else {
hashclr(one->sha1);
}
-static int similarity_index(struct diff_filepair *p)
-{
- return p->score * 100 / MAX_SCORE;
-}
-
static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
{
/* Strip the prefix but do not molest /dev/null and absolute paths */
{
const char *pgm = external_diff();
struct strbuf msg;
- char *xfrm_msg;
struct diff_filespec *one = p->one;
struct diff_filespec *two = p->two;
const char *name;
const char *other;
const char *attr_path;
- int complete_rewrite = 0;
name = p->one->path;
other = (strcmp(name, p->two->path) ? p->two->path : NULL);
if (DIFF_PAIR_UNMERGED(p)) {
run_diff_cmd(pgm, name, NULL, attr_path,
- NULL, NULL, NULL, o, 0);
+ NULL, NULL, NULL, o, p);
return;
}
diff_fill_sha1_info(one);
diff_fill_sha1_info(two);
- strbuf_init(&msg, PATH_MAX * 2 + 300);
- switch (p->status) {
- case DIFF_STATUS_COPIED:
- strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
- strbuf_addstr(&msg, "\ncopy from ");
- quote_c_style(name, &msg, NULL, 0);
- strbuf_addstr(&msg, "\ncopy to ");
- quote_c_style(other, &msg, NULL, 0);
- strbuf_addch(&msg, '\n');
- break;
- case DIFF_STATUS_RENAMED:
- strbuf_addf(&msg, "similarity index %d%%", similarity_index(p));
- strbuf_addstr(&msg, "\nrename from ");
- quote_c_style(name, &msg, NULL, 0);
- strbuf_addstr(&msg, "\nrename to ");
- quote_c_style(other, &msg, NULL, 0);
- strbuf_addch(&msg, '\n');
- break;
- case DIFF_STATUS_MODIFIED:
- if (p->score) {
- strbuf_addf(&msg, "dissimilarity index %d%%\n",
- similarity_index(p));
- complete_rewrite = 1;
- break;
- }
- /* fallthru */
- default:
- /* nothing */
- ;
- }
-
- if (hashcmp(one->sha1, two->sha1)) {
- int abbrev = DIFF_OPT_TST(o, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
-
- if (DIFF_OPT_TST(o, BINARY)) {
- mmfile_t mf;
- if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
- (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
- abbrev = 40;
- }
- strbuf_addf(&msg, "index %.*s..%.*s",
- abbrev, sha1_to_hex(one->sha1),
- abbrev, sha1_to_hex(two->sha1));
- if (one->mode == two->mode)
- strbuf_addf(&msg, " %06o", one->mode);
- strbuf_addch(&msg, '\n');
- }
-
- if (msg.len)
- strbuf_setlen(&msg, msg.len - 1);
- xfrm_msg = msg.len ? msg.buf : NULL;
-
if (!pgm &&
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
(S_IFMT & one->mode) != (S_IFMT & two->mode)) {
- /* a filepair that changes between file and symlink
+ /*
+ * a filepair that changes between file and symlink
* needs to be split into deletion and creation.
*/
struct diff_filespec *null = alloc_filespec(two->path);
run_diff_cmd(NULL, name, other, attr_path,
- one, null, xfrm_msg, o, 0);
+ one, null, &msg, o, p);
free(null);
+ strbuf_release(&msg);
+
null = alloc_filespec(one->path);
run_diff_cmd(NULL, name, other, attr_path,
- null, two, xfrm_msg, o, 0);
+ null, two, &msg, o, p);
free(null);
}
else
run_diff_cmd(pgm, name, other, attr_path,
- one, two, xfrm_msg, o, complete_rewrite);
+ one, two, &msg, o, p);
strbuf_release(&msg);
}
DIFF_OPT_CLR(options, COLOR_DIFF);
else if (!strcmp(arg, "--color-words"))
options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+ else if (!prefixcmp(arg, "--color-words=")) {
+ options->flags |= DIFF_OPT_COLOR_DIFF | DIFF_OPT_COLOR_DIFF_WORDS;
+ options->word_regex = arg + 14;
+ }
else if (!strcmp(arg, "--exit-code"))
DIFF_OPT_SET(options, EXIT_WITH_STATUS);
else if (!strcmp(arg, "--quiet"))
static char *run_textconv(const char *pgm, struct diff_filespec *spec,
size_t *outsize)
{
- struct diff_tempfile temp;
+ struct diff_tempfile *temp;
const char *argv[3];
const char **arg = argv;
struct child_process child;
struct strbuf buf = STRBUF_INIT;
- prepare_temp_file(spec->path, &temp, spec);
+ temp = prepare_temp_file(spec->path, spec);
*arg++ = pgm;
- *arg++ = temp.name;
+ *arg++ = temp->name;
*arg = NULL;
memset(&child, 0, sizeof(child));
if (start_command(&child) != 0 ||
strbuf_read(&buf, child.out, 0) < 0 ||
finish_command(&child) != 0) {
- if (temp.name == temp.tmp_path)
- unlink(temp.name);
+ remove_tempfile();
error("error running textconv command '%s'", pgm);
return NULL;
}
- if (temp.name == temp.tmp_path)
- unlink(temp.name);
+ remove_tempfile();
return strbuf_detach(&buf, outsize);
}
int stat_width;
int stat_name_width;
+ const char *word_regex;
/* this is set by diffcore for DIFF_FORMAT_PATCH */
int found_changes;
* and a mark is left in seen[] array for pathspec element that
* actually matched anything.
*/
-int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
+int match_pathspec(const char **pathspec, const char *name, int namelen,
+ int prefix, char *seen)
{
- int retval;
- const char *match;
+ int i, retval = 0;
+
+ if (!pathspec)
+ return 1;
name += prefix;
namelen -= prefix;
- for (retval = 0; (match = *pathspec++) != NULL; seen++) {
+ for (i = 0; pathspec[i] != NULL; i++) {
int how;
- if (retval && *seen == MATCHED_EXACTLY)
+ const char *match = pathspec[i] + prefix;
+ if (seen && seen[i] == MATCHED_EXACTLY)
continue;
- match += prefix;
how = match_one(match, name, namelen);
if (how) {
if (retval < how)
retval = how;
- if (*seen < how)
- *seen = how;
+ if (seen && seen[i] < how)
+ seen[i] = how;
}
}
return retval;
+++ /dev/null
-#include "cache.h"
-#include "tree.h"
-#include "cache-tree.h"
-
-
-static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
-{
- if (it->entry_count < 0)
- printf("%-40s %s%s (%d subtrees)\n",
- "invalid", x, pfx, it->subtree_nr);
- else
- printf("%s %s%s (%d entries, %d subtrees)\n",
- sha1_to_hex(it->sha1), x, pfx,
- it->entry_count, it->subtree_nr);
-}
-
-static int dump_cache_tree(struct cache_tree *it,
- struct cache_tree *ref,
- const char *pfx)
-{
- int i;
- int errs = 0;
-
- if (!it || !ref)
- /* missing in either */
- return 0;
-
- if (it->entry_count < 0) {
- dump_one(it, pfx, "");
- dump_one(ref, pfx, "#(ref) ");
- if (it->subtree_nr != ref->subtree_nr)
- errs = 1;
- }
- else {
- dump_one(it, pfx, "");
- if (hashcmp(it->sha1, ref->sha1) ||
- ref->entry_count != it->entry_count ||
- ref->subtree_nr != it->subtree_nr) {
- dump_one(ref, pfx, "#(ref) ");
- errs = 1;
- }
- }
-
- for (i = 0; i < it->subtree_nr; i++) {
- char path[PATH_MAX];
- struct cache_tree_sub *down = it->down[i];
- struct cache_tree_sub *rdwn;
-
- rdwn = cache_tree_sub(ref, down->name);
- sprintf(path, "%s%.*s/", pfx, down->namelen, down->name);
- if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
- errs = 1;
- }
- return errs;
-}
-
-int main(int ac, char **av)
-{
- struct cache_tree *another = cache_tree();
- if (read_cache() < 0)
- die("unable to read index file");
- cache_tree_update(another, active_cache, active_nr, 0, 1);
- return dump_cache_tree(active_cache_tree, another, "");
-}
const char *slash = path;
while ((slash = strchr(slash+1, '/')) != NULL) {
- struct stat st;
- int stat_status;
-
len = slash - path;
memcpy(buf, path, len);
buf[len] = 0;
- if (len <= state->base_dir_len)
- /*
- * checkout-index --prefix=<dir>; <dir> is
- * allowed to be a symlink to an existing
- * directory.
- */
- stat_status = stat(buf, &st);
- else
- /*
- * if there currently is a symlink, we would
- * want to replace it with a real directory.
- */
- stat_status = lstat(buf, &st);
-
- if (!stat_status && S_ISDIR(st.st_mode))
+ /*
+ * For 'checkout-index --prefix=<dir>', <dir> is
+ * allowed to be a symlink to an existing directory,
+ * and we set 'state->base_dir_len' below, such that
+ * we test the path components of the prefix with the
+ * stat() function instead of the lstat() function.
+ */
+ if (has_dirs_only_path(len, buf, state->base_dir_len))
continue; /* ok, it is already a directory. */
/*
- * We know stat_status == 0 means something exists
- * there and this mkdir would fail, but that is an
- * error codepath; we do not care, as we unlink and
- * mkdir again in such a case.
+ * If this mkdir() would fail, it could be that there
+ * is already a symlink or something else exists
+ * there, therefore we then try to unlink it and try
+ * one more time to create the directory.
*/
if (mkdir(buf, 0777)) {
if (errno == EEXIST && state->force &&
const char *system_path(const char *path)
{
- if (!is_absolute_path(path) && argv0_path) {
- struct strbuf d = STRBUF_INIT;
- strbuf_addf(&d, "%s/%s", argv0_path, path);
- path = strbuf_detach(&d, NULL);
+#ifdef RUNTIME_PREFIX
+ static const char *prefix;
+#else
+ static const char *prefix = PREFIX;
+#endif
+ struct strbuf d = STRBUF_INIT;
+
+ if (is_absolute_path(path))
+ return path;
+
+#ifdef RUNTIME_PREFIX
+ assert(argv0_path);
+ assert(is_absolute_path(argv0_path));
+
+ if (!prefix) {
+ const char *strip[] = {
+ GIT_EXEC_PATH,
+ BINDIR,
+ 0
+ };
+ const char **s;
+
+ for (s = strip; *s; s++) {
+ const char *sargv = argv0_path + strlen(argv0_path);
+ const char *ss = *s + strlen(*s);
+ while (argv0_path < sargv && *s < ss
+ && (*sargv == *ss ||
+ (is_dir_sep(*sargv) && is_dir_sep(*ss)))) {
+ sargv--;
+ ss--;
+ }
+ if (*s == ss) {
+ struct strbuf d = STRBUF_INIT;
+ /* We also skip the trailing directory separator. */
+ assert(sargv - argv0_path - 1 >= 0);
+ strbuf_add(&d, argv0_path, sargv - argv0_path - 1);
+ prefix = strbuf_detach(&d, NULL);
+ break;
+ }
+ }
}
+
+ if (!prefix) {
+ prefix = PREFIX;
+ fprintf(stderr, "RUNTIME_PREFIX requested, "
+ "but prefix computation failed. "
+ "Using static fallback '%s'.\n", prefix);
+ }
+#endif
+
+ strbuf_addf(&d, "%s/%s", prefix, path);
+ path = strbuf_detach(&d, NULL);
return path;
}
-void git_set_argv0_path(const char *path)
+const char *git_extract_argv0_path(const char *argv0)
{
- argv0_path = path;
+ const char *slash;
+
+ if (!argv0 || !*argv0)
+ return NULL;
+ slash = argv0 + strlen(argv0);
+
+ while (argv0 <= slash && !is_dir_sep(*slash))
+ slash--;
+
+ if (slash >= argv0) {
+ argv0_path = xstrndup(argv0, slash - argv0);
+ return slash + 1;
+ }
+
+ return argv0;
}
void git_set_argv_exec_path(const char *exec_path)
const char *old_path = getenv("PATH");
struct strbuf new_path = STRBUF_INIT;
- add_path(&new_path, argv_exec_path);
- add_path(&new_path, getenv(EXEC_PATH_ENVIRONMENT));
- add_path(&new_path, system_path(GIT_EXEC_PATH));
+ add_path(&new_path, git_exec_path());
add_path(&new_path, argv0_path);
if (old_path)
#define GIT_EXEC_CMD_H
extern void git_set_argv_exec_path(const char *exec_path);
-extern void git_set_argv0_path(const char *path);
-extern const char* git_exec_path(void);
+extern const char *git_extract_argv0_path(const char *path);
+extern const char *git_exec_path(void);
extern void setup_path(void);
extern const char **prepare_git_cmd(const char **argv);
extern int execv_git_cmd(const char **argv); /* NULL terminated */
#include "refs.h"
#include "csum-file.h"
#include "quote.h"
+#include "exec_cmd.h"
#define PACK_ID_BITS 16
#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
{
unsigned int i, show_stats = 1;
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory();
git_config(git_pack_config, NULL);
if (!pack_compression_seen && core_compression_seen)
git am [options] [<mbox>|<Maildir>...]
git am [options] (--resolved | --skip | --abort)
--
-d,dotest= (removed -- do not use)
i,interactive run interactively
-b,binary (historical option -- no-op)
+b,binary* (historical option -- no-op)
3,3way allow fall back on 3way merging if needed
s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
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
-rebasing (internal use for git-rebase)"
+rebasing* (internal use for git-rebase)"
. git-sh-setup
prefix=$(git rev-parse --show-prefix)
# unreliable -- stdin could be /dev/null for example
# and the caller did not intend to feed us a patch but
# wanted to continue unattended.
- tty -s
+ test -t 0
;;
*)
false
case "$resolved" in
'')
files=$(git diff-index --cached --name-only HEAD --) || exit
- if [ "$files" ]; then
- echo "Dirty index: cannot apply patches (dirty: $files)" >&2
- exit 1
- fi
+ test "$files" && die "Dirty index: cannot apply patches (dirty: $files)"
esac
if test "$(cat "$dotest/utf8")" = t
'history' => \&req_CATCHALL,
'watchers' => \&req_EMPTY,
'editors' => \&req_EMPTY,
+ 'noop' => \&req_EMPTY,
'annotate' => \&req_annotate,
'Global_option' => \&req_Globaloption,
#'annotate' => \&req_CATCHALL,
close $pipe || die "bad pipe: $! $?";
}
+ $updater->update();
+
### Then hooks/post-update
$hook = $ENV{GIT_DIR}.'hooks/post-update';
if (-x $hook) {
system($hook, "refs/heads/$state->{module}");
}
- $updater->update();
-
# foreach file specified on the command line ...
foreach my $filename ( @committedfiles )
{
if test -z "$merge_tool" ; then
if test -n "$DISPLAY"; then
- merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
if test -n "$GNOME_DESKTOP_SESSION_ID" ; then
- merge_tool_candidates="meld $merge_tool_candidates"
- fi
- if test "$KDE_FULL_SESSION" = "true"; then
- merge_tool_candidates="kdiff3 $merge_tool_candidates"
+ merge_tool_candidates="meld kdiff3 tkdiff xxdiff gvimdiff"
+ else
+ merge_tool_candidates="kdiff3 tkdiff xxdiff meld gvimdiff"
fi
fi
if echo "${VISUAL:-$EDITOR}" | grep 'emacs' > /dev/null 2>&1; then
- merge_tool_candidates="$merge_tool_candidates emerge"
- fi
- if echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
- merge_tool_candidates="$merge_tool_candidates vimdiff"
+ merge_tool_candidates="$merge_tool_candidates emerge opendiff vimdiff"
+ elif echo "${VISUAL:-$EDITOR}" | grep 'vim' > /dev/null 2>&1; then
+ merge_tool_candidates="$merge_tool_candidates vimdiff opendiff emerge"
+ else
+ merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
fi
- merge_tool_candidates="$merge_tool_candidates opendiff emerge vimdiff"
echo "merge tool candidates: $merge_tool_candidates"
for i in $merge_tool_candidates; do
init_merge_tool_path $i
pick_one -n $sha1 || failed=t
case "$(peek_next_command)" in
squash|s)
- EDIT_COMMIT=
USE_OUTPUT=output
MSG_OPT=-F
- MSG_FILE="$MSG"
+ EDIT_OR_FILE="$MSG"
cp "$MSG" "$SQUASH_MSG"
;;
*)
- EDIT_COMMIT=-e
USE_OUTPUT=
MSG_OPT=
- MSG_FILE=
+ EDIT_OR_FILE=-e
rm -f "$SQUASH_MSG" || exit
cp "$MSG" "$GIT_DIR"/SQUASH_MSG
rm -f "$GIT_DIR"/MERGE_MSG || exit
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 "$MSG_FILE" $EDIT_COMMIT || failed=t
+ $USE_OUTPUT git commit --no-verify \
+ $MSG_OPT "$EDIT_OR_FILE" || failed=t
fi
if test $failed = t
then
test -d "$REWRITTEN" && PRESERVE_MERGES=t
test -f "$DOTEST"/strategy && STRATEGY="$(cat "$DOTEST"/strategy)"
test -f "$DOTEST"/verbose && VERBOSE=t
- test ! -s "$DOTEST"/upstream && REBASE_ROOT=t
+ test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
}
while test $# != 0
;;
--)
shift
- test ! -z "$REBASE_ROOT" -o $# -eq 1 -o $# -eq 2 || usage
+ test -z "$REBASE_ROOT" -a $# -ge 1 -a $# -le 2 ||
+ test ! -z "$REBASE_ROOT" -a $# -le 1 || usage
test -d "$DOTEST" &&
die "Interactive rebase already started"
test -z "$ONTO" && ONTO=$UPSTREAM
shift
else
+ UPSTREAM=
UPSTREAM_ARG=--root
test -z "$ONTO" &&
die "You must specify --onto when using --root"
echo "detached HEAD" > "$DOTEST"/head-name
echo $HEAD > "$DOTEST"/head
- echo $UPSTREAM > "$DOTEST"/upstream
+ case "$REBASE_ROOT" in
+ '')
+ rm -f "$DOTEST"/rebase-root ;;
+ *)
+ : >"$DOTEST"/rebase-root ;;
+ esac
echo $ONTO > "$DOTEST"/onto
test -z "$STRATEGY" || echo "$STRATEGY" > "$DOTEST"/strategy
test t = "$VERBOSE" && : > "$DOTEST"/verbose
$Git::SVN::_follow_parent = 1;
my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
'config-dir=s' => \$Git::SVN::Ra::config_dir,
- 'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache );
+ 'no-auth-cache' => \$Git::SVN::Prompt::_no_auth_cache,
+ 'ignore-paths=s' => \$SVN::Git::Fetcher::_ignore_regex );
my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent,
'authors-file|A=s' => \$_authors,
'repack:i' => \$Git::SVN::_repack,
use Carp qw/croak/;
use File::Temp qw/tempfile/;
use IO::File qw//;
+use vars qw/$_ignore_regex/;
# file baton members: path, mode_a, mode_b, pool, fh, blob, base
sub new {
$_[0] =~ m{(?:^|/)\.git(?:/|$)};
}
+# return value: 0 -- don't ignore, 1 -- ignore
+sub is_path_ignored {
+ my ($path) = @_;
+ return 1 if in_dot_git($path);
+ return 0 unless defined($_ignore_regex);
+ return 1 if $path =~ m!$_ignore_regex!o;
+ return 0;
+}
+
sub set_path_strip {
my ($self, $path) = @_;
$self->{path_strip} = qr/^\Q$path\E(\/|$)/ if length $path;
sub delete_entry {
my ($self, $path, $rev, $pb) = @_;
- return undef if in_dot_git($path);
+ return undef if is_path_ignored($path);
my $gpath = $self->git_path($path);
return undef if ($gpath eq '');
my ($self, $path, $pb, $rev) = @_;
my ($mode, $blob);
- goto out if in_dot_git($path);
+ goto out if is_path_ignored($path);
my $gpath = $self->git_path($path);
($mode, $blob) = (command('ls-tree', $self->{c}, '--', $gpath)
my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
my $mode;
- if (!in_dot_git($path)) {
+ if (!is_path_ignored($path)) {
my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
delete $self->{empty}->{$dir};
$mode = '100644';
sub add_directory {
my ($self, $path, $cp_path, $cp_rev) = @_;
- goto out if in_dot_git($path);
+ goto out if is_path_ignored($path);
my $gpath = $self->git_path($path);
if ($gpath eq '') {
my ($ls, $ctx) = command_output_pipe(qw/ls-tree
sub change_dir_prop {
my ($self, $db, $prop, $value) = @_;
- return undef if in_dot_git($db->{path});
+ return undef if is_path_ignored($db->{path});
$self->{dir_prop}->{$db->{path}} ||= {};
$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
undef;
sub absent_directory {
my ($self, $path, $pb) = @_;
- return undef if in_dot_git($pb->{path});
+ return undef if is_path_ignored($path);
$self->{absent_dir}->{$pb->{path}} ||= [];
push @{$self->{absent_dir}->{$pb->{path}}}, $path;
undef;
sub absent_file {
my ($self, $path, $pb) = @_;
- return undef if in_dot_git($pb->{path});
+ return undef if is_path_ignored($path);
$self->{absent_file}->{$pb->{path}} ||= [];
push @{$self->{absent_file}->{$pb->{path}}}, $path;
undef;
sub change_file_prop {
my ($self, $fb, $prop, $value) = @_;
- return undef if in_dot_git($fb->{path});
+ return undef if is_path_ignored($fb->{path});
if ($prop eq 'svn:executable') {
if ($fb->{mode_b} != 120000) {
$fb->{mode_b} = defined $value ? 100755 : 100644;
sub apply_textdelta {
my ($self, $fb, $exp) = @_;
- return undef if (in_dot_git($fb->{path}));
+ return undef if is_path_ignored($fb->{path});
my $fh = $::_repository->temp_acquire('svn_delta');
# $fh gets auto-closed() by SVN::TxDelta::apply(),
# (but $base does not,) so dup() it for reading in close_file
sub close_file {
my ($self, $fb, $exp) = @_;
- return undef if (in_dot_git($fb->{path}));
+ return undef if is_path_ignored($fb->{path});
my $hash;
my $path = $self->git_path($fb->{path});
BEGIN {
# enforce temporary pool usage for some simple functions
no strict 'refs';
- for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) {
+ for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root
+ get_file/) {
my $SUPER = "SUPER::$f";
*$f = sub {
my $self = shift;
#include "exec_cmd.h"
#include "cache.h"
#include "quote.h"
+#include "run-command.h"
const char git_usage_string[] =
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
int option;
};
-static int run_command(struct cmd_struct *p, int argc, const char **argv)
+static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
{
int status;
struct stat st;
struct cmd_struct *p = commands+i;
if (strcmp(p->cmd, cmd))
continue;
- exit(run_command(p, argc, argv));
+ exit(run_builtin(p, argc, argv));
}
}
{
struct strbuf cmd = STRBUF_INIT;
const char *tmp;
+ int status;
strbuf_addf(&cmd, "git-%s", argv[0]);
trace_argv_printf(argv, "trace: exec:");
- /* execvp() can only ever return if it fails */
- execvp(cmd.buf, (char **)argv);
-
- trace_printf("trace: exec failed: %s\n", strerror(errno));
+ /*
+ * if we fail because the command is not found, it is
+ * OK to return. Otherwise, we just pass along the status code.
+ */
+ status = run_command_v_opt(argv, 0);
+ if (status != -ERR_RUN_COMMAND_EXEC) {
+ if (IS_RUN_COMMAND_ERR(status))
+ die("unable to run '%s'", argv[0]);
+ exit(-status);
+ }
+ errno = ENOENT; /* as if we called execvp */
argv[0] = tmp;
int main(int argc, const char **argv)
{
- const char *cmd = argv[0] && *argv[0] ? argv[0] : "git-help";
- char *slash = (char *)cmd + strlen(cmd);
+ const char *cmd;
- /*
- * Take the basename of argv[0] as the command
- * name, and the dirname as the default exec_path
- * if we don't have anything better.
- */
- while (cmd <= slash && !is_dir_sep(*slash))
- slash--;
- if (cmd <= slash) {
- *slash++ = 0;
- git_set_argv0_path(cmd);
- cmd = slash;
- }
+ cmd = git_extract_argv0_path(argv[0]);
+ if (!cmd)
+ cmd = "git-help";
/*
* "git-xxxx" is the same as "git xxxx", but we obviously:
$GITWEB_LIST during installation. If empty, $projectroot is used
to scan for repositories.
* $my_url, $my_uri
- URL and absolute URL of gitweb script; you might need to set those
- variables if you are using 'pathinfo' feature: see also below.
+ Full URL and absolute URL of gitweb script;
+ in earlier versions of gitweb you might have need to set those
+ variables, now there should be no need to do it.
* $home_link
Target of the home link on top of all pages (the first part of view
- "breadcrumbs"). By default set to absolute URI of a page; you might
- need to set it up to [base] gitweb URI if you use 'pathinfo' feature
- (alternative format of the URLs, with project name embedded directly
- in the path part of URL).
+ "breadcrumbs"). By default set to absolute URI of a page ($my_uri).
* @stylesheets
List of URIs of stylesheets (relative to base URI of a page). You
might specify more than one stylesheet, for example use gitweb.css
$home_link = "/";
+PATH_INFO usage
+-----------------------
+If you enable PATH_INFO usage in gitweb by putting
+
+ $feature{'pathinfo'}{'default'} = [1];
+
+in your gitweb.conf, it is possible to set up your server so that it
+consumes and produces URLs in the form
+
+http://git.example.com/project.git/shortlog/sometag
+
+by using a configuration such as the following, that assumes that
+/var/www/gitweb is the DocumentRoot of your webserver, and that it
+contains the gitweb.cgi script and complementary static files
+(stylesheet, favicon):
+
+<VirtualHost *:80>
+ ServerAlias git.example.com
+
+ DocumentRoot /var/www/gitweb
+
+ <Directory /var/www/gitweb>
+ Options ExecCGI
+ AddHandler cgi-script cgi
+
+ DirectoryIndex gitweb.cgi
+
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
+ </Directory>
+</VirtualHost>
+
+The rewrite rule guarantees that existing static files will be properly
+served, whereas any other URL will be passed to gitweb as PATH_INFO
+parameter.
+
+Notice that in this case you don't need special settings for
+@stylesheets, $my_uri and $home_link, but you lose "dumb client" access
+to your project .git dirs. A possible workaround for the latter is the
+following: in your project root dir (e.g. /pub/git) have the projects
+named without a .git extension (e.g. /pub/git/project instead of
+/pub/git/project.git) and configure Apache as follows:
+
+<VirtualHost *:80>
+ ServerAlias git.example.com
+
+ DocumentRoot /var/www/gitweb
+
+ AliasMatch ^(/.*?)(\.git)(/.*)? /pub/git$1$3
+ <Directory /var/www/gitweb>
+ Options ExecCGI
+ AddHandler cgi-script cgi
+
+ DirectoryIndex gitweb.cgi
+
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteRule ^.* /gitweb.cgi/$0 [L,PT]
+ </Directory>
+</VirtualHost>
+
+The additional AliasMatch makes it so that
+
+http://git.example.com/project.git
+
+will give raw access to the project's git dir (so that the project can
+be cloned), while
+
+http://git.example.com/project
+
+will provide human-friendly gitweb access.
+
+
Originally written by:
Kay Sievers <kay.sievers@vrfy.org>
<meta name="robots" content="index, nofollow"/>
<title>$title</title>
EOF
-# print out each stylesheet that exist
+ # the stylesheet, favicon etc urls won't work correctly with path_info
+ # unless we set the appropriate base URL
+ if ($ENV{'PATH_INFO'}) {
+ print '<base href="'.esc_url($my_url).'" />\n';
+ }
+ # print out each stylesheet that exist, providing backwards capability
+ # for those people who defined $stylesheet in a config file
if (defined $stylesheet) {
-#provides backwards capability for those people who define style sheet in a config file
print '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'"/>'."\n";
} else {
foreach my $stylesheet (@stylesheets) {
}
if (defined($commitlist[0])) {
%latest_commit = %{$commitlist[0]};
- %latest_date = parse_date($latest_commit{'author_epoch'});
+ my $latest_epoch = $latest_commit{'committer_epoch'};
+ %latest_date = parse_date($latest_epoch);
+ my $if_modified = $cgi->http('IF_MODIFIED_SINCE');
+ if (defined $if_modified) {
+ my $since;
+ if (eval { require HTTP::Date; 1; }) {
+ $since = HTTP::Date::str2time($if_modified);
+ } elsif (eval { require Time::ParseDate; 1; }) {
+ $since = Time::ParseDate::parsedate($if_modified, GMT => 1);
+ }
+ if (defined $since && $latest_epoch <= $since) {
+ print $cgi->header(
+ -type => $content_type,
+ -charset => 'utf-8',
+ -last_modified => $latest_date{'rfc2822'},
+ -status => '304 Not Modified');
+ return;
+ }
+ }
print $cgi->header(
-type => $content_type,
-charset => 'utf-8',
print "<title>$title</title>\n" .
"<link>$alt_url</link>\n" .
"<description>$descr</description>\n" .
- "<language>en</language>\n";
+ "<language>en</language>\n" .
+ # project owner is responsible for 'editorial' content
+ "<managingEditor>$owner</managingEditor>\n";
+ if (defined $logo || defined $favicon) {
+ # prefer the logo to the favicon, since RSS
+ # doesn't allow both
+ my $img = esc_url($logo || $favicon);
+ print "<image>\n" .
+ "<url>$img</url>\n" .
+ "<title>$title</title>\n" .
+ "<link>$alt_url</link>\n" .
+ "</image>\n";
+ }
+ if (%latest_date) {
+ print "<pubDate>$latest_date{'rfc2822'}</pubDate>\n";
+ print "<lastBuildDate>$latest_date{'rfc2822'}</lastBuildDate>\n";
+ }
+ print "<generator>gitweb v.$version/$git_version</generator>\n";
} elsif ($format eq 'atom') {
print <<XML;
<feed xmlns="http://www.w3.org/2005/Atom">
} else {
print "<updated>$latest_date{'iso-8601'}</updated>\n";
}
+ print "<generator version='$version/$git_version'>gitweb</generator>\n";
}
# contents
#include "blob.h"
#include "quote.h"
#include "parse-options.h"
+#include "exec_cmd.h"
static void hash_fd(int fd, const char *type, int write_object, const char *path)
{
type = blob_type;
+ git_extract_argv0_path(argv[0]);
+
git_config(git_default_config, NULL);
argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0);
#include "exec_cmd.h"
#include "remote.h"
#include "list-objects.h"
+#include "sigchain.h"
#include <expat.h>
DAV_HEADER_TIMEOUT = (1u << 2)
};
-static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options) {
+static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
+{
struct strbuf buf = STRBUF_INIT;
struct curl_slist *dav_headers = NULL;
- if(options & DAV_HEADER_IF) {
+ if (options & DAV_HEADER_IF) {
strbuf_addf(&buf, "If: (<%s>)", lock->token);
dav_headers = curl_slist_append(dav_headers, buf.buf);
strbuf_reset(&buf);
}
- if(options & DAV_HEADER_LOCK) {
+ if (options & DAV_HEADER_LOCK) {
strbuf_addf(&buf, "Lock-Token: <%s>", lock->token);
dav_headers = curl_slist_append(dav_headers, buf.buf);
strbuf_reset(&buf);
}
- if(options & DAV_HEADER_TIMEOUT) {
+ if (options & DAV_HEADER_TIMEOUT) {
strbuf_addf(&buf, "Timeout: Second-%ld", lock->timeout);
dav_headers = curl_slist_append(dav_headers, buf.buf);
strbuf_reset(&buf);
static void remove_locks_on_signal(int signo)
{
remove_locks();
- signal(signo, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
struct ref *ref;
char *rewritten_url = NULL;
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory();
remote = xcalloc(sizeof(*remote), 1);
goto cleanup;
}
- signal(SIGINT, remove_locks_on_signal);
- signal(SIGHUP, remove_locks_on_signal);
- signal(SIGQUIT, remove_locks_on_signal);
- signal(SIGTERM, remove_locks_on_signal);
+ sigchain_push_common(remove_locks_on_signal);
/* Check whether the remote has server info files */
remote->can_update_info_refs = 0;
*/
#include "cache.h"
+#include "exec_cmd.h"
#ifdef NO_OPENSSL
typedef void *SSL;
#endif
int total, n = 0;
int nongit_ok;
+ git_extract_argv0_path(argv[0]);
+
/* init the random number generator */
arc4_init();
#include "tree.h"
#include "progress.h"
#include "fsck.h"
+#include "exec_cmd.h"
static const char index_pack_usage[] =
"git index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] [--strict] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
struct pack_idx_entry **idx_objects;
unsigned char pack_sha1[20];
+ git_extract_argv0_path(argv[0]);
+
/*
* We wish to read the repository's config file if any, and
* for that it is necessary to call setup_git_directory_gently().
* Copyright (c) 2005, Junio C Hamano
*/
#include "cache.h"
+#include "sigchain.h"
static struct lock_file *lock_file_list;
static const char *alternate_index_output;
static void remove_lock_file_on_signal(int signo)
{
remove_lock_file();
- signal(signo, SIG_DFL);
+ sigchain_pop(signo);
raise(signo);
}
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= lk->fd) {
if (!lock_file_list) {
- signal(SIGINT, remove_lock_file_on_signal);
- signal(SIGHUP, remove_lock_file_on_signal);
- signal(SIGTERM, remove_lock_file_on_signal);
- signal(SIGQUIT, remove_lock_file_on_signal);
- signal(SIGPIPE, remove_lock_file_on_signal);
+ sigchain_push_common(remove_lock_file_on_signal);
atexit(remove_lock_file);
}
lk->owner = getpid();
#include "cache.h"
#include "run-command.h"
+#include "exec_cmd.h"
static const char *pgm;
static const char *arguments[9];
if (argc < 3)
usage("git-merge-index [-o] [-q] <merge-program> (-a | [--] <filename>*)");
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory();
read_cache();
#include "tree-walk.h"
#include "xdiff-interface.h"
#include "blob.h"
+#include "exec_cmd.h"
static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
static int resolve_directories = 1;
if (argc != 4)
usage(merge_tree_usage);
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory();
buf1 = get_tree_descriptor(t+0, argv[1]);
#include "cache.h"
#include "tag.h"
+#include "exec_cmd.h"
/*
* A signature file has a very simple fixed format: four lines
if (argc != 1)
usage("git-mktag < signaturefile");
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory();
if (strbuf_read(&buf, 0, 4096) < 0) {
#include "cache.h"
#include "quote.h"
#include "tree.h"
+#include "exec_cmd.h"
static struct treeent {
unsigned mode;
unsigned char sha1[20];
int line_termination = '\n';
+ git_extract_argv0_path(av[0]);
+
setup_git_directory();
while ((1 < ac) && av[1][0] == '-') {
objects[nr].mode = mode;
array->nr = ++nr;
}
+
+void object_array_remove_duplicates(struct object_array *array)
+{
+ int ref, src, dst;
+ struct object_array_entry *objects = array->objects;
+
+ for (ref = 0; ref < array->nr - 1; ref++) {
+ for (src = ref + 1, dst = src;
+ src < array->nr;
+ src++) {
+ if (!strcmp(objects[ref].name, objects[src].name))
+ continue;
+ if (src != dst)
+ objects[dst] = objects[src];
+ dst++;
+ }
+ array->nr = dst;
+ }
+}
/* Object array handling .. */
void add_object_array(struct object *obj, const char *name, struct object_array *array);
void add_object_array_with_mode(struct object *obj, const char *name, struct object_array *array, unsigned mode);
+void object_array_remove_duplicates(struct object_array *);
#endif /* OBJECT_H */
*/
#include "cache.h"
+#include "exec_cmd.h"
#define BLKSIZE 512
unsigned char *sha1;
char buf[42]; /* 40 byte sha1 + \n + \0 */
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory();
for (i = 1; i < argc; i++) {
#include "cache.h"
#include "run-command.h"
+#include "sigchain.h"
/*
* This is split up from the rest of git so that we can do
finish_command(&pager_process);
}
+static void wait_for_pager_signal(int signo)
+{
+ wait_for_pager();
+ sigchain_pop(signo);
+ raise(signo);
+}
+
void setup_pager(void)
{
const char *pager = getenv("GIT_PAGER");
close(pager_process.in);
/* this makes sure that the parent terminates after the pager */
+ sigchain_push_common(wait_for_pager_signal);
atexit(wait_for_pager);
}
#include "git-compat-util.h"
#include "parse-options.h"
#include "cache.h"
+#include "commit.h"
#define OPT_SHORT 1
#define OPT_UNSET 2
return 0;
}
+int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
+{
+ unsigned char sha1[20];
+ struct commit *commit;
+
+ if (!arg)
+ return -1;
+ if (get_sha1(arg, sha1))
+ return error("malformed object name %s", arg);
+ commit = lookup_commit_reference(sha1);
+ if (!commit)
+ return error("no such commit %s", arg);
+ commit_list_insert(commit, opt->value);
+ return 0;
+}
+
/*
* This should really be OPTION_FILENAME type as a part of
* parse_options that take prefix to do this while parsing.
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);
#define OPT__VERBOSE(var) OPT_BOOLEAN('v', "verbose", (var), "be verbose")
#define OPT__QUIET(var) OPT_BOOLEAN('q', "quiet", (var), "be quiet")
#include "cache.h"
+#include "exec_cmd.h"
static void flush_current_id(int patchlen, unsigned char *id, git_SHA_CTX *c)
{
if (argc != 1)
usage(patch_id_usage);
+ git_extract_argv0_path(argv[0]);
+
generate_id_list();
return 0;
}
default:
die("unexpected diff status %c", p->status);
case DIFF_STATUS_UNMERGED:
+ /*
+ * ADD_CACHE_IGNORE_REMOVAL is unset if "git
+ * add -u" is calling us, In such a case, a
+ * missing work tree file needs to be removed
+ * if there is an unmerged entry at stage #2,
+ * but such a diff record is followed by
+ * another with DIFF_STATUS_DELETED (and if
+ * there is no stage #2, we won't see DELETED
+ * nor MODIFIED). We can simply continue
+ * either way.
+ */
+ if (!(data->flags & ADD_CACHE_IGNORE_REMOVAL))
+ continue;
+ /*
+ * Otherwise, it is "git add path" is asking
+ * to explicitly add it; we fall through. A
+ * missing work tree file is an error and is
+ * caught by add_file_to_index() in such a
+ * case.
+ */
case DIFF_STATUS_MODIFIED:
case DIFF_STATUS_TYPE_CHANGED:
if (add_file_to_index(&the_index, path, data->flags)) {
return 1;
}
-int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs, void *cb_data)
{
const char *logfile;
FILE *logfp;
logfp = fopen(logfile, "r");
if (!logfp)
return -1;
+
+ if (ofs) {
+ struct stat statbuf;
+ if (fstat(fileno(logfp), &statbuf) ||
+ statbuf.st_size < ofs ||
+ fseek(logfp, -ofs, SEEK_END) ||
+ fgets(buf, sizeof(buf), logfp))
+ return -1;
+ }
+
while (fgets(buf, sizeof(buf), logfp)) {
unsigned char osha1[20], nsha1[20];
char *email_end, *message;
return ret;
}
+int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+{
+ return for_each_recent_reflog_ent(ref, fn, 0, cb_data);
+}
+
static int do_for_each_reflog(const char *base, each_ref_fn fn, void *cb_data)
{
DIR *dir = opendir(git_path("logs/%s", base));
/* iterate over reflog entries */
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
+int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long, void *cb_data);
/*
* Calls the specified function for each reflog file until it returns nonzero,
if (!tag->tagged)
die("bad tag");
object = parse_object(tag->tagged->sha1);
- if (!object)
+ if (!object) {
+ if (flags & UNINTERESTING)
+ return NULL;
die("bad object %s", sha1_to_hex(tag->tagged->sha1));
+ }
}
/*
while (parent) {
struct commit *p = parent->item;
parent = parent->next;
+ if (p)
+ p->object.flags |= UNINTERESTING;
if (parse_commit(p) < 0)
- return -1;
- p->object.flags |= UNINTERESTING;
+ continue;
if (p->parents)
mark_parents_uninteresting(p);
if (p->object.flags & SEEN)
if (!strcmp(arg, "--all")) {
handle_refs(revs, flags, for_each_ref);
+ handle_refs(revs, flags, head_ref);
continue;
}
if (!strcmp(arg, "--branches")) {
} else {
execvp(cmd->argv[0], (char *const*) cmd->argv);
}
- die("exec %s failed.", cmd->argv[0]);
+ trace_printf("trace: exec '%s' failed: %s\n", cmd->argv[0],
+ strerror(errno));
+ exit(127);
}
#else
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
#endif
if (cmd->pid < 0) {
+ int err = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
close(cmd->out);
if (need_err)
close_pair(fderr);
- return -ERR_RUN_COMMAND_FORK;
+ return err == ENOENT ?
+ -ERR_RUN_COMMAND_EXEC :
+ -ERR_RUN_COMMAND_FORK;
}
if (need_in)
if (!WIFEXITED(status))
return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
code = WEXITSTATUS(status);
- if (code)
+ switch (code) {
+ case 127:
+ return -ERR_RUN_COMMAND_EXEC;
+ case 0:
+ return 0;
+ default:
return -code;
- return 0;
+ }
}
}
ERR_RUN_COMMAND_WAITPID_SIGNAL,
ERR_RUN_COMMAND_WAITPID_NOEXIT,
};
+#define IS_RUN_COMMAND_ERR(x) ((x) <= -ERR_RUN_COMMAND_FORK)
struct child_process {
const char **argv;
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
void *buf, unsigned long len, time_t mtime)
{
- int fd, size, ret;
+ int fd, ret;
+ size_t size;
unsigned char *compressed;
z_stream stream;
char *filename;
return slash;
}
+/*
+ * *string and *len will only be substituted, and *string returned (for
+ * later free()ing) if the string passed in is of the form @{-<n>}.
+ */
+static char *substitute_nth_last_branch(const char **string, int *len)
+{
+ struct strbuf buf = STRBUF_INIT;
+ int ret = interpret_nth_last_branch(*string, &buf);
+
+ if (ret == *len) {
+ size_t size;
+ *string = strbuf_detach(&buf, &size);
+ *len = size;
+ return (char *)*string;
+ }
+
+ return NULL;
+}
+
int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
{
+ char *last_branch = substitute_nth_last_branch(&str, &len);
const char **p, *r;
int refs_found = 0;
break;
}
}
+ free(last_branch);
return refs_found;
}
int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
{
+ char *last_branch = substitute_nth_last_branch(&str, &len);
const char **p;
int logs_found = 0;
if (!warn_ambiguous_refs)
break;
}
+ free(last_branch);
return logs_found;
}
+static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
{
static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
if (len == 40 && !get_sha1_hex(str, sha1))
return 0;
- /* basic@{time or number} format to query ref-log */
+ /* basic@{time or number or -number} format to query ref-log */
reflog_len = at = 0;
- if (str[len-1] == '}') {
- for (at = 0; at < len - 1; at++) {
+ if (len && str[len-1] == '}') {
+ for (at = len-2; at >= 0; at--) {
if (str[at] == '@' && str[at+1] == '{') {
reflog_len = (len-1) - (at+2);
len = at;
return -1;
if (!len && reflog_len) {
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+ /* try the @{-N} syntax for n-th checkout */
+ ret = interpret_nth_last_branch(str+at, &buf);
+ if (ret > 0) {
+ /* substitute this branch name and restart */
+ return get_sha1_1(buf.buf, buf.len, sha1);
+ } else if (ret == 0) {
+ return -1;
+ }
/* allow "@{...}" to mean the current branch reflog */
refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
} else if (reflog_len)
return 0;
}
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
-
static int get_parent(const char *name, int len,
unsigned char *result, int idx)
{
return retval;
}
+struct grab_nth_branch_switch_cbdata {
+ long cnt, alloc;
+ struct strbuf *buf;
+};
+
+static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+ const char *email, unsigned long timestamp, int tz,
+ const char *message, void *cb_data)
+{
+ struct grab_nth_branch_switch_cbdata *cb = cb_data;
+ const char *match = NULL, *target = NULL;
+ size_t len;
+ int nth;
+
+ if (!prefixcmp(message, "checkout: moving from ")) {
+ match = message + strlen("checkout: moving from ");
+ target = strstr(match, " to ");
+ }
+
+ if (!match || !target)
+ return 0;
+
+ len = target - match;
+ nth = cb->cnt++ % cb->alloc;
+ strbuf_reset(&cb->buf[nth]);
+ strbuf_add(&cb->buf[nth], match, len);
+ return 0;
+}
+
+/*
+ * This reads "@{-N}" syntax, finds the name of the Nth previous
+ * branch we were on, and places the name of the branch in the given
+ * buf and returns the number of characters parsed if successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ */
+int interpret_nth_last_branch(const char *name, struct strbuf *buf)
+{
+ long nth;
+ int i, retval;
+ struct grab_nth_branch_switch_cbdata cb;
+ const char *brace;
+ char *num_end;
+
+ if (name[0] != '@' || name[1] != '{' || name[2] != '-')
+ return -1;
+ brace = strchr(name, '}');
+ if (!brace)
+ return -1;
+ nth = strtol(name+3, &num_end, 10);
+ if (num_end != brace)
+ return -1;
+ if (nth <= 0)
+ return -1;
+ cb.alloc = nth;
+ cb.buf = xmalloc(nth * sizeof(struct strbuf));
+ for (i = 0; i < nth; i++)
+ strbuf_init(&cb.buf[i], 20);
+ cb.cnt = 0;
+ retval = 0;
+ for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
+ if (cb.cnt < nth) {
+ cb.cnt = 0;
+ for (i = 0; i < nth; i++)
+ strbuf_release(&cb.buf[i]);
+ for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+ }
+ if (cb.cnt < nth)
+ goto release_return;
+ i = cb.cnt % nth;
+ strbuf_reset(buf);
+ strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
+ retval = brace-name+1;
+
+release_return:
+ for (i = 0; i < nth; i++)
+ strbuf_release(&cb.buf[i]);
+ free(cb.buf);
+
+ return retval;
+}
+
/*
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
* notably "xyz^" for "parent of xyz"
--- /dev/null
+#include "sigchain.h"
+#include "cache.h"
+
+#define SIGCHAIN_MAX_SIGNALS 32
+
+struct sigchain_signal {
+ sigchain_fun *old;
+ int n;
+ int alloc;
+};
+static struct sigchain_signal signals[SIGCHAIN_MAX_SIGNALS];
+
+static void check_signum(int sig)
+{
+ if (sig < 1 || sig >= SIGCHAIN_MAX_SIGNALS)
+ die("BUG: signal out of range: %d", sig);
+}
+
+int sigchain_push(int sig, sigchain_fun f)
+{
+ struct sigchain_signal *s = signals + sig;
+ check_signum(sig);
+
+ ALLOC_GROW(s->old, s->n + 1, s->alloc);
+ s->old[s->n] = signal(sig, f);
+ if (s->old[s->n] == SIG_ERR)
+ return -1;
+ s->n++;
+ return 0;
+}
+
+int sigchain_pop(int sig)
+{
+ struct sigchain_signal *s = signals + sig;
+ check_signum(sig);
+ if (s->n < 1)
+ return 0;
+
+ if (signal(sig, s->old[s->n - 1]) == SIG_ERR)
+ return -1;
+ s->n--;
+ return 0;
+}
+
+void sigchain_push_common(sigchain_fun f)
+{
+ sigchain_push(SIGINT, f);
+ sigchain_push(SIGHUP, f);
+ sigchain_push(SIGTERM, f);
+ sigchain_push(SIGQUIT, f);
+ sigchain_push(SIGPIPE, f);
+}
--- /dev/null
+#ifndef SIGCHAIN_H
+#define SIGCHAIN_H
+
+typedef void (*sigchain_fun)(int);
+
+int sigchain_push(int sig, sigchain_fun f);
+int sigchain_pop(int sig);
+
+void sigchain_push_common(sigchain_fun f);
+
+#endif /* SIGCHAIN_H */
#include "cache.h"
-struct pathname {
+static struct cache_def {
+ char path[PATH_MAX + 1];
int len;
- char path[PATH_MAX];
-};
+ int flags;
+ int track_flags;
+ int prefix_len_stat_func;
+} cache;
-/* Return matching pathname prefix length, or zero if not matching */
-static inline int match_pathname(int len, const char *name, struct pathname *match)
+/*
+ * Returns the length (on a path component basis) of the longest
+ * common prefix match of 'name' and the cached path string.
+ */
+static inline int longest_match_lstat_cache(int len, const char *name,
+ int *previous_slash)
{
- int match_len = match->len;
- return (len > match_len &&
- name[match_len] == '/' &&
- !memcmp(name, match->path, match_len)) ? match_len : 0;
+ int max_len, match_len = 0, match_len_prev = 0, i = 0;
+
+ max_len = len < cache.len ? len : cache.len;
+ while (i < max_len && name[i] == cache.path[i]) {
+ if (name[i] == '/') {
+ match_len_prev = match_len;
+ match_len = i;
+ }
+ i++;
+ }
+ /* Is the cached path string a substring of 'name'? */
+ if (i == cache.len && cache.len < len && name[cache.len] == '/') {
+ match_len_prev = match_len;
+ match_len = cache.len;
+ /* Is 'name' a substring of the cached path string? */
+ } else if ((i == len && len < cache.len && cache.path[len] == '/') ||
+ (i == len && len == cache.len)) {
+ match_len_prev = match_len;
+ match_len = len;
+ }
+ *previous_slash = match_len_prev;
+ return match_len;
}
-static inline void set_pathname(int len, const char *name, struct pathname *match)
+static inline void reset_lstat_cache(int track_flags, int prefix_len_stat_func)
{
- if (len < PATH_MAX) {
- match->len = len;
- memcpy(match->path, name, len);
- match->path[len] = 0;
- }
+ cache.path[0] = '\0';
+ cache.len = 0;
+ cache.flags = 0;
+ cache.track_flags = track_flags;
+ cache.prefix_len_stat_func = prefix_len_stat_func;
}
-int has_symlink_leading_path(int len, const char *name)
+#define FL_DIR (1 << 0)
+#define FL_NOENT (1 << 1)
+#define FL_SYMLINK (1 << 2)
+#define FL_LSTATERR (1 << 3)
+#define FL_ERR (1 << 4)
+#define FL_FULLPATH (1 << 5)
+
+/*
+ * Check if name 'name' of length 'len' has a symlink leading
+ * component, or if the directory exists and is real, or not.
+ *
+ * To speed up the check, some information is allowed to be cached.
+ * This can be indicated by the 'track_flags' argument, which also can
+ * be used to indicate that we should check the full path.
+ *
+ * The 'prefix_len_stat_func' parameter can be used to set the length
+ * of the prefix, where the cache should use the stat() function
+ * instead of the lstat() function to test each path component.
+ */
+static int lstat_cache(int len, const char *name,
+ int track_flags, int prefix_len_stat_func)
{
- static struct pathname link, nonlink;
- char path[PATH_MAX];
+ int match_len, last_slash, last_slash_dir, previous_slash;
+ int match_flags, ret_flags, save_flags, max_len, ret;
struct stat st;
- char *sp;
- int known_dir;
- /*
- * See if the last known symlink cache matches.
- */
- if (match_pathname(len, name, &link))
- return 1;
+ if (cache.track_flags != track_flags ||
+ cache.prefix_len_stat_func != prefix_len_stat_func) {
+ /*
+ * As a safeguard we clear the cache if the values of
+ * track_flags and/or prefix_len_stat_func does not
+ * match with the last supplied values.
+ */
+ reset_lstat_cache(track_flags, prefix_len_stat_func);
+ match_len = last_slash = 0;
+ } else {
+ /*
+ * Check to see if we have a match from the cache for
+ * the 2 "excluding" path types.
+ */
+ match_len = last_slash =
+ longest_match_lstat_cache(len, name, &previous_slash);
+ match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
+ if (match_flags && match_len == cache.len)
+ return match_flags;
+ /*
+ * If we now have match_len > 0, we would know that
+ * the matched part will always be a directory.
+ *
+ * Also, if we are tracking directories and 'name' is
+ * a substring of the cache on a path component basis,
+ * we can return immediately.
+ */
+ match_flags = track_flags & FL_DIR;
+ if (match_flags && len == match_len)
+ return match_flags;
+ }
/*
- * Get rid of the last known directory part
+ * Okay, no match from the cache so far, so now we have to
+ * check the rest of the path components.
*/
- known_dir = match_pathname(len, name, &nonlink);
-
- while ((sp = strchr(name + known_dir + 1, '/')) != NULL) {
- int thislen = sp - name ;
- memcpy(path, name, thislen);
- path[thislen] = 0;
-
- if (lstat(path, &st))
- return 0;
- if (S_ISDIR(st.st_mode)) {
- set_pathname(thislen, path, &nonlink);
- known_dir = thislen;
+ ret_flags = FL_DIR;
+ last_slash_dir = last_slash;
+ max_len = len < PATH_MAX ? len : PATH_MAX;
+ while (match_len < max_len) {
+ do {
+ cache.path[match_len] = name[match_len];
+ match_len++;
+ } while (match_len < max_len && name[match_len] != '/');
+ if (match_len >= max_len && !(track_flags & FL_FULLPATH))
+ break;
+ last_slash = match_len;
+ cache.path[last_slash] = '\0';
+
+ if (last_slash <= prefix_len_stat_func)
+ ret = stat(cache.path, &st);
+ else
+ ret = lstat(cache.path, &st);
+
+ if (ret) {
+ ret_flags = FL_LSTATERR;
+ if (errno == ENOENT)
+ ret_flags |= FL_NOENT;
+ } else if (S_ISDIR(st.st_mode)) {
+ last_slash_dir = last_slash;
continue;
- }
- if (S_ISLNK(st.st_mode)) {
- set_pathname(thislen, path, &link);
- return 1;
+ } else if (S_ISLNK(st.st_mode)) {
+ ret_flags = FL_SYMLINK;
+ } else {
+ ret_flags = FL_ERR;
}
break;
}
- return 0;
+
+ /*
+ * At the end update the cache. Note that max 3 different
+ * path types, FL_NOENT, FL_SYMLINK and FL_DIR, can be cached
+ * for the moment!
+ */
+ save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
+ if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
+ cache.path[last_slash] = '\0';
+ cache.len = last_slash;
+ cache.flags = save_flags;
+ } else if (track_flags & FL_DIR &&
+ last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
+ /*
+ * We have a separate test for the directory case,
+ * since it could be that we have found a symlink or a
+ * non-existing directory and the track_flags says
+ * that we cannot cache this fact, so the cache would
+ * then have been left empty in this case.
+ *
+ * But if we are allowed to track real directories, we
+ * can still cache the path components before the last
+ * one (the found symlink or non-existing component).
+ */
+ cache.path[last_slash_dir] = '\0';
+ cache.len = last_slash_dir;
+ cache.flags = FL_DIR;
+ } else {
+ reset_lstat_cache(track_flags, prefix_len_stat_func);
+ }
+ return ret_flags;
+}
+
+/*
+ * Invalidate the given 'name' from the cache, if 'name' matches
+ * completely with the cache.
+ */
+void invalidate_lstat_cache(int len, const char *name)
+{
+ int match_len, previous_slash;
+
+ match_len = longest_match_lstat_cache(len, name, &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.track_flags,
+ cache.prefix_len_stat_func);
+ }
+}
+
+/*
+ * Completely clear the contents of the cache
+ */
+void clear_lstat_cache(void)
+{
+ reset_lstat_cache(0, 0);
+}
+
+#define USE_ONLY_LSTAT 0
+
+/*
+ * Return non-zero if path 'name' has a leading symlink component
+ */
+int has_symlink_leading_path(int len, const char *name)
+{
+ return lstat_cache(len, name,
+ FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
+ FL_SYMLINK;
+}
+
+/*
+ * Return non-zero if path 'name' has a leading symlink component or
+ * if some leading path component does not exists.
+ */
+int has_symlink_or_noent_leading_path(int len, const char *name)
+{
+ return lstat_cache(len, name,
+ FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
+ (FL_SYMLINK|FL_NOENT);
+}
+
+/*
+ * Return non-zero if all path components of 'name' exists as a
+ * directory. If prefix_len > 0, we will test with the stat()
+ * function instead of the lstat() function for a prefix length of
+ * 'prefix_len', thus we then allow for symlinks in the prefix part as
+ * long as those points to real existing directories.
+ */
+int has_dirs_only_path(int len, const char *name, int prefix_len)
+{
+ return lstat_cache(len, name,
+ FL_DIR|FL_FULLPATH, prefix_len) &
+ FL_DIR;
}
is to summarize successes and failures in the test script and
exit with an appropriate error code.
+ - test_tick
+
+ Make commit and tag names consistent by setting the author and
+ committer times to defined stated. Subsequent calls will
+ advance the times by a fixed amount.
+
+ - test_commit <message> [<filename> [<contents>]]
+
+ Creates a commit with the given message, committing the given
+ file with the given contents (default for both is to reuse the
+ message string), and adds a tag (again reusing the message
+ string as name). Calls test_tick to make the SHA-1s
+ reproducible.
+
+ - test_merge <message> <commit-or-tag>
+
+ Merges the given rev using the given message. Like test_commit,
+ creates a tag and calls test_tick before committing.
Tips for Writing Tests
----------------------
--- /dev/null
+#!/bin/sh
+
+# After setting the fake editor with this function, you can
+#
+# - 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
+#
+# "[<lineno1>] [<lineno2>]..."
+#
+# If a line number is prefixed with "squash" or "edit", the respective line's
+# command will be replaced with the specified one.
+
+set_fake_editor () {
+ echo "#!$SHELL_PATH" >fake-editor.sh
+ cat >> fake-editor.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+ test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+ test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
+ exit
+ ;;
+esac
+test -z "$EXPECT_COUNT" ||
+ test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+ exit
+test -z "$FAKE_LINES" && exit
+grep -v '^#' < "$1" > "$1".tmp
+rm -f "$1"
+cat "$1".tmp
+action=pick
+for line in $FAKE_LINES; do
+ case $line in
+ squash|edit)
+ action="$line";;
+ *)
+ 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
+EOF
+
+ test_set_editor "$(pwd)/fake-editor.sh"
+ chmod a+x fake-editor.sh
+}
--- /dev/null
+#!/bin/sh
+
+test_description='signals work as we expect'
+. ./test-lib.sh
+
+cat >expect <<EOF
+three
+two
+one
+EOF
+
+test_expect_success 'sigchain works' '
+ test-sigchain >actual
+ case "$?" in
+ 143) true ;; # POSIX w/ SIGTERM=15
+ 3) true ;; # Windows
+ *) false ;;
+ esac &&
+ test_cmp expect actual
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='test @{-N} syntax'
+
+. ./test-lib.sh
+
+
+make_commit () {
+ echo "$1" > "$1" &&
+ git add "$1" &&
+ git commit -m "$1"
+}
+
+
+test_expect_success 'setup' '
+
+ make_commit 1 &&
+ git branch side &&
+ make_commit 2 &&
+ make_commit 3 &&
+ git checkout side &&
+ make_commit 4 &&
+ git merge master &&
+ git checkout master
+
+'
+
+# 1 -- 2 -- 3 master
+# \ \
+# \ \
+# --- 4 --- 5 side
+#
+# and 'side' should be the last branch
+
+test_rev_equivalent () {
+
+ git rev-parse "$1" > expect &&
+ git rev-parse "$2" > output &&
+ test_cmp expect output
+
+}
+
+test_expect_success '@{-1} works' '
+ test_rev_equivalent side @{-1}
+'
+
+test_expect_success '@{-1}~2 works' '
+ test_rev_equivalent side~2 @{-1}~2
+'
+
+test_expect_success '@{-1}^2 works' '
+ test_rev_equivalent side^2 @{-1}^2
+'
+
+test_expect_success '@{-1}@{1} works' '
+ test_rev_equivalent side@{1} @{-1}@{1}
+'
+
+test_expect_success '@{-2} works' '
+ test_rev_equivalent master @{-2}
+'
+
+test_expect_success '@{-3} fails' '
+ test_must_fail git rev-parse @{-3}
+'
+
+test_done
+
+
--- /dev/null
+#!/bin/sh
+
+test_description='checkout can switch to last branch'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo hello >world &&
+ git add world &&
+ git commit -m initial &&
+ git branch other &&
+ echo "hello again" >>world &&
+ git add world &&
+ git commit -m second
+'
+
+test_expect_success '"checkout -" does not work initially' '
+ test_must_fail git checkout -
+'
+
+test_expect_success 'first branch switch' '
+ git checkout other
+'
+
+test_expect_success '"checkout -" switches back' '
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" switches forth' '
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success 'detach HEAD' '
+ git checkout $(git rev-parse HEAD)
+'
+
+test_expect_success '"checkout -" attaches again' '
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/other"
+'
+
+test_expect_success '"checkout -" detaches again' '
+ git checkout - &&
+ test "z$(git rev-parse HEAD)" = "z$(git rev-parse other)" &&
+ test_must_fail git symbolic-ref HEAD
+'
+
+test_expect_success 'more switches' '
+ for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
+ do
+ git checkout -b branch$i
+ done
+'
+
+more_switches () {
+ for i in 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
+ do
+ git checkout branch$i
+ done
+}
+
+test_expect_success 'switch to the last' '
+ more_switches &&
+ git checkout @{-1} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch2"
+'
+
+test_expect_success 'switch to second from the last' '
+ more_switches &&
+ git checkout @{-2} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch3"
+'
+
+test_expect_success 'switch to third from the last' '
+ more_switches &&
+ git checkout @{-3} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch4"
+'
+
+test_expect_success 'switch to fourth from the last' '
+ more_switches &&
+ git checkout @{-4} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch5"
+'
+
+test_expect_success 'switch to twelfth from the last' '
+ more_switches &&
+ git checkout @{-12} &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
+'
+
+test_done
only the updates to dir/sub.
Also tested are "git add -u" without limiting, and "git add -u"
-without contents changes.'
+without contents changes, and other conditions'
. ./test-lib.sh
'
+test_expect_success 'add -u resolves unmerged paths' '
+ git reset --hard &&
+ one=$(echo 1 | git hash-object -w --stdin) &&
+ two=$(echo 2 | git hash-object -w --stdin) &&
+ three=$(echo 3 | git hash-object -w --stdin) &&
+ {
+ for path in path1 path2
+ do
+ echo "100644 $one 1 $path"
+ echo "100644 $two 2 $path"
+ echo "100644 $three 3 $path"
+ done
+ echo "100644 $one 1 path3"
+ echo "100644 $one 1 path4"
+ echo "100644 $one 3 path5"
+ echo "100644 $one 3 path6"
+ } |
+ git update-index --index-info &&
+ echo 3 >path1 &&
+ echo 2 >path3 &&
+ echo 2 >path5 &&
+ git add -u &&
+ git ls-files -s "path?" >actual &&
+ {
+ echo "100644 $three 0 path1"
+ echo "100644 $one 1 path3"
+ echo "100644 $one 1 path4"
+ echo "100644 $one 3 path5"
+ echo "100644 $one 3 path6"
+ } >expect &&
+ test_cmp expect actual &&
+
+ # Bonus tests. Explicit resolving
+ git add path3 path5 &&
+ test_must_fail git add path4 &&
+ test_must_fail git add path6 &&
+ git rm path4 &&
+ git rm path6 &&
+
+ git ls-files -s "path?" >actual &&
+ {
+ echo "100644 $three 0 path1"
+ echo "100644 $two 0 path3"
+ echo "100644 $two 0 path5"
+ } >expect
+
+'
+
test_done
'
. ./test-lib.sh
+. ../lib-rebase.sh
+
+set_fake_editor
+
# set up two branches like this:
#
# A - B - C - D - E
git tag I
'
-echo "#!$SHELL_PATH" >fake-editor.sh
-cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
- test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
- test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
- exit
- ;;
-esac
-test -z "$EXPECT_COUNT" ||
- test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
- exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
- case $line in
- squash|edit)
- action="$line";;
- *)
- 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
-EOF
-
-test_set_editor "$(pwd)/fake-editor.sh"
-chmod a+x fake-editor.sh
-
test_expect_success 'no changes are a nop' '
git rebase -i F &&
test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch2" &&
'
+test_expect_success 'submodule rebase setup' '
+ git checkout A &&
+ mkdir sub &&
+ (
+ cd sub && git init && >elif &&
+ git add elif && git commit -m "submodule initial"
+ ) &&
+ echo 1 >file1 &&
+ git add file1 sub
+ test_tick &&
+ git commit -m "One" &&
+ echo 2 >file1 &&
+ test_tick &&
+ git commit -a -m "Two" &&
+ (
+ cd sub && echo 3 >elif &&
+ git commit -a -m "submodule second"
+ ) &&
+ test_tick &&
+ git commit -a -m "Three changes submodule"
+'
+
+test_expect_success 'submodule rebase -i' '
+ FAKE_LINES="1 squash 2 3" git rebase -i A
+'
+
test_done
test_expect_success 'pre-rebase hook stops rebase (2)' '
git checkout test &&
git reset --hard side &&
- EDITOR=true test_must_fail git rebase -i master &&
+ (
+ EDITOR=:
+ export EDITOR
+ test_must_fail git rebase -i master
+ ) &&
test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
test 0 = $(git rev-list HEAD...side | wc -l)
'
# where B, D and G touch the same file.
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 &&
- git tag E &&
- 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 &&
- git tag G &&
- : > file5 &&
- git add file5 &&
- test_tick &&
- git commit -m H &&
- git tag H &&
- git checkout -b branch2 F &&
- : > file6 &&
- git add file6 &&
- test_tick &&
- git commit -m I &&
- git tag I
+ test_commit A file1 &&
+ test_commit B file1 1 &&
+ test_commit C file2 &&
+ test_commit D file1 2 &&
+ test_commit E file3 &&
+ git checkout A &&
+ test_commit F file4 &&
+ test_commit G file1 3 &&
+ test_commit H file5 &&
+ git checkout F &&
+ test_commit I file6
'
# A - B - C - D - E
# I -- G2 -- J -- K I -- K
# G2 = same changes as G
test_expect_success 'skip same-resolution merges with -p' '
- git checkout branch1 &&
+ git checkout H &&
! git merge E &&
- echo 23 > file1 &&
- git add file1 &&
- git commit -m L &&
- git checkout branch2 &&
- echo 3 > file1 &&
- git commit -a -m G2 &&
+ test_commit L file1 23 &&
+ git checkout I &&
+ test_commit G2 file1 3 &&
! git merge E &&
- echo 23 > file1 &&
- git add file1 &&
- git commit -m J &&
- echo file7 > file7 &&
- git add file7 &&
- git commit -m K &&
- GIT_EDITOR=: git rebase -i -p branch1 &&
- test $(git rev-parse branch2^^) = $(git rev-parse branch1) &&
+ test_commit J file1 23 &&
+ test_commit K file7 file7 &&
+ git rebase -i -p L &&
+ test $(git rev-parse HEAD^^) = $(git rev-parse L) &&
test "23" = "$(cat file1)" &&
- test "" = "$(cat file6)" &&
- test "file7" = "$(cat file7)" &&
-
- git checkout branch1 &&
- git reset --hard H &&
- git checkout branch2 &&
- git reset --hard I
+ test "I" = "$(cat file6)" &&
+ test "file7" = "$(cat file7)"
'
# A - B - C - D - E
# \ \ \
-# F - G - H -- L \ --> L
-# \ | \
-# I -- G2 -- J -- K I -- G2 -- K
+# F - G - H -- L2 \ --> L2
+# \ | \
+# I -- G3 --- J2 -- K2 I -- G3 -- K2
# G2 = different changes as G
test_expect_success 'keep different-resolution merges with -p' '
- git checkout branch1 &&
+ git checkout H &&
! git merge E &&
- echo 23 > file1 &&
- git add file1 &&
- git commit -m L &&
- git checkout branch2 &&
- echo 4 > file1 &&
- git commit -a -m G2 &&
+ test_commit L2 file1 23 &&
+ git checkout I &&
+ test_commit G3 file1 4 &&
! git merge E &&
- echo 24 > file1 &&
- git add file1 &&
- git commit -m J &&
- echo file7 > file7 &&
- git add file7 &&
- git commit -m K &&
- ! GIT_EDITOR=: git rebase -i -p branch1 &&
+ test_commit J2 file1 24 &&
+ test_commit K2 file7 file7 &&
+ test_must_fail git rebase -i -p L2 &&
echo 234 > file1 &&
git add file1 &&
- GIT_EDITOR=: git rebase --continue &&
- test $(git rev-parse branch2^^^) = $(git rev-parse branch1) &&
+ git rebase --continue &&
+ test $(git rev-parse HEAD^^^) = $(git rev-parse L2) &&
test "234" = "$(cat file1)" &&
- test "" = "$(cat file6)" &&
- test "file7" = "$(cat file7)" &&
-
- git checkout branch1 &&
- git reset --hard H &&
- git checkout branch2 &&
- git reset --hard I
+ test "I" = "$(cat file6)" &&
+ test "file7" = "$(cat file7)"
'
test_done
test_description='git rebase preserve merges
-This test runs git rebase with and tries to squash a commit from after a merge
-to before the merge.
+This test runs git rebase with -p and tries to squash a commit from after
+a merge to before the merge.
'
. ./test-lib.sh
-# Copy/paste from t3404-rebase-interactive.sh
-echo "#!$SHELL_PATH" >fake-editor.sh
-cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
- test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
- test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
- exit
- ;;
-esac
-test -z "$EXPECT_COUNT" ||
- test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
- exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
- case $line in
- squash|edit)
- action="$line";;
- *)
- 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
-EOF
+. ../lib-rebase.sh
-test_set_editor "$(pwd)/fake-editor.sh"
-chmod a+x fake-editor.sh
+set_fake_editor
# set up two branches like this:
#
# -- C1 --
test_expect_success 'setup' '
- touch a &&
- touch b &&
- git add a &&
- git commit -m A1 &&
- git tag A1
- git add b &&
- git commit -m B1 &&
- git tag B1 &&
- git checkout -b branch &&
- touch c &&
- git add c &&
- git commit -m C1 &&
- git checkout master &&
- touch d &&
- git add d &&
- git commit -m D1 &&
- git merge branch &&
- touch f &&
- git add f &&
- git commit -m F1 &&
- git tag F1
+ test_commit A1 &&
+ test_commit B1 &&
+ test_commit C1 &&
+ git reset --hard B1 &&
+ test_commit D1 &&
+ test_merge E1 C1 &&
+ test_commit F1
'
# Should result in:
#
test_expect_success 'squash F1 into D1' '
FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
- test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse C1)" &&
test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
git tag E2
'
# And rebase G1..M1 onto E2
test_expect_success 'rebase two levels of merge' '
- git checkout -b branch2 A1 &&
- touch g &&
- git add g &&
- git commit -m G1 &&
- git checkout -b branch3 &&
- touch h
- git add h &&
- git commit -m H1 &&
- git checkout -b branch4 &&
- touch i &&
- git add i &&
- git commit -m I1 &&
- git tag I1 &&
- git checkout branch3 &&
- touch j &&
- git add j &&
- git commit -m J1 &&
- git merge I1 --no-commit &&
- git commit -m K1 &&
- git tag K1 &&
- git checkout branch2 &&
- touch l &&
- git add l &&
- git commit -m L1 &&
- git merge K1 --no-commit &&
- git commit -m M1 &&
+ test_commit G1 &&
+ test_commit H1 &&
+ test_commit I1 &&
+ git checkout -b branch3 H1 &&
+ test_commit J1 &&
+ test_merge K1 I1 &&
+ git checkout -b branch2 G1 &&
+ test_commit L1 &&
+ test_merge M1 K1 &&
GIT_EDITOR=: git rebase -i -p E2 &&
test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
'
. ./test-lib.sh
+log_with_names () {
+ git rev-list --topo-order --parents --pretty="tformat:%s" HEAD |
+ git name-rev --stdin --name-only --refs=refs/heads/$1
+}
+
+
test_expect_success 'prepare repository' '
- echo 1 > A &&
- git add A &&
- git commit -m 1 &&
- echo 2 > A &&
- git add A &&
- git commit -m 2 &&
+ test_commit 1 A &&
+ test_commit 2 A &&
git symbolic-ref HEAD refs/heads/other &&
rm .git/index &&
- echo 3 > B &&
- git add B &&
- git commit -m 3 &&
- echo 1 > A &&
- git add A &&
- git commit -m 1b &&
- echo 4 > B &&
- git add B &&
- git commit -m 4
+ test_commit 3 B &&
+ test_commit 1b A 1 &&
+ test_commit 4 B
'
test_expect_success 'rebase --root expects --onto' '
test_expect_success 'rebase -i --root --onto <newbase>' '
git checkout -b work3 other &&
- GIT_EDITOR=: git rebase -i --root --onto master &&
+ git rebase -i --root --onto master &&
git log --pretty=tformat:"%s" > rebased3 &&
test_cmp expect rebased3
'
test_expect_success 'rebase -i --root --onto <newbase> <branch>' '
git branch work4 other &&
- GIT_EDITOR=: git rebase -i --root --onto master work4 &&
+ git rebase -i --root --onto master work4 &&
git log --pretty=tformat:"%s" > rebased4 &&
test_cmp expect rebased4
'
test_expect_success 'rebase -i -p with linear history' '
git checkout -b work5 other &&
- GIT_EDITOR=: git rebase -i -p --root --onto master &&
+ git rebase -i -p --root --onto master &&
git log --pretty=tformat:"%s" > rebased5 &&
test_cmp expect rebased5
'
test_expect_success 'set up merge history' '
git checkout other^ &&
git checkout -b side &&
- echo 5 > C &&
- git add C &&
- git commit -m 5 &&
+ test_commit 5 C &&
git checkout other &&
git merge side
'
-sed 's/#/ /g' > expect-side <<'EOF'
-* Merge branch 'side' into other
-|\##
-| * 5
-* | 4
-|/##
-* 3
-* 2
-* 1
+cat > expect-side <<'EOF'
+commit work6 work6~1 work6^2
+Merge branch 'side' into other
+commit work6^2 work6~2
+5
+commit work6~1 work6~2
+4
+commit work6~2 work6~3
+3
+commit work6~3 work6~4
+2
+commit work6~4
+1
EOF
test_expect_success 'rebase -i -p with merge' '
git checkout -b work6 other &&
- GIT_EDITOR=: git rebase -i -p --root --onto master &&
- git log --graph --topo-order --pretty=tformat:"%s" > rebased6 &&
+ git rebase -i -p --root --onto master &&
+ log_with_names work6 > rebased6 &&
test_cmp expect-side rebased6
'
git symbolic-ref HEAD refs/heads/third &&
rm .git/index &&
rm A B C &&
- echo 6 > D &&
- git add D &&
- git commit -m 6 &&
+ test_commit 6 D &&
git checkout other &&
git merge third
'
-sed 's/#/ /g' > expect-third <<'EOF'
-* Merge branch 'third' into other
-|\##
-| * 6
-* | Merge branch 'side' into other
-|\ \##
-| * | 5
-* | | 4
-|/ /##
-* | 3
-|/##
-* 2
-* 1
+cat > expect-third <<'EOF'
+commit work7 work7~1 work7^2
+Merge branch 'third' into other
+commit work7^2 work7~4
+6
+commit work7~1 work7~2 work7~1^2
+Merge branch 'side' into other
+commit work7~1^2 work7~3
+5
+commit work7~2 work7~3
+4
+commit work7~3 work7~4
+3
+commit work7~4 work7~5
+2
+commit work7~5
+1
EOF
test_expect_success 'rebase -i -p with two roots' '
git checkout -b work7 other &&
- GIT_EDITOR=: git rebase -i -p --root --onto master &&
- git log --graph --topo-order --pretty=tformat:"%s" > rebased7 &&
+ git rebase -i -p --root --onto master &&
+ log_with_names work7 > rebased7 &&
test_cmp expect-third rebased7
'
test_expect_success 'pre-rebase hook stops rebase' '
git checkout -b stops1 other &&
- GIT_EDITOR=: test_must_fail git rebase --root --onto master &&
+ test_must_fail git rebase --root --onto master &&
test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops1
test 0 = $(git rev-list other...stops1 | wc -l)
'
test_expect_success 'pre-rebase hook stops rebase -i' '
git checkout -b stops2 other &&
- GIT_EDITOR=: test_must_fail git rebase --root --onto master &&
+ test_must_fail git rebase --root --onto master &&
test "z$(git symbolic-ref HEAD)" = zrefs/heads/stops2
test 0 = $(git rev-list other...stops2 | wc -l)
'
+test_expect_success 'remove pre-rebase hook' '
+ rm -f .git/hooks/pre-rebase
+'
+
+test_expect_success 'set up a conflict' '
+ git checkout master &&
+ echo conflict > B &&
+ git add B &&
+ git commit -m conflict
+'
+
+test_expect_success 'rebase --root with conflict (first part)' '
+ git checkout -b conflict1 other &&
+ test_must_fail git rebase --root --onto master &&
+ git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+ echo 3 > B &&
+ git add B
+'
+
+cat > expect-conflict <<EOF
+6
+5
+4
+3
+conflict
+2
+1
+EOF
+
+test_expect_success 'rebase --root with conflict (second part)' '
+ git rebase --continue &&
+ git log --pretty=tformat:"%s" > conflict1 &&
+ test_cmp expect-conflict conflict1
+'
+
+test_expect_success 'rebase -i --root with conflict (first part)' '
+ git checkout -b conflict2 other &&
+ test_must_fail git rebase -i --root --onto master &&
+ git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+ echo 3 > B &&
+ git add B
+'
+
+test_expect_success 'rebase -i --root with conflict (second part)' '
+ git rebase --continue &&
+ git log --pretty=tformat:"%s" > conflict2 &&
+ test_cmp expect-conflict conflict2
+'
+
+cat >expect-conflict-p <<\EOF
+commit conflict3 conflict3~1 conflict3^2
+Merge branch 'third' into other
+commit conflict3^2 conflict3~4
+6
+commit conflict3~1 conflict3~2 conflict3~1^2
+Merge branch 'side' into other
+commit conflict3~1^2 conflict3~3
+5
+commit conflict3~2 conflict3~3
+4
+commit conflict3~3 conflict3~4
+3
+commit conflict3~4 conflict3~5
+conflict
+commit conflict3~5 conflict3~6
+2
+commit conflict3~6
+1
+EOF
+
+test_expect_success 'rebase -i -p --root with conflict (first part)' '
+ git checkout -b conflict3 other &&
+ test_must_fail git rebase -i -p --root --onto master &&
+ git ls-files -u | grep "B$"
+'
+
+test_expect_success 'fix the conflict' '
+ echo 3 > B &&
+ git add B
+'
+
+test_expect_success 'rebase -i -p --root with conflict (second part)' '
+ git rebase --continue &&
+ log_with_names conflict3 >out &&
+ test_cmp expect-conflict-p out
+'
+
test_done
git diff-index -M -p $tree > current &&
compare_diff_patch current expected'
+test_expect_success \
+ 'diff symlinks with non-existing targets' \
+ 'ln -s narf pinky &&
+ ln -s take\ over brain &&
+ test_must_fail git diff --no-index pinky brain > output 2> output.err &&
+ grep narf output &&
+ ! grep error output.err'
test_done
-1
diff --git a/file b/file
new file mode 120000
-index ad8b3d2..67be421
+index 0000000..67be421
--- /dev/null
+++ b/file
@@ -0,0 +1 @@
--- /dev/null
+#!/bin/sh
+
+test_description='word diff colors'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ git config diff.color.old red
+ git config diff.color.new green
+
+'
+
+decrypt_color () {
+ sed \
+ -e 's/.\[1m/<WHITE>/g' \
+ -e 's/.\[31m/<RED>/g' \
+ -e 's/.\[32m/<GREEN>/g' \
+ -e 's/.\[36m/<BROWN>/g' \
+ -e 's/.\[m/<RESET>/g'
+}
+
+word_diff () {
+ test_must_fail git diff --no-index "$@" pre post > output &&
+ decrypt_color < output > output.decrypted &&
+ test_cmp expect output.decrypted
+}
+
+cat > pre <<\EOF
+h(4)
+
+a = b + c
+EOF
+
+cat > post <<\EOF
+h(4),hh[44]
+
+a = b + c
+
+aa = a
+
+aeff = aeff * ( aaa )
+EOF
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+<RED>h(4)<RESET><GREEN>h(4),hh[44]<RESET>
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+
+test_expect_success 'word diff with runs of whitespace' '
+
+ word_diff --color-words
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh<RESET>[44]
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+cp expect expect.letter-runs-are-words
+
+test_expect_success 'word diff with a regular expression' '
+
+ word_diff --color-words="[a-z]+"
+
+'
+
+test_expect_success 'set a diff driver' '
+ git config diff.testdriver.wordRegex "[^[:space:]]" &&
+ cat <<EOF > .gitattributes
+pre diff=testdriver
+post diff=testdriver
+EOF
+'
+
+test_expect_success 'option overrides .gitattributes' '
+
+ word_diff --color-words="[a-z]+"
+
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4)<GREEN>,hh[44]<RESET>
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa )<RESET>
+EOF
+cp expect expect.non-whitespace-is-word
+
+test_expect_success 'use regex supplied by driver' '
+
+ word_diff --color-words
+
+'
+
+test_expect_success 'set diff.wordRegex option' '
+ git config diff.wordRegex "[[:alnum:]]+"
+'
+
+cp expect.letter-runs-are-words expect
+
+test_expect_success 'command-line overrides config' '
+ word_diff --color-words="[a-z]+"
+'
+
+cp expect.non-whitespace-is-word expect
+
+test_expect_success '.gitattributes override config' '
+ word_diff --color-words
+'
+
+test_expect_success 'remove diff driver regex' '
+ git config --unset diff.testdriver.wordRegex
+'
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 330b04f..5ed8eff 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1,3 +1,7 @@<RESET>
+h(4),<GREEN>hh[44<RESET>]
+<RESET>
+a = b + c<RESET>
+
+<GREEN>aa = a<RESET>
+
+<GREEN>aeff = aeff * ( aaa<RESET> )
+EOF
+
+test_expect_success 'use configured regex' '
+ word_diff --color-words
+'
+
+echo 'aaa (aaa)' > pre
+echo 'aaa (aaa) aaa' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index c29453b..be22f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+aaa (aaa) <GREEN>aaa<RESET>
+EOF
+
+test_expect_success 'test parsing words for newline' '
+
+ word_diff --color-words="a+"
+
+
+'
+
+echo '(:' > pre
+echo '(' > post
+
+cat > expect <<\EOF
+<WHITE>diff --git a/pre b/post<RESET>
+<WHITE>index 289cb9d..2d06f37 100644<RESET>
+<WHITE>--- a/pre<RESET>
+<WHITE>+++ b/post<RESET>
+<BROWN>@@ -1 +1 @@<RESET>
+(<RED>:<RESET>
+EOF
+
+test_expect_success 'test when words are only removed at the end' '
+
+ word_diff --color-words=.
+
+'
+
+test_done
git update-index foo &&
git commit -m "foo back to file" &&
git branch foo-back-to-file &&
+ printf "\0" > foo &&
+ git update-index foo &&
+ git commit -m "foo becomes binary" &&
+ git branch foo-becomes-binary &&
rm -f foo &&
git update-index --remove foo &&
mkdir foo &&
'
test_debug 'cat patch'
+test_expect_success 'binary file becomes symlink' '
+ git checkout -f foo-becomes-binary &&
+ git diff-tree -p --binary HEAD foo-symlinked-to-bar > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
+test_expect_success 'symlink becomes binary file' '
+ git checkout -f foo-symlinked-to-bar &&
+ git diff-tree -p --binary HEAD foo-becomes-binary > patch &&
+ git apply --index < patch
+ '
+test_debug 'cat patch'
+
test_expect_success 'symlink becomes directory' '
git checkout -f foo-symlinked-to-bar &&
'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
last=`cat last` &&
echo total is $last &&
- test `cat last` = 11'
+ test `cat last` = 13'
for mail in `echo 00*`
do
'
done
+
+test_expect_success 'split box with rfc2047 samples' \
+ 'mkdir rfc2047 &&
+ git mailsplit -orfc2047 "$TEST_DIRECTORY"/t5100/rfc2047-samples.mbox \
+ >rfc2047/last &&
+ last=`cat rfc2047/last` &&
+ echo total is $last &&
+ test `cat rfc2047/last` = 11'
+
+for mail in `echo rfc2047/00*`
+do
+ test_expect_success "mailinfo $mail" '
+ git mailinfo -u $mail-msg $mail-patch <$mail >$mail-info &&
+ echo msg &&
+ test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-msg &&
+ echo patch &&
+ test_cmp "$TEST_DIRECTORY"/t5100/empty $mail-patch &&
+ echo info &&
+ test_cmp "$TEST_DIRECTORY"/t5100/rfc2047-info-$(basename $mail) $mail-info
+ '
+done
+
test_expect_success 'respect NULs' '
git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
-Author: A U Thor
+Author: A (zzz) U Thor (Comment)
Email: a.u.thor@example.com
Subject: a commit.
Date: Fri, 9 Jun 2006 00:44:16 -0700
--- /dev/null
+Author: Dmitriy Blinov
+Email: bda@mnsspb.ru
+Subject: Изменён список пакетов необходимых для сборки
+Date: Wed, 12 Nov 2008 17:54:41 +0300
+
--- /dev/null
+Author: A U Thor
+Email: a.u.thor@example.com
+Subject: a patch
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+
--- /dev/null
+textlive-* исправлены на texlive-*
+docutils заменён на python-docutils
+
+Действительно, оказалось, что rest2web вытягивает за собой
+python-docutils. В то время как сам rest2web не нужен.
+
+Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
--- /dev/null
+---
+ howto/build_navy.txt | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/howto/build_navy.txt b/howto/build_navy.txt
+index 3fd3afb..0ee807e 100644
+--- a/howto/build_navy.txt
++++ b/howto/build_navy.txt
+@@ -119,8 +119,8 @@
+ - libxv-dev
+ - libusplash-dev
+ - latex-make
+- - textlive-lang-cyrillic
+- - textlive-latex-extra
++ - texlive-lang-cyrillic
++ - texlive-latex-extra
+ - dia
+ - python-pyrex
+ - libtool
+@@ -128,7 +128,7 @@
+ - sox
+ - cython
+ - imagemagick
+- - docutils
++ - python-docutils
+
+ #. на машине dinar: добавить свой открытый ssh-ключ в authorized_keys2 пользователя ddev
+ #. на своей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно следующим образом::
+--
+1.5.6.5
--- /dev/null
+Author: Keith Moore
+Email: moore@cs.utk.edu
+Subject: If you can read this you understand the example.
+
--- /dev/null
+Author: Olle Järnefors
+Email: ojarnef@admin.kth.se
+Subject: Time for ISO 10646?
+
--- /dev/null
+Author: Patrik Fältström
+Email: paf@nada.kth.se
+Subject: RFC-HDR care and feeding
+
--- /dev/null
+Author: Nathaniel Borenstein (םולש ןב ילטפנ)
+Email: nsb@thumper.bellcore.com
+Subject: Test of new header generator
+
--- /dev/null
+Subject: (a)
+
--- /dev/null
+Subject: (a b)
+
--- /dev/null
+Subject: (ab)
+
--- /dev/null
+Subject: (ab)
+
--- /dev/null
+Subject: (ab)
+
--- /dev/null
+Subject: (a b)
+
--- /dev/null
+Subject: (a b)
+
--- /dev/null
+From nobody Mon Sep 17 00:00:00 2001
+From: =?US-ASCII?Q?Keith_Moore?= <moore@cs.utk.edu>
+To: =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= <keld@dkuug.dk>
+CC: =?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>
+Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
+ =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=
+
+From nobody Mon Sep 17 00:00:00 2001
+From: =?ISO-8859-1?Q?Olle_J=E4rnefors?= <ojarnef@admin.kth.se>
+To: ietf-822@dimacs.rutgers.edu, ojarnef@admin.kth.se
+Subject: Time for ISO 10646?
+
+From nobody Mon Sep 17 00:00:00 2001
+To: Dave Crocker <dcrocker@mordor.stanford.edu>
+Cc: ietf-822@dimacs.rutgers.edu, paf@comsol.se
+From: =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@nada.kth.se>
+Subject: Re: RFC-HDR care and feeding
+
+From nobody Mon Sep 17 00:00:00 2001
+From: Nathaniel Borenstein <nsb@thumper.bellcore.com>
+ (=?iso-8859-8?b?7eXs+SDv4SDp7Oj08A==?=)
+To: Greg Vaudreuil <gvaudre@NRI.Reston.VA.US>, Ned Freed
+ <ned@innosoft.com>, Keith Moore <moore@cs.utk.edu>
+Subject: Test of new header generator
+MIME-Version: 1.0
+Content-type: text/plain; charset=ISO-8859-1
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a?= b)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a?=
+ =?ISO-8859-1?Q?b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a_b?=)
+
+From nobody Mon Sep 17 00:00:00 2001
+Subject: (=?ISO-8859-1?Q?a?= =?ISO-8859-2?Q?_b?=)
From nobody Mon Sep 17 00:00:00 2001
-From: A U Thor <a.u.thor@example.com>
+From: A (zzz)
+ U
+ Thor
+ <a.u.thor@example.com> (Comment)
Date: Fri, 9 Jun 2006 00:44:16 -0700
Subject: [PATCH] a commit.
--=-=-=--
+From bda@mnsspb.ru Wed Nov 12 17:54:41 2008
+From: Dmitriy Blinov <bda@mnsspb.ru>
+To: navy-patches@dinar.mns.mnsspb.ru
+Date: Wed, 12 Nov 2008 17:54:41 +0300
+Message-Id: <1226501681-24923-1-git-send-email-bda@mnsspb.ru>
+X-Mailer: git-send-email 1.5.6.5
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=utf-8
+Content-Transfer-Encoding: 8bit
+Subject: [Navy-patches] [PATCH]
+ =?utf-8?b?0JjQt9C80LXQvdGR0L0g0YHQv9C40YHQvtC6INC/0LA=?=
+ =?utf-8?b?0LrQtdGC0L7QsiDQvdC10L7QsdGF0L7QtNC40LzRi9GFINC00LvRjyA=?=
+ =?utf-8?b?0YHQsdC+0YDQutC4?=
+
+textlive-* исправлены на texlive-*
+docutils заменён на python-docutils
+
+Действительно, оказалось, что rest2web вытягивает за собой
+python-docutils. В то время как сам rest2web не нужен.
+
+Signed-off-by: Dmitriy Blinov <bda@mnsspb.ru>
+---
+ howto/build_navy.txt | 6 +++---
+ 1 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/howto/build_navy.txt b/howto/build_navy.txt
+index 3fd3afb..0ee807e 100644
+--- a/howto/build_navy.txt
++++ b/howto/build_navy.txt
+@@ -119,8 +119,8 @@
+ - libxv-dev
+ - libusplash-dev
+ - latex-make
+- - textlive-lang-cyrillic
+- - textlive-latex-extra
++ - texlive-lang-cyrillic
++ - texlive-latex-extra
+ - dia
+ - python-pyrex
+ - libtool
+@@ -128,7 +128,7 @@
+ - sox
+ - cython
+ - imagemagick
+- - docutils
++ - python-docutils
+
+ #. на машине dinar: добавить свой открытый ssh-ключ в authorized_keys2 пользователя ddev
+ #. на своей машине: отредактировать /etc/sudoers (команда ``visudo``) примерно следующим образом::
+--
+1.5.6.5
+From nobody Mon Sep 17 00:00:00 2001
+From: <a.u.thor@example.com> (A U Thor)
+Date: Fri, 9 Jun 2006 00:44:16 -0700
+Subject: [PATCH] a patch
+
--- /dev/null
+#!/bin/sh
+
+test_description='push to a repository that borrows from elsewhere'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir alice-pub &&
+ (
+ cd alice-pub &&
+ GIT_DIR=. git init
+ ) &&
+ mkdir alice-work &&
+ (
+ cd alice-work &&
+ git init &&
+ >file &&
+ git add . &&
+ git commit -m initial &&
+ git push ../alice-pub master
+ ) &&
+
+ # Project Bob is a fork of project Alice
+ mkdir bob-pub &&
+ (
+ cd bob-pub &&
+ GIT_DIR=. git init &&
+ mkdir -p objects/info &&
+ echo ../../alice-pub/objects >objects/info/alternates
+ ) &&
+ git clone alice-pub bob-work &&
+ (
+ cd bob-work &&
+ git push ../bob-pub master
+ )
+'
+
+test_expect_success 'alice works and pushes' '
+ (
+ cd alice-work &&
+ echo more >file &&
+ git commit -a -m second &&
+ git push ../alice-pub
+ )
+'
+
+test_expect_success 'bob fetches from alice, works and pushes' '
+ (
+ # Bob acquires what Alice did in his work tree first.
+ # Even though these objects are not directly in
+ # the public repository of Bob, this push does not
+ # need to send the commit Bob received from Alice
+ # to his public repository, as all the object Alice
+ # has at her public repository are available to it
+ # via its alternates.
+ cd bob-work &&
+ git pull ../alice-pub master &&
+ echo more bob >file &&
+ git commit -a -m third &&
+ git push ../bob-pub
+ ) &&
+
+ # Check that the second commit by Alice is not sent
+ # to ../bob-pub
+ (
+ cd bob-pub &&
+ second=$(git rev-parse HEAD^) &&
+ rm -f objects/info/alternates &&
+ test_must_fail git cat-file -t $second &&
+ echo ../../alice-pub/objects >objects/info/alternates
+ )
+'
+
+test_expect_success 'clean-up in case the previous failed' '
+ (
+ cd bob-pub &&
+ echo ../../alice-pub/objects >objects/info/alternates
+ )
+'
+
+test_expect_success 'alice works and pushes again' '
+ (
+ # Alice does not care what Bob does. She does not
+ # even have to be aware of his existence. She just
+ # keeps working and pushing
+ cd alice-work &&
+ echo more alice >file &&
+ git commit -a -m fourth &&
+ git push ../alice-pub
+ )
+'
+
+test_expect_success 'bob works and pushes' '
+ (
+ # This time Bob does not pull from Alice, and
+ # the master branch at her public repository points
+ # at a commit Bob does not know about. This should
+ # not prevent the push by Bob from succeeding.
+ cd bob-work &&
+ echo yet more bob >file &&
+ git commit -a -m fifth &&
+ git push ../bob-pub
+ )
+'
+
+test_expect_success 'alice works and pushes yet again' '
+ (
+ # Alice does not care what Bob does. She does not
+ # even have to be aware of his existence. She just
+ # keeps working and pushing
+ cd alice-work &&
+ echo more and more alice >file &&
+ git commit -a -m sixth.1 &&
+ echo more and more alice >>file &&
+ git commit -a -m sixth.2 &&
+ echo more and more alice >>file &&
+ git commit -a -m sixth.3 &&
+ git push ../alice-pub
+ )
+'
+
+test_expect_success 'bob works and pushes again' '
+ (
+ cd alice-pub &&
+ git cat-file commit master >../bob-work/commit
+ )
+ (
+ # This time Bob does not pull from Alice, and
+ # the master branch at her public repository points
+ # at a commit Bob does not fully know about, but
+ # he happens to have the commit object (but not the
+ # necessary tree) in his repository from Alice.
+ # This should not prevent the push by Bob from
+ # succeeding.
+ cd bob-work &&
+ git hash-object -t commit -w commit &&
+ echo even more bob >file &&
+ git commit -a -m seventh &&
+ git push ../bob-pub
+ )
+'
+
+test_done
git clone --bare . x &&
test "$(GIT_CONFIG=a.git/config git config --bool core.bare)" = true &&
test "$(GIT_CONFIG=x/config git config --bool core.bare)" = true
- git bundle create b1.bundle --all HEAD &&
- git bundle create b2.bundle --all &&
+ git bundle create b1.bundle --all &&
+ git bundle create b2.bundle master &&
mkdir dir &&
cp b1.bundle dir/b3
cp b1.bundle b4
test ! -e .git/refs/heads/master
'
+test_expect_success 'clone empty repository' '
+ cd "$D" &&
+ mkdir empty &&
+ (cd empty && git init) &&
+ git clone empty empty-clone &&
+ test_tick &&
+ (cd empty-clone
+ echo "content" >> foo &&
+ git add foo &&
+ git commit -m "Initial commit" &&
+ git push origin master &&
+ expected=$(git rev-parse master) &&
+ actual=$(git --git-dir=../empty/.git rev-parse master) &&
+ test $actual = $expected)
+'
+
test_done
--- /dev/null
+#!/bin/sh
+
+test_description='--all includes detached HEADs'
+
+. ./test-lib.sh
+
+
+commit () {
+ test_tick &&
+ echo $1 > foo &&
+ git add foo &&
+ git commit -m "$1"
+}
+
+test_expect_success 'setup' '
+
+ commit one &&
+ commit two &&
+ git checkout HEAD^ &&
+ commit detached
+
+'
+
+test_expect_success 'rev-list --all lists detached HEAD' '
+
+ test 3 = $(git rev-list --all | wc -l)
+
+'
+
+test_expect_success 'repack does not lose detached HEAD' '
+
+ git gc &&
+ git prune --expire=now &&
+ git show HEAD
+
+'
+
+test_done
git cat-file tag tag-from-subdir-2 | grep "in sub directory"
'
+# create a few more commits to test --contains
+
+hash1=$(git rev-parse HEAD)
+
+test_expect_success 'creating second commit and tag' '
+ echo foo-2.0 >foo &&
+ git add foo &&
+ git commit -m second
+ git tag v2.0
+'
+
+hash2=$(git rev-parse HEAD)
+
+test_expect_success 'creating third commit without tag' '
+ echo foo-dev >foo &&
+ git add foo &&
+ git commit -m third
+'
+
+hash3=$(git rev-parse HEAD)
+
+# simple linear checks of --continue
+
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+EOF
+
+test_expect_success 'checking that first commit is in all tags (hash)' "
+ git tag -l --contains $hash1 v* >actual
+ test_cmp expected actual
+"
+
+# other ways of specifying the commit
+test_expect_success 'checking that first commit is in all tags (tag)' "
+ git tag -l --contains v1.0 v* >actual
+ test_cmp expected actual
+"
+
+test_expect_success 'checking that first commit is in all tags (relative)' "
+ git tag -l --contains HEAD~2 v* >actual
+ test_cmp expected actual
+"
+
+cat > expected <<EOF
+v2.0
+EOF
+
+test_expect_success 'checking that second commit only has one tag' "
+ git tag -l --contains $hash2 v* >actual
+ test_cmp expected actual
+"
+
+
+cat > expected <<EOF
+EOF
+
+test_expect_success 'checking that third commit has no tags' "
+ git tag -l --contains $hash3 v* >actual
+ test_cmp expected actual
+"
+
+# how about a simple merge?
+
+test_expect_success 'creating simple branch' '
+ git branch stable v2.0 &&
+ git checkout stable &&
+ echo foo-3.0 > foo &&
+ git commit foo -m fourth
+ git tag v3.0
+'
+
+hash4=$(git rev-parse HEAD)
+
+cat > expected <<EOF
+v3.0
+EOF
+
+test_expect_success 'checking that branch head only has one tag' "
+ git tag -l --contains $hash4 v* >actual
+ test_cmp expected actual
+"
+
+test_expect_success 'merging original branch into this branch' '
+ git merge --strategy=ours master &&
+ git tag v4.0
+'
+
+cat > expected <<EOF
+v4.0
+EOF
+
+test_expect_success 'checking that original branch head has one tag now' "
+ git tag -l --contains $hash3 v* >actual
+ test_cmp expected actual
+"
+
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+v3.0
+v4.0
+EOF
+
+test_expect_success 'checking that initial commit is in all tags' "
+ git tag -l --contains $hash1 v* >actual
+ test_cmp expected actual
+"
+
# mixing modes and options:
test_expect_success 'mixing incompatibles modes and options is forbidden' '
compare_mtimes ()
{
- perl -e 'my $reference = shift;
- foreach my $file (@ARGV) {
- exit(1) unless(-f $file && -M $file == -M $reference);
- }
- exit(0);
- ' -- "$@"
+ read tref rest &&
+ while read t rest; do
+ test "$tref" = "$t" || break
+ done
}
test_expect_success '-A without -d option leaves unreachable objects packed' '
tmppack=".git/objects/pack/tmp_pack" &&
ln "$packfile" "$tmppack" &&
git repack -A -l -d &&
- compare_mtimes "$tmppack" "$fsha1path" "$csha1path" "$tsha1path"
+ test-chmtime -v +0 "$tmppack" "$fsha1path" "$csha1path" "$tsha1path" \
+ > mtimes &&
+ compare_mtimes < mtimes
'
test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2009 Vitaly Shukela
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn property tests'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ svn co "$svnrepo" s &&
+ (
+ cd s &&
+ mkdir qqq www &&
+ echo test_qqq > qqq/test_qqq.txt &&
+ echo test_www > www/test_www.txt &&
+ svn add qqq &&
+ svn add www &&
+ svn commit -m "create some files" &&
+ svn up &&
+ echo hi >> www/test_www.txt &&
+ svn commit -m "modify www/test_www.txt" &&
+ svn up
+ )
+'
+
+test_expect_success 'clone an SVN repository with ignored www directory' '
+ git svn clone --ignore-paths="^www" "$svnrepo" g &&
+ echo test_qqq > expect &&
+ for i in g/*/*.txt; do cat $i >> expect2; done &&
+ test_cmp expect expect2
+'
+
+test_expect_success 'SVN-side change outside of www' '
+ (
+ cd s &&
+ echo b >> qqq/test_qqq.txt &&
+ svn commit -m "SVN-side change outside of www" &&
+ svn up &&
+ svn log -v | fgrep "SVN-side change outside of www"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo' '
+ (
+ cd g &&
+ git svn rebase --ignore-paths="^www" &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'SVN-side change inside of ignored www' '
+ (
+ cd s &&
+ echo zaq >> www/test_www.txt
+ svn commit -m "SVN-side change inside of www/test_www.txt" &&
+ svn up &&
+ svn log -v | fgrep "SVN-side change inside of www/test_www.txt"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo' '
+ (
+ cd g &&
+ git svn rebase --ignore-paths="^www" &&
+ printf "test_qqq\nb\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_expect_success 'SVN-side change in and out of ignored www' '
+ (
+ cd s &&
+ echo cvf >> www/test_www.txt
+ echo ygg >> qqq/test_qqq.txt
+ svn commit -m "SVN-side change in and out of ignored www" &&
+ svn up &&
+ svn log -v | fgrep "SVN-side change in and out of ignored www"
+ )
+'
+
+test_expect_success 'update git svn-cloned repo again' '
+ (
+ cd g &&
+ git svn rebase --ignore-paths="^www" &&
+ printf "test_qqq\nb\nygg\n" > expect &&
+ for i in */*.txt; do cat $i >> expect2; done &&
+ test_cmp expect2 expect &&
+ rm expect expect2
+ )
+'
+
+test_done
export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
}
+# Call test_commit with the arguments "<message> [<file> [<contents>]]"
+#
+# This will commit a file with the given contents and the given commit
+# message. It will also add a tag with <message> as name.
+#
+# Both <file> and <contents> default to <message>.
+
+test_commit () {
+ file=${2:-"$1.t"}
+ echo "${3-$1}" > "$file" &&
+ git add "$file" &&
+ test_tick &&
+ git commit -m "$1" &&
+ git tag "$1"
+}
+
+# Call test_merge with the arguments "<message> <commit>", where <commit>
+# can be a tag pointing to the commit-to-merge.
+
+test_merge () {
+ test_tick &&
+ git merge -m "$1" "$2" &&
+ git tag "$1"
+}
+
# You are not expected to call test_ok_ and test_failure_ directly, use
# the text_expect_* functions instead.
--- /dev/null
+#include "cache.h"
+#include "tree.h"
+#include "cache-tree.h"
+
+
+static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
+{
+ if (it->entry_count < 0)
+ printf("%-40s %s%s (%d subtrees)\n",
+ "invalid", x, pfx, it->subtree_nr);
+ else
+ printf("%s %s%s (%d entries, %d subtrees)\n",
+ sha1_to_hex(it->sha1), x, pfx,
+ it->entry_count, it->subtree_nr);
+}
+
+static int dump_cache_tree(struct cache_tree *it,
+ struct cache_tree *ref,
+ const char *pfx)
+{
+ int i;
+ int errs = 0;
+
+ if (!it || !ref)
+ /* missing in either */
+ return 0;
+
+ if (it->entry_count < 0) {
+ dump_one(it, pfx, "");
+ dump_one(ref, pfx, "#(ref) ");
+ if (it->subtree_nr != ref->subtree_nr)
+ errs = 1;
+ }
+ else {
+ dump_one(it, pfx, "");
+ if (hashcmp(it->sha1, ref->sha1) ||
+ ref->entry_count != it->entry_count ||
+ ref->subtree_nr != it->subtree_nr) {
+ dump_one(ref, pfx, "#(ref) ");
+ errs = 1;
+ }
+ }
+
+ for (i = 0; i < it->subtree_nr; i++) {
+ char path[PATH_MAX];
+ struct cache_tree_sub *down = it->down[i];
+ struct cache_tree_sub *rdwn;
+
+ rdwn = cache_tree_sub(ref, down->name);
+ sprintf(path, "%s%.*s/", pfx, down->namelen, down->name);
+ if (dump_cache_tree(down->cache_tree, rdwn->cache_tree, path))
+ errs = 1;
+ }
+ return errs;
+}
+
+int main(int ac, char **av)
+{
+ struct cache_tree *another = cache_tree();
+ if (read_cache() < 0)
+ die("unable to read index file");
+ cache_tree_update(another, active_cache, active_nr, 0, 1);
+ return dump_cache_tree(active_cache_tree, another, "");
+}
int main(int argc, char **argv)
{
if (argc == 3 && !strcmp(argv[1], "normalize_absolute_path")) {
- char *buf = xmalloc(strlen(argv[2])+1);
+ char *buf = xmalloc(PATH_MAX + 1);
int rv = normalize_absolute_path(buf, argv[2]);
assert(strlen(buf) == rv);
puts(buf);
--- /dev/null
+#include "sigchain.h"
+#include "cache.h"
+
+#define X(f) \
+static void f(int sig) { \
+ puts(#f); \
+ fflush(stdout); \
+ sigchain_pop(sig); \
+ raise(sig); \
+}
+X(one)
+X(two)
+X(three)
+#undef X
+
+int main(int argc, char **argv) {
+ sigchain_push(SIGTERM, one);
+ sigchain_push(SIGTERM, two);
+ sigchain_push(SIGTERM, three);
+ raise(SIGTERM);
+ return 0;
+}
#include "cache.h"
#include "blob.h"
+#include "exec_cmd.h"
static char *create_temp_file(unsigned char *sha1)
{
{
unsigned char sha1[20];
+ git_extract_argv0_path(argv[0]);
+
if (argc != 2)
usage("git-unpack-file <sha1>");
if (get_sha1(argv[1], sha1))
char *cp, *prev;
char *name = ce->name;
- if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+ if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
return;
if (unlink(name))
return;
return ce;
}
-static int unpack_nondirectories(int n, unsigned long mask, unsigned long dirmask, struct cache_entry *src[5],
- const struct name_entry *names, const struct traverse_info *info)
+static int unpack_nondirectories(int n, unsigned long mask,
+ unsigned long dirmask,
+ struct cache_entry **src,
+ const struct name_entry *names,
+ const struct traverse_info *info)
{
int i;
struct unpack_trees_options *o = info->data;
static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, struct name_entry *names, struct traverse_info *info)
{
- struct cache_entry *src[5] = { NULL, };
+ struct cache_entry *src[MAX_UNPACK_TREES + 1] = { NULL, };
struct unpack_trees_options *o = info->data;
const struct name_entry *p = names;
if (o->index_only || o->reset || !o->update)
return 0;
- if (has_symlink_leading_path(ce_namelen(ce), ce->name))
+ if (has_symlink_or_noent_leading_path(ce_namelen(ce), ce->name))
return 0;
if (!lstat(ce->name, &st)) {
#include "cache.h"
+#include "exec_cmd.h"
static const char update_server_info_usage[] =
"git update-server-info [--force]";
if (i != ac)
usage(update_server_info_usage);
+ git_extract_argv0_path(av[0]);
+
setup_git_directory();
return !!update_server_info(force);
int i;
int strict = 0;
+ git_extract_argv0_path(argv[0]);
+
for (i = 1; i < argc; i++) {
char *arg = argv[i];
static int ndrivers;
static int drivers_alloc;
-#define FUNCNAME(name, pattern) \
- { name, NULL, -1, { pattern, REG_EXTENDED } }
+#define PATTERNS(name, pattern, word_regex) \
+ { name, NULL, -1, { pattern, REG_EXTENDED }, word_regex }
static struct userdiff_driver builtin_drivers[] = {
-FUNCNAME("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$"),
-FUNCNAME("java",
+PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+ "[^<>= \t]+|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("java",
"!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
- "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$"),
-FUNCNAME("objc",
+ "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]="
+ "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"
+ "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("objc",
/* Negate C statements that can look like functions */
"!^[ \t]*(do|for|if|else|return|switch|while)\n"
/* Objective-C methods */
/* C functions */
"^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
/* Objective-C class/protocol definitions */
- "^(@(implementation|interface|protocol)[ \t].*)$"),
-FUNCNAME("pascal",
+ "^(@(implementation|interface|protocol)[ \t].*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+ "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("pascal",
"^((procedure|function|constructor|destructor|interface|"
"implementation|initialization|finalization)[ \t]*.*)$"
"\n"
- "^(.*=[ \t]*(class|record).*)$"),
-FUNCNAME("php", "^[\t ]*((function|class).*)"),
-FUNCNAME("python", "^[ \t]*((class|def)[ \t].*)$"),
-FUNCNAME("ruby", "^[ \t]*((class|module|def)[ \t].*)$"),
-FUNCNAME("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$"),
-FUNCNAME("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$"),
+ "^(.*=[ \t]*(class|record).*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+ "|<>|<=|>=|:=|\\.\\."
+ "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("php", "^[\t ]*((function|class).*)",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+ "|[-+*/<>%&^|=!.]=|--|\\+\\+|<<=?|>>=?|===|&&|\\|\\||::|->"
+ "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"
+ "|[^[:space:]|[\x80-\xff]+"),
+ /* -- */
+PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+ /* -- */
+ "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
+ "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"
+ "|[^[:space:]|[\x80-\xff]+"),
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+ "[={}\"]|[^={}\" \t]+"),
+PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
+ "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+|[^[:space:]]"),
+PATTERNS("cpp",
+ /* Jump targets or access declarations */
+ "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:.*$\n"
+ /* C/++ functions/methods at top level */
+ "^([A-Za-z_][A-Za-z_0-9]*([ \t]+[A-Za-z_][A-Za-z_0-9]*([ \t]*::[ \t]*[^[:space:]]+)?){1,}[ \t]*\\([^;]*)$\n"
+ /* compound type at top level */
+ "^((struct|class|enum)[^;]*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+ "|[^[:space:]]|[\x80-\xff]+"),
{ "default", NULL, -1, { NULL, 0 } },
};
-#undef FUNCNAME
+#undef PATTERNS
static struct userdiff_driver driver_true = {
"diff=true",
return parse_string(&drv->external, k, v);
if ((drv = parse_driver(k, v, "textconv")))
return parse_string(&drv->textconv, k, v);
+ if ((drv = parse_driver(k, v, "wordregex")))
+ return parse_string(&drv->word_regex, k, v);
return 0;
}
const char *external;
int binary;
struct userdiff_funcname funcname;
+ const char *word_regex;
const char *textconv;
};
* Copyright (C) Eric Biederman, 2005
*/
#include "cache.h"
+#include "exec_cmd.h"
static const char var_usage[] = "git var [-l | <variable>]";
usage(var_usage);
}
+ git_extract_argv0_path(argv[0]);
+
setup_git_directory_gently(&nongit);
val = NULL;