diff options
60 files changed, 1705 insertions, 341 deletions
diff --git a/.gitignore b/.gitignore index bd49cd4627..e8b060cbe4 100644 --- a/.gitignore +++ b/.gitignore @@ -157,7 +157,7 @@ common-cmds.h *.tar.gz *.dsc *.deb -git-core.spec +git.spec *.exe *.[aos] *.py[co] diff --git a/Documentation/Makefile b/Documentation/Makefile index 9cef4806d1..f3a6c733b6 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -2,7 +2,7 @@ MAN1_TXT= \ $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \ $(wildcard git-*.txt)) \ gitk.txt -MAN5_TXT=gitattributes.txt gitignore.txt +MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt MAN7_TXT=git.txt DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)) @@ -29,7 +29,7 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT)) prefix?=$(HOME) bindir?=$(prefix)/bin -mandir?=$(prefix)/man +mandir?=$(prefix)/share/man man1dir=$(mandir)/man1 man5dir=$(mandir)/man5 man7dir=$(mandir)/man7 @@ -37,6 +37,9 @@ man7dir=$(mandir)/man7 ASCIIDOC=asciidoc ASCIIDOC_EXTRA = +ifdef ASCIIDOC8 +ASCIIDOC_EXTRA += -a asciidoc7compatible +endif INSTALL?=install DOC_REF = origin/man diff --git a/Documentation/RelNotes-1.5.2.2.txt b/Documentation/RelNotes-1.5.2.2.txt new file mode 100644 index 0000000000..f6393f8a94 --- /dev/null +++ b/Documentation/RelNotes-1.5.2.2.txt @@ -0,0 +1,61 @@ +GIT v1.5.2.2 Release Notes +========================== + +Fixes since v1.5.2.1 +-------------------- + +* Usability fix + + - git-gui is shipped with its updated blame interface. It is + rumored that the older one was not just unusable but was + active health hazard, but this one is actually pretty. + Please see for yourself. + +* Bugfixes + + - "git checkout fubar" was utterly confused when there is a + branch fubar and a tag fubar at the same time. It correctly + checks out the branch fubar now. + + - "git clone /path/foo" to clone a local /path/foo.git + repository left an incorrect configuration. + + - "git send-email" correctly unquotes RFC 2047 quoted names in + the patch-email before using their values. + + - We did not accept number of seconds since epoch older than + year 2000 as a valid timestamp. We now interpret positive + integers more than 8 digits as such, which allows us to + express timestamps more recent than March 1973. + + - git-cvsimport did not work when you have GIT_DIR to point + your repository at a nonstandard location. + + - Some systems (notably, Solaris) lack hstrerror() to make + h_errno human readable; prepare a replacement + implementation. + + - .gitignore file listed git-core.spec but what we generate is + git.spec, and nobody noticed for a long time. + + - "git-merge-recursive" does not try to run file level merge + on binary files. + + - "git-branch --track" did not create tracking configuration + correctly when the branch name had slash in it. + + - The email address of the user specified with user.email + configuration was overriden by EMAIL environment variable. + + - The tree parser did not warn about tree entries with + nonsense file modes, and assumed they must be blobs. + + - "git log -z" without any other request to generate diff still + invoked the diff machinery, wasting cycles. + +* Documentation + + - Many updates to fix stale or missing documentation. + + - Although our documentation was primarily meant to be formatted + with AsciiDoc7, formatting with AsciiDoc8 is supported better. diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf index 99302c5beb..6b6220dfdb 100644 --- a/Documentation/asciidoc.conf +++ b/Documentation/asciidoc.conf @@ -8,7 +8,8 @@ # the command. [attributes] -caret=^ +plus=+ +caret=^ startsb=[ endsb=] tilde=~ diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl index a181f753e0..fcea1d74d5 100755 --- a/Documentation/cmd-list.perl +++ b/Documentation/cmd-list.perl @@ -86,6 +86,7 @@ git-check-attr purehelpers git-check-ref-format purehelpers git-cherry ancillaryinterrogators git-cherry-pick mainporcelain +git-citool mainporcelain git-clean mainporcelain git-clone mainporcelain git-commit mainporcelain @@ -111,6 +112,7 @@ git-fsck ancillaryinterrogators git-gc mainporcelain git-get-tar-commit-id ancillaryinterrogators git-grep mainporcelain +git-gui mainporcelain git-hash-object plumbingmanipulators git-http-fetch synchelpers git-http-push synchelpers diff --git a/Documentation/git-citool.txt b/Documentation/git-citool.txt new file mode 100644 index 0000000000..5217ab2234 --- /dev/null +++ b/Documentation/git-citool.txt @@ -0,0 +1,32 @@ +git-citool(1) +============= + +NAME +---- +git-citool - Graphical alternative to git-commit + +SYNOPSIS +-------- +'git citool' + +DESCRIPTION +----------- +A Tcl/Tk based graphical interface to review modified files, stage +them into the index, enter a commit message and record the new +commit onto the current branch. This interface is an alternative +to the less interactive gitlink:git-commit[1] program. + +git-citool is actually a standard alias for 'git gui citool'. +See gitlink:git-gui[1] for more details. + +Author +------ +Written by Shawn O. Pearce <spearce@spearce.org>. + +Documentation +-------------- +Documentation by Shawn O. Pearce <spearce@spearce.org>. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt index 827711c3c9..6c423e3a2f 100644 --- a/Documentation/git-cvsexportcommit.txt +++ b/Documentation/git-cvsexportcommit.txt @@ -76,7 +76,7 @@ $ git-cvsexportcommit -v <commit-sha1> $ cvs commit -F .mgs <files> ------------ -Merge pending patches into CVS automatically -- only if you really know what you are doing :: +Merge pending patches into CVS automatically -- only if you really know what you are doing:: + ------------ $ export GIT_DIR=~/project/.git diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index e5005f02f9..60d0bcf0f3 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -7,10 +7,53 @@ git-cvsserver - A CVS server emulator for git SYNOPSIS -------- + +SSH: + [verse] export CVS_SERVER=git-cvsserver 'cvs' -d :ext:user@server/path/repo.git co <HEAD_name> +pserver (/etc/inetd.conf): + +[verse] +cvspserver stream tcp nowait nobody /usr/bin/git-cvsserver git-cvsserver pserver + +Usage: + +[verse] +'git-cvsserver' [options] [pserver|server] [<directory> ...] + +OPTIONS +------- + +All these options obviously only make sense if enforced by the server side. +They have been implemented to resemble the gitlink:git-daemon[1] options as +closely as possible. + +--base-path <path>:: +Prepend 'path' to requested CVSROOT + +--strict-paths:: +Don't allow recursing into subdirectories + +--export-all:: +Don't check for `gitcvs.enabled` in config. You also have to specify a list +of allowed directories (see below) if you want to use this option. + +--version, -V:: +Print version information and exit + +--help, -h, -H:: +Print usage information and exit + +<directory>:: +You can specify a list of allowed directories. If no directories +are given, all are allowed. This is an additional restriction, gitcvs +access still needs to be enabled by the `gitcvs.enabled` config option +unless '--export-all' was given, too. + + DESCRIPTION ----------- diff --git a/Documentation/git-gui.txt b/Documentation/git-gui.txt new file mode 100644 index 0000000000..bd613b2fcf --- /dev/null +++ b/Documentation/git-gui.txt @@ -0,0 +1,115 @@ +git-gui(1) +========== + +NAME +---- +git-gui - A portable graphical interface to Git + +SYNOPSIS +-------- +'git gui' [<command>] [arguments] + +DESCRIPTION +----------- +A Tcl/Tk based graphical user interface to Git. git-gui focuses +on allowing users to make changes to their repository by making +new commits, amending existing ones, creating branches, performing +local merges, and fetching/pushing to remote repositories. + +Unlike gitlink:gitk[1], git-gui focuses on commit generation +and single file annotation, and does not show project history. +It does however supply menu actions to start a gitk session from +within git-gui. + +git-gui is known to work on all popular UNIX systems, Mac OS X, +and Windows (under both Cygwin and MSYS). To the extent possible +OS specific user interface guidelines are followed, making git-gui +a fairly native interface for users. + +COMMANDS +-------- +blame:: + Start a blame viewer on the specified file on the given + version (or working directory if not specified). + +browser:: + Start a tree browser showing all files in the specified + commit (or 'HEAD' by default). Files selected through the + browser are opened in the blame viewer. + +citool:: + Start git-gui and arrange to make exactly one commit before + exiting and returning to the shell. The interface is limited + to only commit actions, slightly reducing the application's + startup time and simplifying the menubar. + +version:: + Display the currently running version of git-gui. + + +Examples +-------- +git gui blame Makefile:: + + Show the contents of the file 'Makefile' in the current + working directory, and provide annotations for both the + original author of each line, and who moved the line to its + current location. The uncommitted file is annotated, and + uncommitted changes (if any) are explicitly attributed to + 'Not Yet Committed'. + +git gui blame v0.99.8 Makefile:: + + Show the contents of 'Makefile' in revision 'v0.99.8' + and provide annotations for each line. Unlike the above + example the file is read from the object database and not + the working directory. + +git gui citool:: + + Make one commit and return to the shell when it is complete. + +git citool:: + + Same as 'git gui citool' (above). + +git gui browser maint:: + + Show a browser for the tree of the 'maint' branch. Files + selected in the browser can be viewed with the internal + blame viewer. + +See Also +-------- +'gitk(1)':: + The git repository browser. Shows branches, commit history + and file differences. gitk is the utility started by + git-gui's Repository Visualize actions. + +Other +----- +git-gui is actually maintained as an independent project, but stable +versions are distributed as part of the Git suite for the convience +of end users. + +A git-gui development repository can be obtained from: + + git clone git://repo.or.cz/git-gui.git + +or + + git clone http://repo.or.cz/r/git-gui.git + +or browsed online at http://repo.or.cz/w/git-gui.git/[]. + +Author +------ +Written by Shawn O. Pearce <spearce@spearce.org>. + +Documentation +-------------- +Documentation by Shawn O. Pearce <spearce@spearce.org>. + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 6157edb404..7adcdefacf 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -52,7 +52,14 @@ include::pretty-options.txt[] See also gitlink:git-reflog[1]. --decorate:: - Print out the ref names of any commits that are shown. + Print out the ref names of any commits that are shown. + +--full-diff:: + Without this flag, "git log -p <paths>..." shows commits that + touch the specified paths, and diffs about the same specified + paths. With this, the full diff is shown for commits that touch + the specified paths; this means that "<paths>..." limits only + commits, and doesn't limit diff for those commits. <paths>...:: Show only commits that affect the specified paths. diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt index 366c5dbdce..665f6dc709 100644 --- a/Documentation/git-push.txt +++ b/Documentation/git-push.txt @@ -53,9 +53,8 @@ side are updated. + `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`. + -A parameter <ref> without a colon is equivalent to -<ref>`:`<ref>, hence updates <ref> in the destination from <ref> -in the source. +A parameter <ref> without a colon pushes the <ref> from the source +repository to the destination repository under the same name. + Pushing an empty <src> allows you to delete the <dst> ref from the remote repository. @@ -98,6 +97,26 @@ the remote repository. include::urls.txt[] + +Examples +-------- + +git push origin master:: + Find a ref that matches `master` in the source repository + (most likely, it would find `refs/heads/master`), and update + the same ref (e.g. `refs/heads/master`) in `origin` repository + with it. + +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 master:satellite/master:: + Find a ref that matches `master` in the source repository + (most likely, it would find `refs/heads/master`), and update + the ref that matches `satellite/master` (most likely, it would + be `refs/remotes/satellite/master`) in `origin` repository with it. + Author ------ Written by Junio C Hamano <junkio@cox.net>, later rewritten in C diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt index 84184d6294..74c5478ba1 100644 --- a/Documentation/git-read-tree.txt +++ b/Documentation/git-read-tree.txt @@ -8,7 +8,7 @@ git-read-tree - Reads tree information into the index SYNOPSIS -------- -'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]) +'git-read-tree' (<tree-ish> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]) DESCRIPTION @@ -50,6 +50,12 @@ OPTIONS trees that are not directly related to the current working tree status into a temporary index file. +--trivial:: + Restrict three-way merge by `git-read-tree` to happen + only if there is no file-level merging required, instead + of resolving merge for trivial cases and leaving + conflicting files unresolved in the index. + --aggressive:: Usually a three-way merge by `git-read-tree` resolves the merge for really trivial cases and leaves other diff --git a/Documentation/git-reflog.txt b/Documentation/git-reflog.txt index f717e1e30c..89bc9c51ea 100644 --- a/Documentation/git-reflog.txt +++ b/Documentation/git-reflog.txt @@ -39,6 +39,19 @@ the current branch. It is basically an alias for 'git log -g --abbrev-commit OPTIONS ------- +--stale-fix:: + This revamps the logic -- the definition of "broken commit" + becomes: a commit that is not reachable from any of the refs and + there is a missing object among the commit, tree, or blob + objects reachable from it that is not reachable from any of the + refs. ++ +This computation involves traversing all the reachable objects, i.e. it +has the same cost as 'git prune'. Fortunately, once this is run, we +should not have to ever worry about missing objects, because the current +prune and pack-objects know about reflogs and protect objects referred by +them. + --expire=<time>:: Entries older than this time are pruned. Without the option it is taken from configuration `gc.reflogExpire`, diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt index 0dba73f276..32cb13faec 100644 --- a/Documentation/git-rev-list.txt +++ b/Documentation/git-rev-list.txt @@ -16,11 +16,13 @@ SYNOPSIS [ \--sparse ] [ \--no-merges ] [ \--remove-empty ] + [ \--full-history ] [ \--not ] [ \--all ] [ \--stdin ] [ \--topo-order ] [ \--parents ] + [ \--timestamp ] [ \--left-right ] [ \--cherry-pick ] [ \--encoding[=<encoding>] ] @@ -116,6 +118,9 @@ e.g. "2 hours ago". Print the parents of the commit. +--timestamp:: + Print the raw commit timestamp. + --left-right:: Mark which side of a symmetric diff a commit is reachable from. @@ -228,6 +233,14 @@ limiting may be applied. Stop when a given path disappears from the tree. +--full-history:: + + Show also parts of history irrelevant to current state of a given + path. This turns off history simplification, which removed merges + which didn't change anything at all at some child. It will still actually + simplify away merges that didn't change anything at all into either + child. + --no-merges:: Do not print commits with more than one parent. diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index e1cb4ef856..87771b832b 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -89,6 +89,10 @@ OPTIONS --git-dir:: Show `$GIT_DIR` if defined else show the path to the .git directory. +--is-inside-git-dir:: + Return "true" if we are in the git directory, otherwise "false". + Some commands require to be run in a working directory. + --short, --short=number:: Instead of outputting the full SHA1 values of object names try to abbreviate them to a shorter unique name. When no length is specified diff --git a/Documentation/git.txt b/Documentation/git.txt index ba077c39c6..20b5b7bb48 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -41,9 +41,11 @@ unreleased) version of git, that is available from 'master' branch of the `git.git` repository. Documentation for older releases are available here: -* link:v1.5.2/git.html[documentation for release 1.5.2] +* link:v1.5.2.2/git.html[documentation for release 1.5.2.2] * release notes for + link:RelNotes-1.5.2.2.txt[1.5.2.2], + link:RelNotes-1.5.2.1.txt[1.5.2.1], link:RelNotes-1.5.2.txt[1.5.2]. * link:v1.5.1.6/git.html[documentation for release 1.5.1.6] diff --git a/Documentation/gitmodules.txt b/Documentation/gitmodules.txt new file mode 100644 index 0000000000..035294e208 --- /dev/null +++ b/Documentation/gitmodules.txt @@ -0,0 +1,62 @@ +gitmodules(5) +============= + +NAME +---- +gitmodules - defining submodule properties + +SYNOPSIS +-------- +gitmodules + + +DESCRIPTION +----------- + +The `.gitmodules` file, located in the top-level directory of a git +working tree, is a text file with a syntax matching the requirements +of gitlink:git-config[1]. + +The file contains one subsection per submodule, and the subsection value +is the name of the submodule. Each submodule section also contains the +following required keys: + +submodule.<name>.path:: + Defines the path, relative to the top-level directory of the git + working tree, where the submodule is expected to be checked out. + The path name must not end with a `/`. All submodule paths must + be unique within the .gitmodules file. + +submodule.<name>.url:: + Defines an url from where the submodule repository can be cloned. + + +EXAMPLES +-------- + +Consider the following .gitmodules file: + + [submodule "libfoo"] + path = include/foo + url = git://foo.com/git/lib.git + + [submodule "libbar"] + path = include/bar + url = git://bar.com/git/lib.git + + +This defines two submodules, `libfoo` and `libbar`. These are expected to +be checked out in the paths 'include/foo' and 'include/bar', and for both +submodules an url is specified which can be used for cloning the submodules. + +SEE ALSO +-------- +gitlink:git-submodule[1] gitlink:git-config[1] + +DOCUMENTATION +------------- +Documentation by Lars Hjemli <hjemli@gmail.com> + +GIT +--- +Part of the gitlink:git[7] suite diff --git a/Documentation/pretty-options.txt b/Documentation/pretty-options.txt index 6338def5a7..746bc5b7f9 100644 --- a/Documentation/pretty-options.txt +++ b/Documentation/pretty-options.txt @@ -5,6 +5,15 @@ 'full', 'fuller', 'email', 'raw' and 'format:<string>'. When left out the format default to 'medium'. +--abbrev-commit:: + Instead of showing the full 40-byte hexadecimal commit object + name, show only handful hexdigits prefix. Non default number of + digits can be specified with "--abbrev=<n>" (which also modifies + diff output, if it is displayed). ++ +This should make "--pretty=oneline" a whole lot more readable for +people using 80-column terminals. + --encoding[=<encoding>]:: The commit objects record the encoding used for the log message in their encoding header; this option can be used to tell the diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index 0bfa21b3d2..ff7c71d4fb 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -2757,8 +2757,8 @@ As a result, the general consistency of an object can always be tested independently of the contents or the type of the object: all objects can be validated by verifying that (a) their hashes match the content of the file and (b) the object successfully inflates to a stream of bytes that -forms a sequence of <ascii type without space> + <space> + <ascii decimal -size> + <byte\0> + <binary object data>. +forms a sequence of <ascii type without space> {plus} <space> {plus} <ascii decimal +size> {plus} <byte\0> {plus} <binary object data>. The structured objects can further have their structure and connectivity to other objects verified. This is generally done with @@ -89,10 +89,16 @@ Issues of note: will include them. Note that config.mak is not distributed; the name is reserved for local settings. - - To build and install documentation suite, you need to have the - asciidoc/xmlto toolchain. Alternatively, pre-formatted - documentation are available in "html" and "man" branches of the git - repository itself. For example, you could: + - To build and install documentation suite, you need to have + the asciidoc/xmlto toolchain. Because not many people are + inclined to install the tools, the default build target + ("make all") does _not_ build them. The documentation is + written for AsciiDoc 7, but "make ASCIIDOC8=YesPlease doc" + will let you format with AsciiDoc 8. + + Alternatively, pre-formatted documentation are available in + "html" and "man" branches of the git repository itself. For + example, you could: $ mkdir manual && cd manual $ git init @@ -107,6 +107,8 @@ all:: # Define USE_STDEV below if you want git to care about the underlying device # change being considered an inode change from the update-cache perspective. # +# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8 +# # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's # MakeMaker (e.g. using ActiveState under Cygwin). # @@ -209,7 +211,8 @@ SCRIPT_SH = \ git-am.sh \ git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \ git-merge-resolve.sh git-merge-ours.sh \ - git-lost-found.sh git-quiltimport.sh git-submodule.sh + git-lost-found.sh git-quiltimport.sh git-submodule.sh \ + git-filter-branch.sh SCRIPT_PERL = \ git-add--interactive.perl \ @@ -410,6 +413,7 @@ ifeq ($(uname_S),SunOS) NEEDS_NSL = YesPlease SHELL_PATH = /bin/bash NO_STRCASESTR = YesPlease + NO_HSTRERROR = YesPlease ifeq ($(uname_R),5.8) NEEDS_LIBICONV = YesPlease NO_UNSETENV = YesPlease @@ -654,6 +658,10 @@ endif ifdef NO_PERL_MAKEMAKER export NO_PERL_MAKEMAKER endif +ifdef NO_HSTRERROR + COMPAT_CFLAGS += -DNO_HSTRERROR + COMPAT_OBJS += compat/hstrerror.o +endif ifeq ($(TCLTK_PATH),) NO_TCLTK=NoThanks @@ -684,6 +692,10 @@ ifndef V endif endif +ifdef ASCIIDOC8 + export ASCIIDOC8 +endif + # Shell quote (do not use $(call) to accommodate ancient setups); SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER)) @@ -735,9 +747,13 @@ gitk-wish: gitk GIT-GUI-VARS chmod +x $@+ && \ mv -f $@+ $@ -git$X: git.c common-cmds.h $(BUILTIN_OBJS) $(GITLIBS) GIT-CFLAGS +git.o: git.c common-cmds.h GIT-CFLAGS + $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ + $(ALL_CFLAGS) -c $(filter %.c,$^) + +git$X: git.o $(BUILTIN_OBJS) $(GITLIBS) $(QUIET_LINK)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \ - $(ALL_CFLAGS) -o $@ $(filter %.c,$^) \ + $(ALL_CFLAGS) -o $@ $(filter %.c,$^) git.o \ $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS) help.o: common-cmds.h @@ -748,6 +764,8 @@ git-merge-subtree$X: git-merge-recursive$X $(BUILT_INS): git$X $(QUIET_BUILT_IN)rm -f $@ && ln git$X $@ +common-cmds.h: ./generate-cmdlist.sh + common-cmds.h: $(wildcard Documentation/git-*.txt) $(QUIET_GEN)./generate-cmdlist.sh > $@+ && mv $@+ $@ diff --git a/builtin-add.c b/builtin-add.c index 159117106a..734547994f 100644 --- a/builtin-add.c +++ b/builtin-add.c @@ -40,42 +40,29 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p dir->nr = dst - dir->entries; for (i = 0; i < specs; i++) { - struct stat st; - const char *match; - if (seen[i]) - continue; - - match = pathspec[i]; - if (!match[0]) - continue; - - /* Existing file? We must have ignored it */ - if (!lstat(match, &st)) { - struct dir_entry *ent; - - ent = dir_add_name(dir, match, strlen(match)); - ent->ignored = 1; - if (S_ISDIR(st.st_mode)) - ent->ignored_dir = 1; - continue; - } - die("pathspec '%s' did not match any files", match); + if (!seen[i] && !file_exists(pathspec[i])) + die("pathspec '%s' did not match any files", + pathspec[i]); } } -static void fill_directory(struct dir_struct *dir, const char **pathspec) +static void fill_directory(struct dir_struct *dir, const char **pathspec, + int ignored_too) { const char *path, *base; int baselen; /* Set up the default git porcelain excludes */ memset(dir, 0, sizeof(*dir)); - dir->exclude_per_dir = ".gitignore"; - path = git_path("info/exclude"); - if (!access(path, R_OK)) - add_excludes_from_file(dir, path); - if (!access(excludes_file, R_OK)) - add_excludes_from_file(dir, excludes_file); + if (!ignored_too) { + dir->collect_ignored = 1; + dir->exclude_per_dir = ".gitignore"; + path = git_path("info/exclude"); + if (!access(path, R_OK)) + add_excludes_from_file(dir, path); + if (!access(excludes_file, R_OK)) + add_excludes_from_file(dir, excludes_file); + } /* * Calculate common prefix for the pathspec, and @@ -219,13 +206,11 @@ int cmd_add(int argc, const char **argv, const char *prefix) } pathspec = get_pathspec(prefix, argv + i); - fill_directory(&dir, pathspec); + fill_directory(&dir, pathspec, ignored_too); if (show_only) { const char *sep = "", *eof = ""; for (i = 0; i < dir.nr; i++) { - if (!ignored_too && dir.entries[i]->ignored) - continue; printf("%s%s", sep, dir.entries[i]->name); sep = " "; eof = "\n"; @@ -237,25 +222,13 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (read_cache() < 0) die("index file corrupt"); - if (!ignored_too) { - int has_ignored = 0; - for (i = 0; i < dir.nr; i++) - if (dir.entries[i]->ignored) - has_ignored = 1; - if (has_ignored) { - fprintf(stderr, ignore_warning); - for (i = 0; i < dir.nr; i++) { - if (!dir.entries[i]->ignored) - continue; - fprintf(stderr, "%s", dir.entries[i]->name); - if (dir.entries[i]->ignored_dir) - fprintf(stderr, " (directory)"); - fputc('\n', stderr); - } - fprintf(stderr, - "Use -f if you really want to add them.\n"); - exit(1); + if (dir.ignored_nr) { + fprintf(stderr, ignore_warning); + for (i = 0; i < dir.ignored_nr; i++) { + fprintf(stderr, "%s\n", dir.ignored[i]->name); } + fprintf(stderr, "Use -f if you really want to add them.\n"); + exit(1); } for (i = 0; i < dir.nr; i++) diff --git a/builtin-branch.c b/builtin-branch.c index d7c321af4f..77b85dde1f 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -85,6 +85,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) unsigned char sha1[20]; char *name = NULL; const char *fmt, *remote; + char section[PATH_MAX]; int i; int ret = 0; @@ -152,9 +153,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) error("Error deleting %sbranch '%s'", remote, argv[i]); ret = 1; - } else + } else { printf("Deleted %sbranch %s.\n", remote, argv[i]); - + snprintf(section, sizeof(section), "branch.%s", + argv[i]); + if (git_config_rename_section(section, NULL) < 0) + warning("Update of config-file failed"); + } } if (name) diff --git a/builtin-read-tree.c b/builtin-read-tree.c index 316fb0f8da..41f8110238 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -84,7 +84,7 @@ static void prime_cache_tree(void) } -static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])"; +static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])"; static struct lock_file lock_file; diff --git a/builtin-revert.c b/builtin-revert.c index 8f02ed7bd1..499bbe7343 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -25,7 +25,7 @@ static const char *cherry_pick_usage = "git-cherry-pick [--edit] [-n] [-r] [-x] static int edit; static int replay; -enum { REVERT, CHERRY_PICK } action; +static enum { REVERT, CHERRY_PICK } action; static int no_commit; static struct commit *commit; static int needed_deref; @@ -129,7 +129,7 @@ static char *get_encoding(const char *message) return NULL; } -struct lock_file msg_file; +static struct lock_file msg_file; static int msg_fd; static void add_to_msg(const char *string) diff --git a/cache-tree.c b/cache-tree.c index 350a79b768..077f034369 100644 --- a/cache-tree.c +++ b/cache-tree.c @@ -478,7 +478,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p) if (0 <= it->entry_count) { if (size < 20) goto free_return; - hashcpy(it->sha1, (unsigned char*)buf); + hashcpy(it->sha1, (const unsigned char*)buf); buf += 20; size -= 20; } @@ -372,7 +372,6 @@ extern int move_temp_to_file(const char *tmpfile, const char *filename); extern int has_sha1_pack(const unsigned char *sha1, const char **ignore); extern int has_sha1_file(const unsigned char *sha1); -extern void *map_sha1_file(const unsigned char *sha1, unsigned long *); extern int has_pack_file(const unsigned char *sha1); extern int has_pack_index(const unsigned char *sha1); @@ -27,7 +27,7 @@ struct sort_node const char *commit_type = "commit"; -struct cmt_fmt_map { +static struct cmt_fmt_map { const char *n; size_t cmp_len; enum cmit_fmt v; diff --git a/compat/hstrerror.c b/compat/hstrerror.c new file mode 100644 index 0000000000..069c555da4 --- /dev/null +++ b/compat/hstrerror.c @@ -0,0 +1,21 @@ +#include <string.h> +#include <stdio.h> +#include <netdb.h> + +const char *githstrerror(int err) +{ + static char buffer[48]; + switch (err) + { + case HOST_NOT_FOUND: + return "Authoritative answer: host not found"; + case NO_DATA: + return "Valid name, no data record of requested type"; + case NO_RECOVERY: + return "Non recoverable errors, FORMERR, REFUSED, NOTIMP"; + case TRY_AGAIN: + return "Non-authoritative \"host not found\", or SERVERFAIL"; + } + sprintf(buffer, "Name resolution error %d", err); + return buffer; +} @@ -523,7 +523,7 @@ static int store_aux(const char* key, const char* value) return 0; } -static int write_error() +static int write_error(void) { fprintf(stderr, "Failed to write new configuration file\n"); @@ -224,11 +224,10 @@ static int git_tcp_connect_sock(char *host, int flags) } if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) { saved_errno = errno; - fprintf(stderr, "%s[%d: %s]: net=%s, errno=%s\n", + fprintf(stderr, "%s[%d: %s]: errno=%s\n", host, cnt, ai_name(ai), - hstrerror(h_errno), strerror(saved_errno)); close(sockfd); sockfd = -1; @@ -315,11 +314,10 @@ static int git_tcp_connect_sock(char *host, int flags) if (connect(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { saved_errno = errno; - fprintf(stderr, "%s[%d: %s]: net=%s, errno=%s\n", + fprintf(stderr, "%s[%d: %s]: errno=%s\n", host, cnt, inet_ntoa(*(struct in_addr *)&sa.sin_addr), - hstrerror(h_errno), strerror(saved_errno)); close(sockfd); sockfd = -1; diff --git a/contrib/gitview/gitview b/contrib/gitview/gitview index 098cb01353..5931766620 100755 --- a/contrib/gitview/gitview +++ b/contrib/gitview/gitview @@ -352,6 +352,7 @@ class AnnotateWindow(object): self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) self.window.set_border_width(0) self.window.set_title("Git repository browser annotation window") + self.prev_read = "" # Use two thirds of the screen by default screen = self.window.get_screen() @@ -401,7 +402,10 @@ class AnnotateWindow(object): def data_ready(self, source, condition): while (1): try : - buffer = source.read(8192) + # A simple readline doesn't work + # a readline bug ?? + buffer = source.read(100) + except: # resource temporary not available return True @@ -411,6 +415,19 @@ class AnnotateWindow(object): source.close() return False + if (self.prev_read != ""): + buffer = self.prev_read + buffer + self.prev_read = "" + + if (buffer[len(buffer) -1] != '\n'): + try: + newline_index = buffer.rindex("\n") + except ValueError: + newline_index = 0 + + self.prev_read = buffer[newline_index:(len(buffer))] + buffer = buffer[0:newline_index] + for buff in buffer.split("\n"): annotate_line = re.compile('^([0-9a-f]{40}) (.+) (.+) (.+)$') m = annotate_line.match(buff) @@ -516,7 +533,7 @@ class AnnotateWindow(object): self.add_file_data(filename, commit_sha1, line_num) - fp = os.popen("git blame --incremental -- " + filename + " " + commit_sha1) + fp = os.popen("git blame --incremental -C -C -- " + filename + " " + commit_sha1) flags = fcntl.fcntl(fp.fileno(), fcntl.F_GETFL) fcntl.fcntl(fp.fileno(), fcntl.F_SETFL, flags | os.O_NONBLOCK) self.io_watch_tag = gobject.io_add_watch(fp, gobject.IO_IN, self.data_ready) diff --git a/csum-file.c b/csum-file.c index 5109342624..9ab997120d 100644 --- a/csum-file.c +++ b/csum-file.c @@ -73,33 +73,6 @@ int sha1write(struct sha1file *f, void *buf, unsigned int count) return 0; } -struct sha1file *sha1create(const char *fmt, ...) -{ - struct sha1file *f; - unsigned len; - va_list arg; - int fd; - - f = xmalloc(sizeof(*f)); - - va_start(arg, fmt); - len = vsnprintf(f->name, sizeof(f->name), fmt, arg); - va_end(arg); - if (len >= PATH_MAX) - die("you wascally wabbit, you"); - f->namelen = len; - - fd = open(f->name, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd < 0) - die("unable to open %s (%s)", f->name, strerror(errno)); - f->fd = fd; - f->error = 0; - f->offset = 0; - f->do_crc = 0; - SHA1_Init(&f->ctx); - return f; -} - struct sha1file *sha1fd(int fd, const char *name) { struct sha1file *f; @@ -121,34 +94,6 @@ struct sha1file *sha1fd(int fd, const char *name) return f; } -int sha1write_compressed(struct sha1file *f, void *in, unsigned int size, int level) -{ - z_stream stream; - unsigned long maxsize; - void *out; - - memset(&stream, 0, sizeof(stream)); - deflateInit(&stream, level); - maxsize = deflateBound(&stream, size); - out = xmalloc(maxsize); - - /* Compress it */ - stream.next_in = in; - stream.avail_in = size; - - stream.next_out = out; - stream.avail_out = maxsize; - - while (deflate(&stream, Z_FINISH) == Z_OK) - /* nothing */; - deflateEnd(&stream); - - size = stream.total_out; - sha1write(f, out, size); - free(out); - return size; -} - void crc32_begin(struct sha1file *f) { f->crc32 = crc32(0, Z_NULL, 0); diff --git a/csum-file.h b/csum-file.h index 4e8b83e093..c3c792f1b5 100644 --- a/csum-file.h +++ b/csum-file.h @@ -13,10 +13,8 @@ struct sha1file { }; extern struct sha1file *sha1fd(int fd, const char *name); -extern struct sha1file *sha1create(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern int sha1close(struct sha1file *, unsigned char *, int); extern int sha1write(struct sha1file *, void *, unsigned int); -extern int sha1write_compressed(struct sha1file *, void *, unsigned int, int); extern void crc32_begin(struct sha1file *); extern uint32_t crc32_end(struct sha1file *); diff --git a/diffcore-rename.c b/diffcore-rename.c index 93c40d9e04..79c984c9cf 100644 --- a/diffcore-rename.c +++ b/diffcore-rename.c @@ -119,6 +119,21 @@ static int is_exact_match(struct diff_filespec *src, return 0; } +static int basename_same(struct diff_filespec *src, struct diff_filespec *dst) +{ + int src_len = strlen(src->path), dst_len = strlen(dst->path); + while (src_len && dst_len) { + char c1 = src->path[--src_len]; + char c2 = dst->path[--dst_len]; + if (c1 != c2) + return 0; + if (c1 == '/') + return 1; + } + return (!src_len || src->path[src_len - 1] == '/') && + (!dst_len || dst->path[dst_len - 1] == '/'); +} + struct diff_score { int src; /* index in rename_src */ int dst; /* index in rename_dst */ @@ -186,8 +201,11 @@ static int estimate_similarity(struct diff_filespec *src, */ if (!dst->size) score = 0; /* should not happen */ - else + else { score = (int)(src_copied * MAX_SCORE / max_size); + if (basename_same(src, dst)) + score++; + } return score; } @@ -295,9 +313,22 @@ void diffcore_rename(struct diff_options *options) if (rename_dst[i].pair) continue; /* dealt with an earlier round */ for (j = 0; j < rename_src_nr; j++) { + int k; struct diff_filespec *one = rename_src[j].one; if (!is_exact_match(one, two, contents_too)) continue; + + /* see if there is a basename match, too */ + for (k = j; k < rename_src_nr; k++) { + one = rename_src[k].one; + if (basename_same(one, two) && + is_exact_match(one, two, + contents_too)) { + j = k; + break; + } + } + record_rename_pair(i, j, (int)MAX_SCORE); rename_count++; break; /* we are done with this entry */ @@ -275,7 +275,6 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len) { struct dir_entry *ent; ent = xmalloc(sizeof(*ent) + len + 1); - ent->ignored = ent->ignored_dir = 0; ent->len = len; memcpy(ent->name, pathname, len); ent->name[len] = 0; @@ -287,10 +286,19 @@ struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int if (cache_name_pos(pathname, len) >= 0) return NULL; - ALLOC_GROW(dir->entries, dir->nr, dir->alloc); + ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc); return dir->entries[dir->nr++] = dir_entry_new(pathname, len); } +struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len) +{ + if (cache_name_pos(pathname, len) >= 0) + return NULL; + + ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc); + return dir->ignored[dir->ignored_nr++] = dir_entry_new(pathname, len); +} + enum exist_status { index_nonexistent = 0, index_directory, @@ -423,6 +431,18 @@ static int simplify_away(const char *path, int pathlen, const struct path_simpli return 0; } +static int in_pathspec(const char *path, int len, const struct path_simplify *simplify) +{ + if (simplify) { + for (; simplify->path; simplify++) { + if (len == simplify->len + && !memcmp(path, simplify->path, len)) + return 1; + } + } + return 0; +} + /* * Read a directory tree. We currently ignore anything but * directories, regular files and symlinks. That's because git @@ -463,6 +483,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co continue; exclude = excluded(dir, fullname); + if (exclude && dir->collect_ignored + && in_pathspec(fullname, baselen + len, simplify)) + dir_add_ignored(dir, fullname, baselen + len); if (exclude != dir->show_ignored) { if (!dir->show_ignored || DTYPE(de) != DT_DIR) { continue; @@ -609,6 +632,7 @@ int read_directory(struct dir_struct *dir, const char *path, const char *base, i read_directory_recursive(dir, path, base, baselen, 0, simplify); free_simplify(simplify); qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name); + qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name); return dir->nr; } @@ -13,9 +13,7 @@ struct dir_entry { - unsigned int ignored : 1; - unsigned int ignored_dir : 1; - unsigned int len : 30; + unsigned int len; char name[FLEX_ARRAY]; /* more */ }; @@ -31,11 +29,14 @@ struct exclude_list { struct dir_struct { int nr, alloc; + int ignored_nr, ignored_alloc; unsigned int show_ignored:1, show_other_directories:1, hide_empty_directories:1, - no_gitlinks:1; + no_gitlinks:1, + collect_ignored:1; struct dir_entry **entries; + struct dir_entry **ignored; /* Exclude info */ const char *exclude_per_dir; diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh index 975777f05e..17df47b950 100755 --- a/generate-cmdlist.sh +++ b/generate-cmdlist.sh @@ -7,7 +7,7 @@ struct cmdname_help char help[80]; }; -struct cmdname_help common_cmds[] = {" +static struct cmdname_help common_cmds[] = {" sort <<\EOF | add diff --git a/git-clone.sh b/git-clone.sh index 3a410624d3..bd44ce1c84 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -377,6 +377,13 @@ then ) ) + # Upstream URL + git-config remote."$origin".url "$repo" && + + # Set up the mappings to track the remote branches. + git-config remote."$origin".fetch \ + "+refs/heads/*:$remote_top/*" '^$' && + # Write out remote.$origin config, and update our "$head_points_at". case "$head_points_at" in ?*) @@ -384,21 +391,20 @@ then git-symbolic-ref HEAD "refs/heads/$head_points_at" && # Tracking branch for the primary branch at the remote. - origin_track="$remote_top/$head_points_at" && git-update-ref HEAD "$head_sha1" && - # Upstream URL - git-config remote."$origin".url "$repo" && - - # Set up the mappings to track the remote branches. - git-config remote."$origin".fetch \ - "+refs/heads/*:$remote_top/*" '^$' && rm -f "refs/remotes/$origin/HEAD" git-symbolic-ref "refs/remotes/$origin/HEAD" \ "refs/remotes/$origin/$head_points_at" && git-config branch."$head_points_at".remote "$origin" && git-config branch."$head_points_at".merge "refs/heads/$head_points_at" + ;; + '') + # Source had detached HEAD pointing nowhere + git-update-ref --no-deref HEAD "$head_sha1" && + rm -f "refs/remotes/$origin/HEAD" + ;; esac case "$no_checkout" in diff --git a/git-compat-util.h b/git-compat-util.h index 6bd8987b27..b2ab3f8256 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -167,6 +167,11 @@ extern size_t gitstrlcpy(char *, const char *, size_t); extern uintmax_t gitstrtoumax(const char *, char **, int); #endif +#ifdef NO_HSTRERROR +#define hstrerror githstrerror +extern const char *githstrerror(int herror); +#endif + extern void release_pack_memory(size_t, int); static inline char* xstrdup(const char *str) diff --git a/git-cvsimport.perl b/git-cvsimport.perl index 433b7fd324..69ccb88dde 100755 --- a/git-cvsimport.perl +++ b/git-cvsimport.perl @@ -774,7 +774,6 @@ sub commit { or die "Cannot write branch $branch for update: $!\n"; if ($tag) { - my ($in, $out) = ('',''); my ($xtag) = $tag; $xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY ** $xtag =~ tr/_/\./ if ( $opt_u ); diff --git a/git-cvsserver.perl b/git-cvsserver.perl index d41b29f30b..5cbf27eebc 100755 --- a/git-cvsserver.perl +++ b/git-cvsserver.perl @@ -22,6 +22,9 @@ use bytes; use Fcntl; use File::Temp qw/tempdir tempfile/; use File::Basename; +use Getopt::Long qw(:config require_order no_ignore_case); + +my $VERSION = '@@GIT_VERSION@@'; my $log = GITCVS::log->new(); my $cfg; @@ -85,15 +88,57 @@ my $methods = { my $state = { prependdir => '' }; $log->info("--------------- STARTING -----------------"); +my $usage = + "Usage: git-cvsserver [options] [pserver|server] [<directory> ...]\n". + " --base-path <path> : Prepend to requested CVSROOT\n". + " --strict-paths : Don't allow recursing into subdirectories\n". + " --export-all : Don't check for gitcvs.enabled in config\n". + " --version, -V : Print version information and exit\n". + " --help, -h, -H : Print usage information and exit\n". + "\n". + "<directory> ... is a list of allowed directories. If no directories\n". + "are given, all are allowed. This is an additional restriction, gitcvs\n". + "access still needs to be enabled by the gitcvs.enabled config option.\n"; + +my @opts = ( 'help|h|H', 'version|V', + 'base-path=s', 'strict-paths', 'export-all' ); +GetOptions( $state, @opts ) + or die $usage; + +if ($state->{version}) { + print "git-cvsserver version $VERSION\n"; + exit; +} +if ($state->{help}) { + print $usage; + exit; +} + my $TEMP_DIR = tempdir( CLEANUP => 1 ); $log->debug("Temporary directory is '$TEMP_DIR'"); +$state->{method} = 'ext'; +if (@ARGV) { + if ($ARGV[0] eq 'pserver') { + $state->{method} = 'pserver'; + shift @ARGV; + } elsif ($ARGV[0] eq 'server') { + shift @ARGV; + } +} + +# everything else is a directory +$state->{allowed_roots} = [ @ARGV ]; + +# don't export the whole system unless the users requests it +if ($state->{'export-all'} && !@{$state->{allowed_roots}}) { + die "--export-all can only be used together with an explicit whitelist\n"; +} + # if we are called with a pserver argument, # deal with the authentication cat before entering the # main loop -$state->{method} = 'ext'; -if (@ARGV && $ARGV[0] eq 'pserver') { - $state->{method} = 'pserver'; +if ($state->{method} eq 'pserver') { my $line = <STDIN>; chomp $line; unless( $line =~ /^BEGIN (AUTH|VERIFICATION) REQUEST$/) { die "E Do not understand $line - expecting BEGIN AUTH REQUEST\n"; @@ -172,19 +217,48 @@ sub req_Root return 0; } + my $cvsroot = $state->{'base-path'} || ''; + $cvsroot =~ s#/+$##; + $cvsroot .= $data; + if ($state->{CVSROOT} - && ($state->{CVSROOT} ne $data)) { + && ($state->{CVSROOT} ne $cvsroot)) { print "error 1 Conflicting roots specified\n"; return 0; } - $state->{CVSROOT} = $data; + $state->{CVSROOT} = $cvsroot; $ENV{GIT_DIR} = $state->{CVSROOT} . "/"; + + if (@{$state->{allowed_roots}}) { + my $allowed = 0; + foreach my $dir (@{$state->{allowed_roots}}) { + next unless $dir =~ m#^/#; + $dir =~ s#/+$##; + if ($state->{'strict-paths'}) { + if ($ENV{GIT_DIR} =~ m#^\Q$dir\E/?$#) { + $allowed = 1; + last; + } + } elsif ($ENV{GIT_DIR} =~ m#^\Q$dir\E(/?$|/)#) { + $allowed = 1; + last; + } + } + + unless ($allowed) { + print "E $ENV{GIT_DIR} does not seem to be a valid GIT repository\n"; + print "E \n"; + print "error 1 $ENV{GIT_DIR} is not a valid repository\n"; + return 0; + } + } + unless (-d $ENV{GIT_DIR} && -e $ENV{GIT_DIR}.'HEAD') { print "E $ENV{GIT_DIR} does not seem to be a valid GIT repository\n"; - print "E \n"; - print "error 1 $ENV{GIT_DIR} is not a valid repository\n"; + print "E \n"; + print "error 1 $ENV{GIT_DIR} is not a valid repository\n"; return 0; } @@ -207,7 +281,8 @@ sub req_Root my $enabled = ($cfg->{gitcvs}{$state->{method}}{enabled} || $cfg->{gitcvs}{enabled}); - unless ($enabled && $enabled =~ /^\s*(1|true|yes)\s*$/i) { + unless ($state->{'export-all'} || + ($enabled && $enabled =~ /^\s*(1|true|yes)\s*$/i)) { print "E GITCVS emulation needs to be enabled on this repo\n"; print "E the repo config file needs a [gitcvs] section added, and the parameter 'enabled' set to 1\n"; print "E \n"; diff --git a/git-filter-branch.sh b/git-filter-branch.sh new file mode 100644 index 0000000000..8fa5ce6467 --- /dev/null +++ b/git-filter-branch.sh @@ -0,0 +1,441 @@ +#!/bin/sh +# +# Rewrite revision history +# Copyright (c) Petr Baudis, 2006 +# Minimal changes to "port" it to core-git (c) Johannes Schindelin, 2007 +# +# Lets you rewrite GIT revision history by creating a new branch from +# your current branch by applying custom filters on each revision. +# Those filters can modify each tree (e.g. removing a file or running +# a perl rewrite on all files) or information about each commit. +# Otherwise, all information (including original commit times or merge +# information) will be preserved. +# +# The command takes the new branch name as a mandatory argument and +# the filters as optional arguments. If you specify no filters, the +# commits will be recommitted without any changes, which would normally +# have no effect and result with the new branch pointing to the same +# branch as your current branch. (Nevertheless, this may be useful in +# the future for compensating for some Git bugs or such, therefore +# such a usage is permitted.) +# +# WARNING! The rewritten history will have different ids for all the +# objects and will not converge with the original branch. You will not +# be able to easily push and distribute the rewritten branch. Please do +# not use this command if you do not know the full implications, and +# avoid using it anyway - do not do what a simple single commit on top +# of the current version would fix. +# +# Always verify that the rewritten version is correct before disposing +# the original branch. +# +# Note that since this operation is extensively I/O expensive, it might +# be a good idea to do it off-disk, e.g. on tmpfs. Reportedly the speedup +# is very noticeable. +# +# OPTIONS +# ------- +# -d TEMPDIR:: The path to the temporary tree used for rewriting +# When applying a tree filter, the command needs to temporary +# checkout the tree to some directory, which may consume +# considerable space in case of large projects. By default it +# does this in the '.git-rewrite/' directory but you can override +# that choice by this parameter. +# +# Filters +# ~~~~~~~ +# The filters are applied in the order as listed below. The COMMAND +# argument is always evaluated in shell using the 'eval' command. +# The $GIT_COMMIT environment variable is permanently set to contain +# the id of the commit being rewritten. The author/committer environment +# variables are set before the first filter is run. +# +# A 'map' function is available that takes an "original sha1 id" argument +# and outputs a "rewritten sha1 id" if the commit has been already +# rewritten, fails otherwise; the 'map' function can return several +# ids on separate lines if your commit filter emitted multiple commits +# (see below). +# +# --env-filter COMMAND:: The filter for modifying environment +# This is the filter for modifying the environment in which +# the commit will be performed. Specifically, you might want +# to rewrite the author/committer name/email/time environment +# variables (see `git-commit` for details). Do not forget to +# re-export the variables. +# +# --tree-filter COMMAND:: The filter for rewriting tree (and its contents) +# This is the filter for rewriting the tree and its contents. +# The COMMAND argument is evaluated in shell with the working +# directory set to the root of the checked out tree. The new tree +# is then used as-is (new files are auto-added, disappeared files +# are auto-removed - .gitignore files nor any other ignore rules +# HAVE NO EFFECT!). +# +# --index-filter COMMAND:: The filter for rewriting index +# This is the filter for rewriting the Git's directory index. +# It is similar to the tree filter but does not check out the +# tree, which makes it much faster. However, you must use the +# lowlevel Git index manipulation commands to do your work. +# +# --parent-filter COMMAND:: The filter for rewriting parents +# This is the filter for rewriting the commit's parent list. +# It will receive the parent string on stdin and shall output +# the new parent string on stdout. The parent string is in +# format accepted by `git-commit-tree`: empty for initial +# commit, "-p parent" for a normal commit and "-p parent1 +# -p parent2 -p parent3 ..." for a merge commit. +# +# --msg-filter COMMAND:: The filter for rewriting commit message +# This is the filter for rewriting the commit messages. +# The COMMAND argument is evaluated in shell with the original +# commit message on standard input; its standard output is +# is used as the new commit message. +# +# --commit-filter COMMAND:: The filter for performing the commit +# If this filter is passed, it will be called instead of the +# `git-commit-tree` command, with those arguments: +# +# TREE_ID [-p PARENT_COMMIT_ID]... +# +# and the log message on stdin. The commit id is expected on +# stdout. As a special extension, the commit filter may emit +# multiple commit ids; in that case, all of them will be used +# as parents instead of the original commit in further commits. +# +# --tag-name-filter COMMAND:: The filter for rewriting tag names. +# If this filter is passed, it will be called for every tag ref +# that points to a rewritten object (or to a tag object which +# points to a rewritten object). The original tag name is passed +# via standard input, and the new tag name is expected on standard +# output. +# +# The original tags are not deleted, but can be overwritten; +# use "--tag-name-filter=cat" to simply update the tags. In this +# case, be very careful and make sure you have the old tags +# backed up in case the conversion has run afoul. +# +# Note that there is currently no support for proper rewriting of +# tag objects; in layman terms, if the tag has a message or signature +# attached, the rewritten tag won't have it. Sorry. (It is by +# definition impossible to preserve signatures at any rate, though.) +# +# --subdirectory-filter DIRECTORY:: Only regard the history, as seen by +# the given subdirectory. The result will contain that directory as +# its project root. +# +# EXAMPLE USAGE +# ------------- +# Suppose you want to remove a file (containing confidential information +# or copyright violation) from all commits: +# +# git-filter-branch --tree-filter 'rm filename' newbranch +# +# A significantly faster version: +# +# git-filter-branch --index-filter 'git-update-index --remove filename' newbranch +# +# Now, you will get the rewritten history saved in the branch 'newbranch' +# (your current branch is left untouched). +# +# To "etch-graft" a commit to the revision history (set a commit to be +# the parent of the current initial commit and propagate that): +# +# git-filter-branch --parent-filter sed\ 's/^$/-p graftcommitid/' newbranch +# +# (if the parent string is empty - therefore we are dealing with the +# initial commit - add graftcommit as a parent). Note that this assumes +# history with a single root (that is, no git-merge without common ancestors +# happened). If this is not the case, use: +# +# git-filter-branch --parent-filter 'cat; [ "$GIT_COMMIT" = "COMMIT" ] && echo "-p GRAFTCOMMIT"' newbranch +# +# To remove commits authored by "Darl McBribe" from the history: +# +# git-filter-branch --commit-filter 'if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ]; then shift; while [ -n "$1" ]; do shift; echo "$1"; shift; done; else git-commit-tree "$@"; fi' newbranch +# +# (the shift magic first throws away the tree id and then the -p +# parameters). Note that this handles merges properly! In case Darl +# committed a merge between P1 and P2, it will be propagated properly +# and all children of the merge will become merge commits with P1,P2 +# as their parents instead of the merge commit. +# +# To restrict rewriting to only part of the history, specify a revision +# range in addition to the new branch name. The new branch name will +# point to the top-most revision that a 'git rev-list' of this range +# will print. +# +# Consider this history: +# +# D--E--F--G--H +# / / +# A--B-----C +# +# To rewrite commits D,E,F,G,H, use: +# +# git-filter-branch ... new-H C..H +# +# To rewrite commits E,F,G,H, use one of these: +# +# git-filter-branch ... new-H C..H --not D +# git-filter-branch ... new-H D..H --not C +# +# To move the whole tree into a subdirectory, or remove it from there: +# +# git-filter-branch --index-filter \ +# 'git-ls-files -s | sed "s-\t-&newsubdir/-" | +# GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ +# git-update-index --index-info && +# mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' directorymoved + +# Testsuite: TODO + +set -e + +USAGE="git-filter-branch [-d TEMPDIR] [FILTERS] DESTBRANCH [REV-RANGE]" +. git-sh-setup + +map() +{ + # if it was not rewritten, take the original + test -r "$workdir/../map/$1" || echo "$1" + cat "$workdir/../map/$1" +} + +# When piped a commit, output a script to set the ident of either +# "author" or "committer + +set_ident () { + lid="$(echo "$1" | tr "A-Z" "a-z")" + uid="$(echo "$1" | tr "a-z" "A-Z")" + pick_id_script=' + /^'$lid' /{ + s/'\''/'\''\\'\'\''/g + h + s/^'$lid' \([^<]*\) <[^>]*> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/export GIT_'$uid'_NAME='\''&'\''/p + + g + s/^'$lid' [^<]* <\([^>]*\)> .*$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/export GIT_'$uid'_EMAIL='\''&'\''/p + + g + s/^'$lid' [^<]* <[^>]*> \(.*\)$/\1/ + s/'\''/'\''\'\'\''/g + s/.*/export GIT_'$uid'_DATE='\''&'\''/p + + q + } + ' + + LANG=C LC_ALL=C sed -ne "$pick_id_script" + # Ensure non-empty id name. + echo "[ -n \"\$GIT_${uid}_NAME\" ] || export GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\"" +} + +tempdir=.git-rewrite +filter_env= +filter_tree= +filter_index= +filter_parent= +filter_msg=cat +filter_commit='git-commit-tree "$@"' +filter_tag_name= +filter_subdir= +while case "$#" in 0) usage;; esac +do + case "$1" in + --) + shift + break + ;; + -*) + ;; + *) + break; + esac + + # all switches take one argument + ARG="$1" + case "$#" in 1) usage ;; esac + shift + OPTARG="$1" + shift + + case "$ARG" in + -d) + tempdir="$OPTARG" + ;; + --env-filter) + filter_env="$OPTARG" + ;; + --tree-filter) + filter_tree="$OPTARG" + ;; + --index-filter) + filter_index="$OPTARG" + ;; + --parent-filter) + filter_parent="$OPTARG" + ;; + --msg-filter) + filter_msg="$OPTARG" + ;; + --commit-filter) + filter_commit="$OPTARG" + ;; + --tag-name-filter) + filter_tag_name="$OPTARG" + ;; + --subdirectory-filter) + filter_subdir="$OPTARG" + ;; + *) + usage + ;; + esac +done + +dstbranch="$1" +shift +test -n "$dstbranch" || die "missing branch name" +git-show-ref "refs/heads/$dstbranch" 2> /dev/null && + die "branch $dstbranch already exists" + +test ! -e "$tempdir" || die "$tempdir already exists, please remove it" +mkdir -p "$tempdir/t" +cd "$tempdir/t" +workdir="$(pwd)" + +case "$GIT_DIR" in +/*) + ;; +*) + export GIT_DIR="$(pwd)/../../$GIT_DIR" + ;; +esac + +export GIT_INDEX_FILE="$(pwd)/../index" +git-read-tree # seed the index file + +ret=0 + + +mkdir ../map # map old->new commit ids for rewriting parents + +case "$filter_subdir" in +"") + git-rev-list --reverse --topo-order --default HEAD \ + --parents "$@" + ;; +*) + git-rev-list --reverse --topo-order --default HEAD \ + --parents --full-history "$@" -- "$filter_subdir" +esac > ../revs +commits=$(cat ../revs | wc -l | tr -d " ") + +test $commits -eq 0 && die "Found nothing to rewrite" + +i=0 +while read commit parents; do + i=$(($i+1)) + printf "$commit ($i/$commits) " + + case "$filter_subdir" in + "") + git-read-tree -i -m $commit + ;; + *) + git-read-tree -i -m $commit:"$filter_subdir" + esac + + export GIT_COMMIT=$commit + git-cat-file commit "$commit" >../commit + + eval "$(set_ident AUTHOR <../commit)" + eval "$(set_ident COMMITTER <../commit)" + eval "$filter_env" < /dev/null + + if [ "$filter_tree" ]; then + git-checkout-index -f -u -a + # files that $commit removed are now still in the working tree; + # remove them, else they would be added again + git-ls-files -z --others | xargs -0 rm -f + eval "$filter_tree" < /dev/null + git-diff-index -r $commit | cut -f 2- | tr '\n' '\0' | \ + xargs -0 git-update-index --add --replace --remove + git-ls-files -z --others | \ + xargs -0 git-update-index --add --replace --remove + fi + + eval "$filter_index" < /dev/null + + parentstr= + for parent in $parents; do + for reparent in $(map "$parent"); do + parentstr="$parentstr -p $reparent" + done + done + if [ "$filter_parent" ]; then + parentstr="$(echo "$parentstr" | eval "$filter_parent")" + fi + + sed -e '1,/^$/d' <../commit | \ + eval "$filter_msg" | \ + sh -c "$filter_commit" git-commit-tree $(git-write-tree) $parentstr | \ + tee ../map/$commit +done <../revs + +src_head=$(tail -n 1 ../revs | sed -e 's/ .*//') +target_head=$(head -n 1 ../map/$src_head) +case "$target_head" in +'') + echo Nothing rewritten + ;; +*) + git-update-ref refs/heads/"$dstbranch" $target_head + if [ $(cat ../map/$src_head | wc -l) -gt 1 ]; then + echo "WARNING: Your commit filter caused the head commit to expand to several rewritten commits. Only the first such commit was recorded as the current $dstbranch head but you will need to resolve the situation now (probably by manually merging the other commits). These are all the commits:" >&2 + sed 's/^/ /' ../map/$src_head >&2 + ret=1 + fi + ;; +esac + +if [ "$filter_tag_name" ]; then + git-for-each-ref --format='%(objectname) %(objecttype) %(refname)' refs/tags | + while read sha1 type ref; do + ref="${ref#refs/tags/}" + # XXX: Rewrite tagged trees as well? + if [ "$type" != "commit" -a "$type" != "tag" ]; then + continue; + fi + + if [ "$type" = "tag" ]; then + # Dereference to a commit + sha1t="$sha1" + sha1="$(git-rev-parse "$sha1"^{commit} 2>/dev/null)" || continue + fi + + [ -f "../map/$sha1" ] || continue + new_sha1="$(cat "../map/$sha1")" + export GIT_COMMIT="$sha1" + new_ref="$(echo "$ref" | eval "$filter_tag_name")" + + echo "$ref -> $new_ref ($sha1 -> $new_sha1)" + + if [ "$type" = "tag" ]; then + # Warn that we are not rewriting the tag object itself. + warn "unreferencing tag object $sha1t" + fi + + git-update-ref "refs/tags/$new_ref" "$new_sha1" + done +fi + +cd ../.. +rm -rf "$tempdir" +echo "Rewritten history saved to the $dstbranch branch" + +exit $ret diff --git a/git-submodule.sh b/git-submodule.sh index 8bdd99a2f3..89a3885350 100755 --- a/git-submodule.sh +++ b/git-submodule.sh @@ -25,6 +25,19 @@ say() fi } +# +# Map submodule path to submodule name +# +# $1 = path +# +module_name() +{ + name=$(GIT_CONFIG=.gitmodules git-config --get-regexp '^submodule\..*\.path$' "$1" | + sed -nre 's/^submodule\.(.+)\.path .+$/\1/p') + test -z "$name" && + die "No submodule mapping found in .gitmodules for path '$path'" + echo "$name" +} # # Clone a submodule @@ -49,7 +62,7 @@ module_clone() die "A file already exist at path '$path'" git-clone -n "$url" "$path" || - die "Clone of submodule '$path' failed" + die "Clone of '$url' into submodule path '$path' failed" } # @@ -63,17 +76,18 @@ modules_init() while read mode sha1 stage path do # Skip already registered paths - url=$(git-config submodule."$path".url) + name=$(module_name "$path") || exit + url=$(git-config submodule."$name".url) test -z "$url" || continue - url=$(GIT_CONFIG=.gitmodules git-config module."$path".url) + url=$(GIT_CONFIG=.gitmodules git-config submodule."$name".url) test -z "$url" && - die "No url found for submodule '$path' in .gitmodules" + die "No url found for submodule path '$path' in .gitmodules" - git-config submodule."$path".url "$url" || - die "Failed to register url for submodule '$path'" + git-config submodule."$name".url "$url" || + die "Failed to register url for submodule path '$path'" - say "Submodule '$path' registered with url '$url'" + say "Submodule '$name' ($url) registered for path '$path'" done } @@ -87,38 +101,40 @@ modules_update() git ls-files --stage -- "$@" | grep -e '^160000 ' | while read mode sha1 stage path do - url=$(git-config submodule."$path".url) + name=$(module_name "$path") || exit + url=$(git-config submodule."$name".url) if test -z "$url" then # Only mention uninitialized submodules when its # path have been specified test "$#" != "0" && - say "Submodule '$path' not initialized" + say "Submodule path '$path' not initialized" continue fi if ! test -d "$path"/.git then module_clone "$path" "$url" || exit + subsha1= + else + subsha1=$(unset GIT_DIR && cd "$path" && + git-rev-parse --verify HEAD) || + die "Unable to find current revision in submodule path '$path'" fi - subsha1=$(unset GIT_DIR && cd "$path" && - git-rev-parse --verify HEAD) || - die "Unable to find current revision of submodule '$path'" - if test "$subsha1" != "$sha1" then (unset GIT_DIR && cd "$path" && git-fetch && git-checkout -q "$sha1") || - die "Unable to checkout '$sha1' in submodule '$path'" + die "Unable to checkout '$sha1' in submodule path '$path'" - say "Submodule '$path': checked out '$sha1'" + say "Submodule path '$path': checked out '$sha1'" fi done } # -# List all registered submodules, prefixed with: +# List all submodules, prefixed with: # - submodule not initialized # + different revision checked out # @@ -132,7 +148,9 @@ modules_list() git ls-files --stage -- "$@" | grep -e '^160000 ' | while read mode sha1 stage path do - if ! test -d "$path"/.git + name=$(module_name "$path") || exit + url=$(git-config submodule."$name".url) + if test -z "url" || ! test -d "$path"/.git then say "-$sha1 $path" continue; diff --git a/git-svn.perl b/git-svn.perl index e35006142a..50128d7285 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -38,14 +38,16 @@ use IPC::Open3; use Git; BEGIN { - my $s; + # import functions from Git into our packages, en masse + no strict 'refs'; foreach (qw/command command_oneline command_noisy command_output_pipe command_input_pipe command_close_pipe/) { - $s .= "*SVN::Git::Editor::$_ = *SVN::Git::Fetcher::$_ = ". - "*Git::SVN::Migration::$_ = ". - "*Git::SVN::Log::$_ = *Git::SVN::$_ = *$_ = *Git::$_; "; + for my $package ( qw(SVN::Git::Editor SVN::Git::Fetcher + Git::SVN::Migration Git::SVN::Log Git::SVN), + __PACKAGE__) { + *{"${package}::$_"} = \&{"Git::$_"}; + } } - eval $s; } my ($SVN); @@ -846,26 +848,26 @@ BEGIN { # some options are read globally, but can be overridden locally # per [svn-remote "..."] section. Command-line options will *NOT* # override options set in an [svn-remote "..."] section - my $e; - foreach (qw/follow_parent no_metadata use_svm_props - use_svnsync_props/) { - my $key = $_; + no strict 'refs'; + for my $option (qw/follow_parent no_metadata use_svm_props + use_svnsync_props/) { + my $key = $option; $key =~ tr/_//d; - $e .= "sub $_ { - my (\$self) = \@_; - return \$self->{-$_} if exists \$self->{-$_}; - my \$k = \"svn-remote.\$self->{repo_id}\.$key\"; - eval { command_oneline(qw/config --get/, \$k) }; - if (\$@) { - \$self->{-$_} = \$Git::SVN::_$_; + my $prop = "-$option"; + *$option = sub { + my ($self) = @_; + return $self->{$prop} if exists $self->{$prop}; + my $k = "svn-remote.$self->{repo_id}.$key"; + eval { command_oneline(qw/config --get/, $k) }; + if ($@) { + $self->{$prop} = ${"Git::SVN::_$option"}; } else { - my \$v = command_oneline(qw/config --bool/,\$k); - \$self->{-$_} = \$v eq 'false' ? 0 : 1; + my $v = command_oneline(qw/config --bool/,$k); + $self->{$prop} = $v eq 'false' ? 0 : 1; } - return \$self->{-$_} }\n"; + return $self->{$prop}; + } } - $e .= "1;\n"; - eval $e or die $@; } my %LOCKFILES; @@ -1457,7 +1459,7 @@ sub tmp_config { my (@args) = @_; my $old_def_config = "$ENV{GIT_DIR}/svn/config"; my $config = "$ENV{GIT_DIR}/svn/.metadata"; - if (-e $old_def_config && ! -e $config) { + if (! -f $config && -f $old_def_config) { rename $old_def_config, $config or die "Failed rename $old_def_config => $config: $!\n"; } @@ -2899,17 +2901,17 @@ my ($can_do_switch, %ignored_err, $RA); BEGIN { # enforce temporary pool usage for some simple functions - my $e; - foreach (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) { - $e .= "sub $_ { - my \$self = shift; - my \$pool = SVN::Pool->new; - my \@ret = \$self->SUPER::$_(\@_,\$pool); - \$pool->clear; - wantarray ? \@ret : \$ret[0]; }\n"; + no strict 'refs'; + for my $f (qw/rev_proplist get_latest_revnum get_uuid get_repos_root/) { + my $SUPER = "SUPER::$f"; + *$f = sub { + my $self = shift; + my $pool = SVN::Pool->new; + my @ret = $self->$SUPER(@_,$pool); + $pool->clear; + wantarray ? @ret : $ret[0]; + }; } - - eval "$e; 1;" or die $@; } sub new { @@ -3072,11 +3074,8 @@ sub gs_do_switch { $editor->{git_commit_ok}; } -sub gs_fetch_loop_common { - my ($self, $base, $head, $gsv, $globs) = @_; - return if ($base > $head); - my $inc = $_log_window_size; - my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc); +sub longest_common_path { + my ($gsv, $globs) = @_; my %common; my $common_max = scalar @$gsv; @@ -3108,6 +3107,15 @@ sub gs_fetch_loop_common { last; } } + $longest_path; +} + +sub gs_fetch_loop_common { + my ($self, $base, $head, $gsv, $globs) = @_; + return if ($base > $head); + my $inc = $_log_window_size; + my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc); + my $longest_path = longest_common_path($gsv, $globs); while (1) { my %revs; my $err; diff --git a/git-svnimport.perl b/git-svnimport.perl index f4597626b9..b73d6494d8 100755 --- a/git-svnimport.perl +++ b/git-svnimport.perl @@ -867,34 +867,14 @@ sub commit { or die "Cannot write branch $dest for update: $!\n"; } - if($tag) { - my($in, $out) = ('',''); + if ($tag) { $last_rev = "-" if %$changed_paths; # the tag was 'complex', i.e. did not refer to a "real" revision $dest =~ tr/_/\./ if $opt_u; - $branch = $dest; - - my $pid = open2($in, $out, 'git-mktag'); - print $out ("object $cid\n". - "type commit\n". - "tag $dest\n". - "tagger $committer_name <$committer_email> 0 +0000\n") and - close($out) - or die "Cannot create tag object $dest: $!\n"; - - my $tagobj = <$in>; - chomp $tagobj; - - if ( !close($in) or waitpid($pid, 0) != $pid or - $? != 0 or $tagobj !~ /^[0123456789abcdef]{40}$/ ) { - die "Cannot create tag object $dest: $!\n"; - } - open(C,">$git_dir/refs/tags/$dest") and - print C ("$tagobj\n") and - close(C) - or die "Cannot create tag $branch: $!\n"; + system('git-tag', $dest, $cid) == 0 + or die "Cannot create tag $dest: $!\n"; print "Created tag '$dest' on '$branch'\n" if $opt_v; } diff --git a/git.spec.in b/git.spec.in index b9dc1d59eb..287057e816 100644 --- a/git.spec.in +++ b/git.spec.in @@ -164,11 +164,10 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/git-gui %{_bindir}/git-citool %{_datadir}/git-gui/ -# Not Yet... -# %{!?_without_docs: %{_mandir}/man1/git-gui.1} -# %{!?_without_docs: %doc Documentation/git-gui.html} -# %{!?_without_docs: %{_mandir}/man1/git-citool.1} -# %{!?_without_docs: %doc Documentation/git-citool.html} +%{!?_without_docs: %{_mandir}/man1/git-gui.1} +%{!?_without_docs: %doc Documentation/git-gui.html} +%{!?_without_docs: %{_mandir}/man1/git-citool.1} +%{!?_without_docs: %doc Documentation/git-citool.html} %files -n gitk %defattr(-,root,root) @@ -188,6 +187,9 @@ rm -rf $RPM_BUILD_ROOT %{!?_without_docs: %doc Documentation/technical} %changelog +* Thu Jun 21 2007 Shawn O. Pearce <spearce@spearce.org> +- Added documentation files for git-gui + * Tue May 13 2007 Quy Tonthat <qtonthat@gmail.com> - Added lib files for git-gui - Added Documentation/technical (As needed by Git Users Manual) diff --git a/merge-recursive.c b/merge-recursive.c index 4a82b741ae..c8539ec0ba 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -127,7 +127,7 @@ static void output(int v, const char *fmt, ...) va_end(args); } -static void flush_output() +static void flush_output(void) { struct output_buffer *b, *n; for (b = output_list; b; b = n) { @@ -150,7 +150,7 @@ static struct ref_list *sort_ref_list(struct ref_list *list) * Future: need to be in "struct repository" * when doing a full libification. */ -struct cached_refs { +static struct cached_refs { char did_loose; char did_packed; struct ref_list *loose; @@ -333,7 +333,6 @@ static int count_refspec_match(const char *pattern, for (weak_match = match = 0; refs; refs = refs->next) { char *name = refs->name; int namelen = strlen(name); - int weak_match; if (namelen < patlen || memcmp(name + namelen - patlen, pattern, patlen)) @@ -406,90 +405,96 @@ static struct ref *try_explicit_object_name(const char *name) return ref; } -static int match_explicit_refs(struct ref *src, struct ref *dst, - struct ref ***dst_tail, struct refspec *rs, - int rs_nr) +static struct ref *make_dst(const char *name, struct ref ***dst_tail) { - int i, errs; - for (i = errs = 0; i < rs_nr; i++) { - struct ref *matched_src, *matched_dst; + struct ref *dst; + size_t len; - const char *dst_value = rs[i].dst; + len = strlen(name) + 1; + dst = xcalloc(1, sizeof(*dst) + len); + memcpy(dst->name, name, len); + link_dst_tail(dst, dst_tail); + return dst; +} - if (rs[i].pattern) - continue; +static int match_explicit(struct ref *src, struct ref *dst, + struct ref ***dst_tail, + struct refspec *rs, + int errs) +{ + struct ref *matched_src, *matched_dst; - if (dst_value == NULL) - dst_value = rs[i].src; + const char *dst_value = rs->dst; - matched_src = matched_dst = NULL; - switch (count_refspec_match(rs[i].src, src, &matched_src)) { - case 1: - break; - case 0: - /* The source could be in the get_sha1() format - * not a reference name. :refs/other is a - * way to delete 'other' ref at the remote end. - */ - matched_src = try_explicit_object_name(rs[i].src); - if (matched_src) - break; - errs = 1; - error("src refspec %s does not match any.", - rs[i].src); - break; - default: - errs = 1; - error("src refspec %s matches more than one.", - rs[i].src); - break; - } - switch (count_refspec_match(dst_value, dst, &matched_dst)) { - case 1: - break; - case 0: - if (!memcmp(dst_value, "refs/", 5)) { - int len = strlen(dst_value) + 1; - matched_dst = xcalloc(1, sizeof(*dst) + len); - memcpy(matched_dst->name, dst_value, len); - link_dst_tail(matched_dst, dst_tail); - } - else if (!strcmp(rs[i].src, dst_value) && - matched_src) { - /* pushing "master:master" when - * remote does not have master yet. - */ - int len = strlen(matched_src->name) + 1; - matched_dst = xcalloc(1, sizeof(*dst) + len); - memcpy(matched_dst->name, matched_src->name, - len); - link_dst_tail(matched_dst, dst_tail); - } - else { - errs = 1; - error("dst refspec %s does not match any " - "existing ref on the remote and does " - "not start with refs/.", dst_value); - } - break; - default: - errs = 1; - error("dst refspec %s matches more than one.", - dst_value); + if (rs->pattern) + return errs; + + matched_src = matched_dst = NULL; + switch (count_refspec_match(rs->src, src, &matched_src)) { + case 1: + break; + case 0: + /* The source could be in the get_sha1() format + * not a reference name. :refs/other is a + * way to delete 'other' ref at the remote end. + */ + matched_src = try_explicit_object_name(rs->src); + if (matched_src) break; - } - if (errs) - continue; - if (matched_dst->peer_ref) { - errs = 1; - error("dst ref %s receives from more than one src.", - matched_dst->name); - } - else { - matched_dst->peer_ref = matched_src; - matched_dst->force = rs[i].force; - } + error("src refspec %s does not match any.", + rs->src); + break; + default: + matched_src = NULL; + error("src refspec %s matches more than one.", + rs->src); + break; + } + + if (!matched_src) + errs = 1; + + if (dst_value == NULL) + dst_value = matched_src->name; + + switch (count_refspec_match(dst_value, dst, &matched_dst)) { + case 1: + break; + case 0: + if (!memcmp(dst_value, "refs/", 5)) + matched_dst = make_dst(dst_value, dst_tail); + else + error("dst refspec %s does not match any " + "existing ref on the remote and does " + "not start with refs/.", dst_value); + break; + default: + matched_dst = NULL; + error("dst refspec %s matches more than one.", + dst_value); + break; + } + if (errs || matched_dst == NULL) + return 1; + if (matched_dst->peer_ref) { + errs = 1; + error("dst ref %s receives from more than one src.", + matched_dst->name); + } + else { + matched_dst->peer_ref = matched_src; + matched_dst->force = rs->force; } + return errs; +} + +static int match_explicit_refs(struct ref *src, struct ref *dst, + struct ref ***dst_tail, struct refspec *rs, + int rs_nr) +{ + int i, errs; + for (i = errs = 0; i < rs_nr; i++) + errs |= match_explicit(src, dst, dst_tail, &rs[i], errs); return -errs; } @@ -513,6 +518,11 @@ static const struct refspec *check_pattern_match(const struct refspec *rs, return NULL; } +/* + * Note. This is used only by "push"; refspec matching rules for + * push and fetch are subtly different, so do not try to reuse it + * without thinking. + */ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, int nr_refspec, char **refspec, int all) { @@ -536,10 +546,11 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, } if (pat) { - dst_name = xmalloc(strlen(pat->dst) + + const char *dst_side = pat->dst ? pat->dst : pat->src; + dst_name = xmalloc(strlen(dst_side) + strlen(src->name) - strlen(pat->src) + 2); - strcpy(dst_name, pat->dst); + strcpy(dst_name, dst_side); strcat(dst_name, src->name + strlen(pat->src)); } else dst_name = xstrdup(src->name); @@ -554,11 +565,8 @@ int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail, goto free_name; if (!dst_peer) { /* Create a new one and link it */ - int len = strlen(dst_name) + 1; - dst_peer = xcalloc(1, sizeof(*dst_peer) + len); - memcpy(dst_peer->name, dst_name, len); + dst_peer = make_dst(dst_name, dst_tail); hashcpy(dst_peer->new_sha1, src->new_sha1); - link_dst_tail(dst_peer, dst_tail); } dst_peer->peer_ref = src; free_name: diff --git a/revision.c b/revision.c index b12c25e2b0..1f4590b896 100644 --- a/revision.c +++ b/revision.c @@ -1180,7 +1180,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i); if (opts > 0) { - revs->diff = 1; + if (strcmp(argv[i], "-z")) + revs->diff = 1; i += opts - 1; continue; } diff --git a/sha1_file.c b/sha1_file.c index 2b860868f5..7628ee97d9 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -413,7 +413,7 @@ static size_t peak_pack_mapped; static size_t pack_mapped; struct packed_git *packed_git; -void pack_report() +void pack_report(void) { fprintf(stderr, "pack_report: getpagesize() = %10" SZ_FMT "\n" @@ -959,7 +959,7 @@ int check_sha1_signature(const unsigned char *sha1, void *map, unsigned long siz return hashcmp(sha1, real_sha1) ? -1 : 0; } -void *map_sha1_file(const unsigned char *sha1, unsigned long *size) +static void *map_sha1_file(const unsigned char *sha1, unsigned long *size) { struct stat st; void *map; diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index 6f6d8844e8..f1793d0b9a 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -171,6 +171,15 @@ test_expect_success 'test tracking setup via --track but deeper' \ test "$(git-config branch.my7.remote)" = local && test "$(git-config branch.my7.merge)" = refs/heads/o/o' +test_expect_success 'test deleting branch deletes branch config' \ + 'git-branch -d my7 && + test "$(git-config branch.my7.remote)" = "" && + test "$(git-config branch.my7.merge)" = ""' + +test_expect_success 'test deleting branch without config' \ + 'git-branch my7 s && + test "$(git-branch -d my7 2>&1)" = "Deleted branch my7."' + # Keep this test last, as it changes the current branch cat >expect <<EOF 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 2e3c20d6b9..90c085f828 100755 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -64,4 +64,17 @@ test_expect_success \ 'validate the output.' \ 'compare_diff_patch current expected' +test_expect_success 'favour same basenames over different ones' ' + cp path1 another-path && + git add another-path && + git commit -m 1 && + git rm path1 && + mkdir subdir && + git mv another-path subdir/path1 && + git runstatus | grep "renamed: .*path1 -> subdir/path1"' + +test_expect_success 'favour same basenames even with minor differences' ' + git show HEAD:path1 | sed "s/15/16/" > subdir/path1 && + git runstatus | grep "renamed: .*path1 -> subdir/path1"' + test_done diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh index dba018f667..08d58e1c8c 100755 --- a/t/t5516-fetch-push.sh +++ b/t/t5516-fetch-push.sh @@ -15,12 +15,58 @@ mk_empty () { ) } +mk_test () { + mk_empty && + ( + for ref in "$@" + do + git push testrepo $the_first_commit:refs/$ref || { + echo "Oops, push refs/$ref failure" + exit 1 + } + done && + cd testrepo && + for ref in "$@" + do + r=$(git show-ref -s --verify refs/$ref) && + test "z$r" = "z$the_first_commit" || { + echo "Oops, refs/$ref is wrong" + exit 1 + } + done && + git fsck --full + ) +} + +check_push_result () { + ( + cd testrepo && + it="$1" && + shift + for ref in "$@" + do + r=$(git show-ref -s --verify refs/$ref) && + test "z$r" = "z$it" || { + echo "Oops, refs/$ref is wrong" + exit 1 + } + done && + git fsck --full + ) +} + test_expect_success setup ' : >path1 && git add path1 && test_tick && git commit -a -m repo && + the_first_commit=$(git show-ref -s --verify refs/heads/master) && + + : >path2 && + git add path2 && + test_tick && + git commit -a -m second && the_commit=$(git show-ref -s --verify refs/heads/master) ' @@ -79,4 +125,122 @@ test_expect_success 'push with wildcard' ' ) ' +test_expect_success 'push with matching heads' ' + + mk_test heads/master && + git push testrepo && + check_push_result $the_commit heads/master + +' + +test_expect_success 'push with no ambiguity (1)' ' + + mk_test heads/master && + git push testrepo master:master && + check_push_result $the_commit heads/master + +' + +test_expect_success 'push with no ambiguity (2)' ' + + mk_test remotes/origin/master && + git push testrepo master:master && + check_push_result $the_commit remotes/origin/master + +' + +test_expect_success 'push with weak ambiguity (1)' ' + + mk_test heads/master remotes/origin/master && + git push testrepo master:master && + check_push_result $the_commit heads/master && + check_push_result $the_first_commit remotes/origin/master + +' + +test_expect_success 'push with weak ambiguity (2)' ' + + mk_test heads/master remotes/origin/master remotes/another/master && + git push testrepo master:master && + check_push_result $the_commit heads/master && + check_push_result $the_first_commit remotes/origin/master remotes/another/master + +' + +test_expect_success 'push with ambiguity (1)' ' + + mk_test remotes/origin/master remotes/frotz/master && + if git push testrepo master:master + then + echo "Oops, should have failed" + false + else + check_push_result $the_first_commit remotes/origin/master remotes/frotz/master + fi +' + +test_expect_success 'push with ambiguity (2)' ' + + mk_test heads/frotz tags/frotz && + if git push testrepo master:frotz + then + echo "Oops, should have failed" + false + else + check_push_result $the_first_commit heads/frotz tags/frotz + fi + +' + +test_expect_success 'push with colon-less refspec (1)' ' + + mk_test heads/frotz tags/frotz && + git branch -f frotz master && + git push testrepo frotz && + check_push_result $the_commit heads/frotz && + check_push_result $the_first_commit tags/frotz + +' + +test_expect_success 'push with colon-less refspec (2)' ' + + mk_test heads/frotz tags/frotz && + if git show-ref --verify -q refs/heads/frotz + then + git branch -D frotz + fi && + git tag -f frotz && + git push testrepo frotz && + check_push_result $the_commit tags/frotz && + check_push_result $the_first_commit heads/frotz + +' + +test_expect_success 'push with colon-less refspec (3)' ' + + mk_test && + if git show-ref --verify -q refs/tags/frotz + then + git tag -d frotz + fi && + git branch -f frotz master && + git push testrepo frotz && + check_push_result $the_commit heads/frotz && + test "$( cd testrepo && git show-ref | wc -l )" = 1 +' + +test_expect_success 'push with colon-less refspec (4)' ' + + mk_test && + if git show-ref --verify -q refs/heads/frotz + then + git branch -D frotz + fi && + git tag -f frotz && + git push testrepo frotz && + check_push_result $the_commit tags/frotz && + test "$( cd testrepo && git show-ref | wc -l )" = 1 + +' + test_done diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh new file mode 100755 index 0000000000..f00c262e45 --- /dev/null +++ b/t/t7003-filter-branch.sh @@ -0,0 +1,110 @@ +#!/bin/sh + +test_description='git-filter-branch' +. ./test-lib.sh + +make_commit () { + lower=$(echo $1 | tr A-Z a-z) + echo $lower > $lower + git add $lower + test_tick + git commit -m $1 + git tag $1 +} + +test_expect_success 'setup' ' + make_commit A + make_commit B + git checkout -b branch B + make_commit D + make_commit E + git checkout master + make_commit C + git checkout branch + git merge C + git tag F + make_commit G + make_commit H +' + +H=$(git-rev-parse H) + +test_expect_success 'rewrite identically' ' + git-filter-branch H2 +' + +test_expect_success 'result is really identical' ' + test $H = $(git-rev-parse H2) +' + +test_expect_success 'rewrite, renaming a specific file' ' + git-filter-branch --tree-filter "mv d doh || :" H3 +' + +test_expect_success 'test that the file was renamed' ' + test d = $(git show H3:doh) +' + +git tag oldD H3~4 +test_expect_success 'rewrite one branch, keeping a side branch' ' + git-filter-branch --tree-filter "mv b boh || :" modD D..oldD +' + +test_expect_success 'common ancestor is still common (unchanged)' ' + test "$(git-merge-base modD D)" = "$(git-rev-parse B)" +' + +test_expect_success 'filter subdirectory only' ' + mkdir subdir && + touch subdir/new && + git add subdir/new && + test_tick && + git commit -m "subdir" && + echo H > a && + test_tick && + git commit -m "not subdir" a && + echo A > subdir/new && + test_tick && + git commit -m "again subdir" subdir/new && + git rm a && + test_tick && + git commit -m "again not subdir" && + git-filter-branch --subdirectory-filter subdir sub +' + +test_expect_success 'subdirectory filter result looks okay' ' + test 2 = $(git-rev-list sub | wc -l) && + git show sub:new && + ! git show sub:subdir +' + +test_expect_success 'setup and filter history that requires --full-history' ' + git checkout master && + mkdir subdir && + echo A > subdir/new && + git add subdir/new && + test_tick && + git commit -m "subdir on master" subdir/new && + git rm a && + test_tick && + git commit -m "again subdir on master" && + git merge branch && + git-filter-branch --subdirectory-filter subdir sub-master +' + +test_expect_success 'subdirectory filter result looks okay' ' + test 3 = $(git-rev-list -1 --parents sub-master | wc -w) && + git show sub-master^:new && + git show sub-master^2:new && + ! git show sub:subdir +' + +test_expect_success 'use index-filter to move into a subdirectory' ' + git-filter-branch --index-filter \ + "git-ls-files -s | sed \"s-\\t-&newsubdir/-\" | + GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \ + git-update-index --index-info && + mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved && + test -z "$(git diff HEAD directorymoved:newsubdir)"' + +test_done diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh index 3940433b8f..7a9b505b13 100755 --- a/t/t7400-submodule-basic.sh +++ b/t/t7400-submodule-basic.sh @@ -18,7 +18,7 @@ subcommands of git-submodule. # -add directory lib to 'superproject', this creates a DIRLINK entry # -add a couple of regular files to enable testing of submodule filtering # -mv lib subrepo -# -add an entry to .gitmodules for path 'lib' +# -add an entry to .gitmodules for submodule 'example' # test_expect_success 'Prepare submodule testing' ' mkdir lib && @@ -40,7 +40,19 @@ test_expect_success 'Prepare submodule testing' ' git-add a lib z && git-commit -m "super commit 1" && mv lib .subrepo && - GIT_CONFIG=.gitmodules git-config module.lib.url git://example.com/lib.git + GIT_CONFIG=.gitmodules git-config submodule.example.url git://example.com/lib.git +' + +test_expect_success 'status should fail for unmapped paths' ' + if git-submodule status + then + echo "[OOPS] submodule status succeeded" + false + elif ! GIT_CONFIG=.gitmodules git-config submodule.example.path lib + then + echo "[OOPS] git-config failed to update .gitmodules" + false + fi ' test_expect_success 'status should only print one line' ' @@ -54,12 +66,12 @@ test_expect_success 'status should initially be "missing"' ' test_expect_success 'init should register submodule url in .git/config' ' git-submodule init && - url=$(git-config submodule.lib.url) && + url=$(git-config submodule.example.url) && if test "$url" != "git://example.com/lib.git" then echo "[OOPS] init succeeded but submodule url is wrong" false - elif ! git-config submodule.lib.url ./.subrepo + elif ! git-config submodule.example.url ./.subrepo then echo "[OOPS] init succeeded but update of url failed" false @@ -72,7 +84,7 @@ test_expect_success 'update should fail when path is used by a file' ' then echo "[OOPS] update should have failed" false - elif test -f lib && test "$(cat lib)" != "hello" + elif test "$(cat lib)" != "hello" then echo "[OOPS] update failed but lib file was molested" false diff --git a/t/t9113-git-svn-dcommit-new-file.sh b/t/t9113-git-svn-dcommit-new-file.sh new file mode 100755 index 0000000000..9ef0db9044 --- /dev/null +++ b/t/t9113-git-svn-dcommit-new-file.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Copyright (c) 2007 Eric Wong +# + +# Don't run this test by default unless the user really wants it +# I don't like the idea of taking a port and possibly leaving a +# daemon running on a users system if the test fails. +# Not all git users will need to interact with SVN. +test -z "$SVNSERVE_PORT" && exit 0 + +test_description='git-svn dcommit new files over svn:// test' + +. ./lib-git-svn.sh + +start_svnserve () { + svnserve --listen-port $SVNSERVE_PORT \ + --root $rawsvnrepo \ + --listen-once \ + --listen-host 127.0.0.1 & +} + +test_expect_success 'start tracking an empty repo' " + svn mkdir -m 'empty dir' $svnrepo/empty-dir && + echo anon-access = write >> $rawsvnrepo/conf/svnserve.conf && + start_svnserve && + git svn init svn://127.0.0.1:$SVNSERVE_PORT && + git svn fetch + " + +test_expect_success 'create files in new directory with dcommit' " + mkdir git-new-dir && + echo hello > git-new-dir/world && + git update-index --add git-new-dir/world && + git commit -m hello && + start_svnserve && + git svn dcommit + " + +test_done diff --git a/t/t9400-git-cvsserver-server.sh b/t/t9400-git-cvsserver-server.sh index 41dcf646d1..0331770686 100755 --- a/t/t9400-git-cvsserver-server.sh +++ b/t/t9400-git-cvsserver-server.sh @@ -143,6 +143,51 @@ test_expect_success 'req_Root failure (conflicting roots)' \ 'cat request-conflict | git-cvsserver pserver >log 2>&1 && tail log | grep -q "^error 1 Conflicting roots specified$"' +test_expect_success 'req_Root (strict paths)' \ + 'cat request-anonymous | git-cvsserver --strict-paths pserver $SERVERDIR >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_failure 'req_Root failure (strict-paths)' \ + 'cat request-anonymous | git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1' + +test_expect_success 'req_Root (w/o strict-paths)' \ + 'cat request-anonymous | git-cvsserver pserver $WORKDIR/ >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_failure 'req_Root failure (w/o strict-paths)' \ + 'cat request-anonymous | git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1' + +cat >request-base <<EOF +BEGIN AUTH REQUEST +/gitcvs.git +anonymous + +END AUTH REQUEST +Root /gitcvs.git +EOF + +test_expect_success 'req_Root (base-path)' \ + 'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_failure 'req_Root failure (base-path)' \ + 'cat request-anonymous | git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1' + +GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1 + +test_expect_success 'req_Root (export-all)' \ + 'cat request-anonymous | git-cvsserver --export-all pserver $WORKDIR >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +test_expect_failure 'req_Root failure (export-all w/o whitelist)' \ + 'cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || + false' + +test_expect_success 'req_Root (everything together)' \ + 'cat request-base | git-cvsserver --export-all --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 && + tail -n1 log | grep -q "^I LOVE YOU$"' + +GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1 #-------------- # CONFIG TESTS diff --git a/tree-walk.h b/tree-walk.h index ee747aba0b..db0fbdc701 100644 --- a/tree-walk.h +++ b/tree-walk.h @@ -22,7 +22,7 @@ static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, co static inline int tree_entry_len(const char *name, const unsigned char *sha1) { - return (char *)sha1 - (char *)name - 1; + return (const char *)sha1 - name - 1; } void update_tree_entry(struct tree_desc *); |