Merge branch 'am/status'
authorShawn O. Pearce <spearce@spearce.org>
Thu, 25 Sep 2008 16:32:49 +0000 (09:32 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Thu, 25 Sep 2008 16:32:49 +0000 (09:32 -0700)
* am/status:
  wt-status: Teach how to discard changes in the working directory
  wt-status: Split header generation into three functions

161 files changed:
Documentation/Makefile
Documentation/RelNotes-1.6.0.2.txt
Documentation/RelNotes-1.6.0.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.1.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-apply.txt
Documentation/git-for-each-ref.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-repack.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitdiffcore.txt
Documentation/merge-config.txt
Documentation/pretty-formats.txt
GIT-VERSION-GEN
INSTALL
Makefile
builtin-apply.c
builtin-archive.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clone.c
builtin-commit-tree.c
builtin-commit.c
builtin-count-objects.c
builtin-diff-files.c
builtin-diff-tree.c
builtin-diff.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-for-each-ref.c
builtin-http-fetch.c
builtin-init-db.c
builtin-log.c
builtin-merge.c
builtin-pack-objects.c
builtin-prune.c
builtin-read-tree.c
builtin-remote.c
builtin-rev-list.c
builtin-rm.c
builtin-send-pack.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin.h
combine-diff.c
compat/fnmatch/fnmatch.c [moved from compat/fnmatch.c with 100% similarity]
compat/fnmatch/fnmatch.h [moved from compat/fnmatch.h with 100% similarity]
compat/regex/regex.c [moved from compat/regex.c with 100% similarity]
compat/regex/regex.h [moved from compat/regex.h with 100% similarity]
config.mak.in
configure.ac
contrib/completion/git-completion.bash
contrib/hooks/setgitperms.perl
csum-file.c
daemon.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore.h
fast-import.c
git-bisect.sh
git-gui/.gitattributes [new file with mode: 0644]
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/commit.tcl
git-gui/lib/diff.tcl
git-gui/lib/encoding.tcl
git-gui/lib/mergetool.tcl
git-gui/lib/option.tcl
git-gui/po/de.po
git-rebase--interactive.sh
git-repack.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.spec.in
gitweb/gitweb.perl
graph.c
graph.h
help.c
help.h
http-push.c
http.c
index-pack.c
levenshtein.c [new file with mode: 0644]
levenshtein.h [new file with mode: 0644]
log-tree.c
log-tree.h
object.h
pack-write.c
perl/Git.pm
pretty.c
read-cache.c
refs.c
remote.c
remote.h
revision.c
revision.h
sha1_file.c
t/lib-git-svn.sh
t/t0024-crlf-archive.sh [new file with mode: 0644]
t/t1300-repo-config.sh
t/t4018-diff-funcname.sh
t/t4019-diff-wserror.sh
t/t5300-pack-object.sh
t/t5510-fetch.sh
t/t6013-rev-list-reverse-parents.sh [new file with mode: 0755]
t/t6023-merge-file.sh
t/t6030-bisect-porcelain.sh
t/t6300-for-each-ref.sh
t/t7201-co.sh
t/t7501-commit.sh
t/t7603-merge-reduce-heads.sh
t/t7606-merge-custom.sh
t/t9100-git-svn-basic.sh
t/t9101-git-svn-props.sh
t/t9102-git-svn-deep-rmdir.sh
t/t9103-git-svn-tracked-directory-removed.sh
t/t9104-git-svn-follow-parent.sh
t/t9105-git-svn-commit-diff.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9106-git-svn-dcommit-clobber-series.sh
t/t9107-git-svn-migrate.sh
t/t9108-git-svn-glob.sh
t/t9108-git-svn-multi-glob.sh
t/t9110-git-svn-use-svm-props.sh
t/t9111-git-svn-use-svnsync-props.sh
t/t9112-git-svn-md5less-file.sh
t/t9113-git-svn-dcommit-new-file.sh
t/t9114-git-svn-dcommit-merge.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9116-git-svn-log.sh
t/t9117-git-svn-init-clone.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9119-git-svn-info.sh
t/t9120-git-svn-clone-with-percent-escapes.sh
t/t9121-git-svn-fetch-renamed-dir.sh
t/t9122-git-svn-author.sh
t/t9123-git-svn-rebuild-with-rewriteroot.sh
t/t9124-git-svn-dcommit-auto-props.sh
t/t9125-git-svn-multi-glob-branch-names.sh
t/t9126-git-svn-follow-deleted-readded-directory.sh [new file with mode: 0755]
t/t9126/follow-deleted-readded.dump [new file with mode: 0644]
t/t9127-git-svn-partial-rebuild.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9300-fast-import.sh
t/t9301-fast-export.sh
t/t9600-cvsimport.sh
t/t9700-perl-git.sh
t/t9700/test.pl
templates/Makefile
unpack-trees.c
unpack-trees.h

index 62269e3..ded0e40 100644 (file)
@@ -44,6 +44,7 @@ MANPAGE_XSL = callouts.xsl
 INSTALL?=install
 RM ?= rm -f
 DOC_REF = origin/man
+HTML_REF = origin/html
 
 infodir?=$(prefix)/share/info
 MAKEINFO=makeinfo
@@ -222,4 +223,7 @@ install-webdoc : html
 quick-install:
        sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
 
+quick-install-html:
+       sh ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
+
 .PHONY: .FORCE-GIT-VERSION-FILE
index 6c5446b..7a9646f 100644 (file)
@@ -17,6 +17,10 @@ Fixes since v1.6.0.1
 * Many commands did not use the correct working tree location when used
   with GIT_WORK_TREE environment settings.
 
+* Some systems needs to use compatibility fnmach and regex libraries
+  independent from each other; the compat/ area has been reorganized to
+  allow this.
+
 
 * "git apply --unidiff-zero" incorrectly applied a -U0 patch that inserts
   a new line before the second line.
@@ -24,9 +28,15 @@ Fixes since v1.6.0.1
 * "git blame -c" did not exactly work like "git annotate" when range
   boundaries are involved.
 
+* "git checkout file" when file is still unmerged checked out contents from
+  a random high order stage, which was confusing.
+
 * "git clone $there $here/" with extra trailing slashes after explicit
   local directory name $here did not work as expected.
 
+* "git diff" on tracked contents with CRLF line endings did not drive "less"
+  intelligently when showing added or removed lines.
+
 * "git diff --dirstat -M" did not add changes in subdirectories up
   correctly for renamed paths.
 
@@ -42,18 +52,29 @@ Fixes since v1.6.0.1
 
 * "git gui" translation updates and i18n fixes.
 
+* "git index-pack" is more careful against disk corruption while completing
+  a thin pack.
+
 * "git log -i --grep=pattern" did not ignore case; neither "git log -E
   --grep=pattern" triggered extended regexp.
 
 * "git log --pretty="%ad" --date=short" did not use short format when
   showing the timestamp.
 
+* "git log --author=author" match incorrectly matched with the
+  timestamp part of "author " line in commit objects.
+
+* "git log -F --author=author" did not work at all.
+
 * Build procedure for "git shell" that used stub versions of some
   functions and globals was not understood by linkers on some platforms.
 
 * "git stash" was fooled by a stat-dirty but otherwise unmodified paths
   and refused to work until the user refreshed the index.
 
+* "git svn" was broken on Perl before 5.8 with recent fixes to reduce
+  use of temporary files.
+
 * "git verify-pack -v" did not work correctly when given more than one
   packfile.
 
@@ -61,7 +82,6 @@ Also contains many documentation updates.
 
 --
 exec >/var/tmp/1
-O=v1.6.0.1-61-g1eff26c
+O=v1.6.0.1-78-g3632cfc
 echo O=$(git describe maint)
 git shortlog --no-merges $O..maint
-
diff --git a/Documentation/RelNotes-1.6.0.3.txt b/Documentation/RelNotes-1.6.0.3.txt
new file mode 100644 (file)
index 0000000..46e13a4
--- /dev/null
@@ -0,0 +1,45 @@
+GIT v1.6.0.3 Release Notes
+==========================
+
+Fixes since v1.6.0.2
+--------------------
+
+* "git archive --format=zip" did not honor core.autocrlf while
+  --format=tar did.
+
+* Continuing "git rebase -i" was very confused when the user left modified
+  files in the working tree while resolving conflicts.
+
+* Continuing "git rebase -i" was also very confused when the user left
+  some staged changes in the index after "edit".
+
+* Behaviour of "git diff --quiet" was inconsistent with "diff --exit-code"
+  with the output redirected to /dev/null.
+
+* "git stash apply sash@{1}" was fixed to error out.  Prior versions
+  would have applied stash@{0} incorrectly.
+
+* "git for-each-ref --format=%(subject)" fixed for commits with no
+  no newline in the message body.
+
+* "git remote" fixed to protect printf from user input.
+
+* "git checkout -q" once again suppresses the locally modified file list.
+
+* Cross-directory renames are no longer used when creating packs.  This
+  allows more graceful behavior on filesystems like sshfs.
+
+* Stale temporary files under $GIT_DIR/objects/pack are now cleaned up
+  automatically by "git prune".
+
+* "Git.pm" tests relied on unnecessarily more recent version of Perl.
+
+* "gitweb" triggered undef warning on commits without log messages.
+
+Many other documentation updates.
+
+--
+exec >/var/tmp/1
+O=v1.6.0.2-41-g7fe4a72
+echo O=$(git describe maint)
+git shortlog --no-merges $O..maint
index 609d4ca..421e569 100644 (file)
@@ -32,8 +32,19 @@ on.
 * Most of the test scripts (but not the ones that try to run servers)
   can be run in parallel.
 
+* Bash completion of refnames in a repository with massive number of
+  refs has been optimized.
+
 (usability, bells and whistles)
 
+* When you mistype a command name, git helpfully suggests what it guesses
+  you might have meant to say.  help.autocorrect configuration can be set
+  to a non-zero value to accept the suggestion when git can uniquely
+  guess.
+
+* "git bisect" is careful about a user mistake and suggests testing of
+  merge base first when good is not a strict ancestor of bad.
+
 * "git checkout --track origin/hack" used to be a syntax error.  It now
   DWIMs to create a corresponding local branch "hack", i.e. acts as if you
   said "git checkout --track -b hack origin/hack".
@@ -54,6 +65,12 @@ on.
 * "git diff" learned to put more sensible hunk headers for Python and
   HTML contents.
 
+* "git diff" learned to vary the a/ vs b/ prefix depending on what are
+  being compared, controlled by diff.mnemonicprefix configuration.
+
+* "git for-each-ref" learned "refname:short" token that gives an
+  unambiguously abbreviated refname.
+
 * "git help" learned to use GIT_MAN_VIEWER environment variable before
   using "man" program.
 
@@ -68,6 +85,9 @@ on.
 * "git log" learned --simplify-merges, a milder variant of --full-history;
   "gitk --simplify-merges" is easier to view than with --full-history.
 
+* "git log --pretty=format:" learned "%d" format element that inserts
+  names of tags that point at the commit.
+
 * "git merge --squash" and "git merge --no-ff" into an unborn branch are
   noticed as user errors.
 
@@ -115,6 +135,6 @@ release, unless otherwise noted.
 
 --
 exec >/var/tmp/1
-O=v1.6.0.1-266-gaf9552f
+O=v1.6.0.2-295-g34a5d35
 echo O=$(git describe master)
 git shortlog --no-merges $O..master ^maint
index ed3285f..bea867d 100644 (file)
@@ -585,6 +585,22 @@ diff.external::
        you want to use an external diff program only on a subset of
        your files, you might want to use linkgit:gitattributes[5] instead.
 
+diff.mnemonicprefix::
+       If set, 'git-diff' uses a prefix pair that is different from the
+       standard "a/" and "b/" depending on what is being compared.  When
+       this configuration is in effect, reverse diff output also swaps
+       the order of the prefixes:
+'git-diff';;
+       compares the (i)ndex and the (w)ork tree;
+'git-diff HEAD';;
+        compares a (c)ommit and the (w)ork tree;
+'git diff --cached';;
+       compares a (c)ommit and the (i)ndex;
+'git-diff HEAD:file1 file2';;
+       compares an (o)bject and a (w)ork tree entity;
+'git diff --no-index a b';;
+       compares two non-git things (1) and (2).
+
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
        detection; equivalent to the 'git-diff' option '-l'.
@@ -790,6 +806,15 @@ help.format::
        Values 'man', 'info', 'web' and 'html' are supported. 'man' is
        the default. 'web' and 'html' are the same.
 
+help.autocorrect::
+       Automatically correct and execute mistyped commands after
+       waiting for the given number of deciseconds (0.1 sec). If more
+       than one command can be deduced from the entered text, nothing
+       will be executed.  If the value of this option is negative,
+       the corrected command will be executed immediately. If the
+       value is 0 - the command will be just shown but not executed.
+       This is the default.
+
 http.proxy::
        Override the HTTP proxy, normally configured using the 'http_proxy'
        environment variable (see linkgit:curl[1]).  This can be overridden
index 6e26832..7788d4f 100644 (file)
@@ -65,6 +65,9 @@ endif::git-format-patch[]
        can be set with "--dirstat=limit". Changes in a child directory is not
        counted for the parent directory, unless "--cumulative" is used.
 
+--dirstat-by-file[=limit]::
+       Same as --dirstat, but counts changed files instead of lines.
+
 --summary::
        Output a condensed summary of extended header information
        such as creations, renames and mode changes.
index feb51f1..e726510 100644 (file)
@@ -14,7 +14,8 @@ SYNOPSIS
          [--allow-binary-replacement | --binary] [--reject] [-z]
          [-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
          [--whitespace=<nowarn|warn|fix|error|error-all>]
-         [--exclude=PATH] [--directory=<root>] [--verbose] [<patch>...]
+         [--exclude=PATH] [--include=PATH] [--directory=<root>]
+         [--verbose] [<patch>...]
 
 DESCRIPTION
 -----------
@@ -137,6 +138,17 @@ discouraged.
        be useful when importing patchsets, where you want to exclude certain
        files or directories.
 
+--include=<path-pattern>::
+       Apply changes to files matching the given path pattern. This can
+       be useful when importing patchsets, where you want to include certain
+       files or directories.
++
+When --exclude and --include patterns are used, they are examined in the
+order they appear on the command line, and the first match determines if a
+patch to each path is used.  A patch to a path that does not match any
+include/exclude pattern is used by default if there is no include pattern
+on the command line, and ignored if there is any include pattern.
+
 --whitespace=<action>::
        When applying a patch, detect a new or modified line that has
        whitespace errors.  What are considered whitespace errors is
index ebd7c5f..5061d3e 100644 (file)
@@ -74,6 +74,7 @@ For all objects, the following names can be used:
 
 refname::
        The name of the ref (the part after $GIT_DIR/).
+       For a non-ambiguous short name of the ref append `:short`.
 
 objecttype::
        The type of the object (`blob`, `tree`, `commit`, `tag`).
index 6f4b9b0..309deac 100644 (file)
@@ -160,7 +160,10 @@ Here are the "carry forward" rules:
       0 nothing             nothing  nothing  (does not happen)
       1 nothing             nothing  exists   use M
       2 nothing             exists   nothing  remove path from index
-      3 nothing             exists   exists   use M
+      3 nothing             exists   exists,  use M if "initial checkout"
+                                    H == M   keep index otherwise
+                                    exists   fail
+                                    H != M
 
         clean I==H  I==M
        ------------------
@@ -207,6 +210,12 @@ you picked it up via e-mail in a patch form), `git diff-index
 merge, but it would not show in `git diff-index --cached $M`
 output after two-tree merge.
 
+Case #3 is slightly tricky and needs explanation.  The result from this
+rule logically should be to remove the path if the user staged the removal
+of the path and then swiching to a new branch.  That however will prevent
+the initial checkout from happening, so the rule is modified to use M (new
+tree) only when the contents of the index is empty.  Otherwise the removal
+of the path is kept as long as $H and $M are the same.
 
 3-Way Merge
 ~~~~~~~~~~~
index 59c1b02..32f0f12 100644 (file)
@@ -92,7 +92,7 @@ branch to another, to pretend that you forked the topic branch
 from the latter branch, using `rebase --onto`.
 
 First let's assume your 'topic' is based on branch 'next'.
-For example feature developed in 'topic' depends on some
+For example, a feature developed in 'topic' depends on some
 functionality which is found in 'next'.
 
 ------------
@@ -103,9 +103,9 @@ functionality which is found in 'next'.
                             o---o---o  topic
 ------------
 
-We would want to make 'topic' forked from branch 'master',
-for example because the functionality 'topic' branch depend on
-got merged into more stable 'master' branch, like this:
+We want to make 'topic' forked from branch 'master'; for example,
+because the functionality on which 'topic' depends was merged into the
+more stable 'master' branch. We want our tree to look like this:
 
 ------------
     o---o---o---o---o  master
index 38ac609..bbe1485 100644 (file)
@@ -60,7 +60,7 @@ OPTIONS
        linkgit:git-pack-objects[1].
 
 -f::
-       Pass the `--no-reuse-delta` option to 'git-pack-objects'. See
+       Pass the `--no-reuse-object` option to `git-pack-objects`, see
        linkgit:git-pack-objects[1].
 
 -q::
index 36afad8..278cf73 100644 (file)
@@ -26,6 +26,7 @@ The following browsers (or commands) are currently supported:
 * lynx
 * dillo
 * open (this is the default under Mac OS X GUI)
+* start (this is the default under MinGW)
 
 Custom commands may also be specified.
 
@@ -77,7 +78,7 @@ the URLs passed as arguments.
 Note about konqueror
 --------------------
 
-When 'konqueror' is specified by the a command line option or a
+When 'konqueror' is specified by a command line option or a
 configuration variable, we launch 'kfmclient' to try to open the HTML
 man page on an already opened konqueror in a new tab if possible.
 
index e178fb5..df420ae 100644 (file)
@@ -43,10 +43,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.6.1/git.html[documentation for release 1.6.1]
+* link:v1.6.0.2/git.html[documentation for release 1.6.0.2]
 
 * release notes for
-  link:RelNotes-1.6.1.txt[1.6.1],
+  link:RelNotes-1.6.0.2.txt[1.6.0.2],
+  link:RelNotes-1.6.0.1.txt[1.6.0.1],
   link:RelNotes-1.6.0.txt[1.6.0].
 
 * link:v1.5.6.5/git.html[documentation for release 1.5.6.5]
index 5fb5007..e848c94 100644 (file)
@@ -270,27 +270,27 @@ See linkgit:git[1] for details.
 Defining a custom hunk-header
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Each group of changes (called "hunk") in the textual diff output
+Each group of changes (called "hunk") in the textual diff output
 is prefixed with a line of the form:
 
        @@ -k,l +n,m @@ TEXT
 
-The text is called 'hunk header', and by default a line that
-begins with an alphabet, an underscore or a dollar sign is used,
-which matches what GNU 'diff -p' output uses.  This default
-selection however is not suited for some contents, and you can
-use customized pattern to make a selection.
+This is called a 'hunk header'.  The "TEXT" portion is by default a line
+that begins with an alphabet, an underscore or a dollar sign; this
+matches what GNU 'diff -p' output uses.  This default selection however
+is not suited for some contents, and you can use a customized pattern
+to make a selection.
 
-First in .gitattributes, you would assign the `diff` attribute
+First, in .gitattributes, you would assign the `diff` attribute
 for paths.
 
 ------------------------
 *.tex  diff=tex
 ------------------------
 
-Then, you would define "diff.tex.funcname" configuration to
+Then, you would define "diff.tex.funcname" configuration to
 specify a regular expression that matches a line that you would
-want to appear as the hunk header, like this:
+want to appear as the hunk header "TEXT", like this:
 
 ------------------------
 [diff "tex"]
@@ -311,18 +311,20 @@ patterns are available:
 
 - `bibtex` suitable for files with BibTeX coded references.
 
-- `java` suitable for source code in the Java lanugage.
+- `html` suitable for HTML/XHTML documents.
+
+- `java` suitable for source code in the Java language.
 
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
+- `php` suitable for source code in the PHP language.
+
 - `python` suitable for source code in the Python language.
 
 - `ruby` suitable for source code in the Ruby language.
 
 - `tex` suitable for source code for LaTeX documents.
 
-- `html` suitable for HTML/XHTML documents.
-
 
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
index 2bdbc3d..e8041bc 100644 (file)
@@ -36,11 +36,25 @@ files:
 
  - 'git-diff-tree' compares contents of two "tree" objects;
 
-In all of these cases, the commands themselves compare
-corresponding paths in the two sets of files.  The result of
-comparison is passed from these commands to what is internally
-called "diffcore", in a format similar to what is output when
-the -p option is not used.  E.g.
+In all of these cases, the commands themselves first optionally limit
+the two sets of files by any pathspecs given on their command-lines,
+and compare corresponding paths in the two resulting sets of files.
+
+The pathspecs are used to limit the world diff operates in.  They remove
+the filepairs outside the specified sets of pathnames.  E.g. If the
+input set of filepairs included:
+
+------------------------------------------------
+:100644 100644 bcd1234... 0123456... M junkfile
+------------------------------------------------
+
+but the command invocation was `git diff-files myfile`, then the
+junkfile entry would be removed from the list because only "myfile"
+is under consideration.
+
+The result of comparison is passed from these commands to what is
+internally called "diffcore", in a format similar to what is output
+when the -p option is not used.  E.g.
 
 ------------------------------------------------
 in-place edit  :100644 100644 bcd1234... 0123456... M file0
@@ -52,9 +66,8 @@ unmerged       :000000 000000 0000000... 0000000... U file6
 The diffcore mechanism is fed a list of such comparison results
 (each of which is called "filepair", although at this point each
 of them talks about a single file), and transforms such a list
-into another list.  There are currently 6 such transformations:
+into another list.  There are currently 5 such transformations:
 
-- diffcore-pathspec
 - diffcore-break
 - diffcore-rename
 - diffcore-merge-broken
@@ -62,38 +75,14 @@ into another list.  There are currently 6 such transformations:
 - diffcore-order
 
 These are applied in sequence.  The set of filepairs 'git-diff-{asterisk}'
-commands find are used as the input to diffcore-pathspec, and
-the output from diffcore-pathspec is used as the input to the
+commands find are used as the input to diffcore-break, and
+the output from diffcore-break is used as the input to the
 next transformation.  The final result is then passed to the
 output routine and generates either diff-raw format (see Output
 format sections of the manual for 'git-diff-{asterisk}' commands) or
 diff-patch format.
 
 
-diffcore-pathspec: For Ignoring Files Outside Our Consideration
----------------------------------------------------------------
-
-The first transformation in the chain is diffcore-pathspec, and
-is controlled by giving the pathname parameters to the
-'git-diff-{asterisk}' commands on the command line.  The pathspec is used
-to limit the world diff operates in.  It removes the filepairs
-outside the specified set of pathnames.  E.g. If the input set
-of filepairs included:
-
-------------------------------------------------
-:100644 100644 bcd1234... 0123456... M junkfile
-------------------------------------------------
-
-but the command invocation was `git diff-files myfile`, then the
-junkfile entry would be removed from the list because only "myfile"
-is under consideration.
-
-Implementation note.  For performance reasons, 'git-diff-tree'
-uses the pathname parameters on the command line to cull set of
-filepairs it feeds the diffcore mechanism itself, and does not
-use diffcore-pathspec, but the end result is the same.
-
-
 diffcore-break: For Splitting Up "Complete Rewrites"
 ----------------------------------------------------
 
index 00277e0..c735788 100644 (file)
@@ -1,5 +1,5 @@
 merge.stat::
-       Whether to print the diffstat between ORIG_HEAD and merge result
+       Whether to print the diffstat between ORIG_HEAD and the merge result
        at the end of the merge.  True by default.
 
 merge.log::
index 388d492..f18d33e 100644 (file)
@@ -116,6 +116,7 @@ The placeholders are:
 - '%cr': committer date, relative
 - '%ct': committer date, UNIX timestamp
 - '%ci': committer date, ISO 8601 format
+- '%d': ref names, like the --decorate option of linkgit:git-log[1]
 - '%e': encoding
 - '%s': subject
 - '%b': body
index 156dc13..6c7465c 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.0.GIT
+DEF_VER=v1.6.0.2.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index 2bae53f..a4fd862 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -6,7 +6,7 @@ will install the git programs in your own ~/bin/ directory.  If you want
 to do a global install, you can do
 
        $ make prefix=/usr all doc info ;# as yourself
-       # make prefix=/usr install install-doc install-info ;# as root
+       # make prefix=/usr install install-doc install-html install-info ;# as root
 
 (or prefix=/usr/local, of course).  Just like any program suite
 that uses $prefix, the built results have some paths encoded,
@@ -19,7 +19,7 @@ set up install paths (via config.mak.autogen), so you can write instead
        $ make configure ;# as yourself
        $ ./configure --prefix=/usr ;# as yourself
        $ make all doc ;# as yourself
-       # make install install-doc ;# as root
+       # make install install-doc install-html;# as root
 
 
 Issues of note:
@@ -89,13 +89,22 @@ Issues of note:
    inclined to install the tools, the default build target
    ("make all") does _not_ build them.
 
+   "make doc" builds documentation in man and html formats; there are
+   also "make man", "make html" and "make info". Note that "make html"
+   requires asciidoc, but not xmlto. "make man" (and thus make doc)
+   requires both.
+
+   "make install-doc" installs documentation in man format only; there
+   are also "make install-man", "make install-html" and "make
+   install-info".
+
    Building and installing the info file additionally requires
    makeinfo and docbook2X.  Version 0.8.3 is known to work.
 
    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
+   Alternatively, pre-formatted documentation is available in
    "html" and "man" branches of the git repository itself.  For
    example, you could:
 
@@ -117,6 +126,12 @@ Issues of note:
 
        http://www.kernel.org/pub/software/scm/git/docs/
 
+   There are also "make quick-install-doc" and "make quick-install-html"
+   which install preformatted man pages and html documentation.
+   This does not require asciidoc/xmlto, but it only works from within
+   a cloned checkout of git.git with these two extra branches, and will
+   not work for the maintainer for obvious chicken-and-egg reasons.
+
    It has been reported that docbook-xsl version 1.72 and 1.73 are
    buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
    the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
index dfed7ba..3c0664a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -296,6 +296,7 @@ PROGRAMS += git-pack-redundant$X
 PROGRAMS += git-patch-id$X
 PROGRAMS += git-receive-pack$X
 PROGRAMS += git-send-pack$X
+PROGRAMS += git-shell$X
 PROGRAMS += git-show-index$X
 PROGRAMS += git-unpack-file$X
 PROGRAMS += git-update-server-info$X
@@ -358,6 +359,7 @@ LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
 LIB_H += help.h
+LIB_H += levenshtein.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
@@ -434,6 +436,7 @@ LIB_OBJS += hash.o
 LIB_OBJS += help.o
 LIB_OBJS += ident.o
 LIB_OBJS += interpolate.o
+LIB_OBJS += levenshtein.o
 LIB_OBJS += list-objects.o
 LIB_OBJS += ll-merge.o
 LIB_OBJS += lockfile.o
@@ -633,6 +636,8 @@ ifeq ($(uname_S),Darwin)
        endif
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@ -683,6 +688,8 @@ ifeq ($(uname_S),FreeBSD)
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -697,8 +704,7 @@ ifeq ($(uname_S),NetBSD)
                NEEDS_LIBICONV = YesPlease
        endif
        BASIC_CFLAGS += -I/usr/pkg/include
-       BASIC_LDFLAGS += -L/usr/pkg/lib
-       ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
+       BASIC_LDFLAGS += -L/usr/pkg/lib $(CC_LD_DYNPATH)/usr/pkg/lib
        THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),AIX)
@@ -710,6 +716,8 @@ ifeq ($(uname_S),AIX)
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
@@ -761,10 +769,10 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_PERL_MAKEMAKER = YesPlease
        NO_POSIX_ONLY_PROGRAMS = YesPlease
        NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
        COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-       COMPAT_OBJS += compat/mingw.o compat/fnmatch.o compat/regex.o compat/winansi.o
+       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
@@ -793,12 +801,14 @@ ifeq ($(uname_S),Darwin)
        endif
 endif
 
-ifdef NO_R_TO_GCC_LINKER
-       # Some gcc does not accept and pass -R to the linker to specify
-       # the runtime dynamic library path.
-       CC_LD_DYNPATH = -Wl,-rpath=
-else
-       CC_LD_DYNPATH = -R
+ifndef CC_LD_DYNPATH
+       ifdef NO_R_TO_GCC_LINKER
+               # Some gcc does not accept and pass -R to the linker to specify
+               # the runtime dynamic library path.
+               CC_LD_DYNPATH = -Wl,-rpath,
+       else
+               CC_LD_DYNPATH = -R
+       endif
 endif
 
 ifdef NO_CURL
@@ -834,7 +844,6 @@ EXTLIBS += -lz
 ifndef NO_POSIX_ONLY_PROGRAMS
        PROGRAMS += git-daemon$X
        PROGRAMS += git-imap-send$X
-       PROGRAMS += git-shell$X
 endif
 ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@ -1262,6 +1271,12 @@ $(XDIFF_LIB): $(XDIFF_OBJS)
 doc:
        $(MAKE) -C Documentation all
 
+man:
+       $(MAKE) -C Documentation man
+
+html:
+       $(MAKE) -C Documentation html
+
 info:
        $(MAKE) -C Documentation info
 
@@ -1365,7 +1380,7 @@ install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-       $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)'
+       $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_TCLTK
@@ -1398,6 +1413,9 @@ install-info:
 quick-install-doc:
        $(MAKE) -C Documentation quick-install
 
+quick-install-html:
+       $(MAKE) -C Documentation quick-install-html
+
 
 
 ### Maintainer's dist rules
index 20bef1f..2ab4aba 100644 (file)
@@ -2994,29 +2994,45 @@ static int write_out_results(struct patch *list, int skipped_patch)
 
 static struct lock_file lock_file;
 
-static struct excludes {
-       struct excludes *next;
-       const char *path;
-} *excludes;
+static struct string_list limit_by_name;
+static int has_include;
+static void add_name_limit(const char *name, int exclude)
+{
+       struct string_list_item *it;
+
+       it = string_list_append(name, &limit_by_name);
+       it->util = exclude ? NULL : (void *) 1;
+}
 
 static int use_patch(struct patch *p)
 {
        const char *pathname = p->new_name ? p->new_name : p->old_name;
-       struct excludes *x = excludes;
-       while (x) {
-               if (fnmatch(x->path, pathname, 0) == 0)
-                       return 0;
-               x = x->next;
-       }
+       int i;
+
+       /* Paths outside are not touched regardless of "--include" */
        if (0 < prefix_length) {
                int pathlen = strlen(pathname);
                if (pathlen <= prefix_length ||
                    memcmp(prefix, pathname, prefix_length))
                        return 0;
        }
-       return 1;
+
+       /* See if it matches any of exclude/include rule */
+       for (i = 0; i < limit_by_name.nr; i++) {
+               struct string_list_item *it = &limit_by_name.items[i];
+               if (!fnmatch(it->string, pathname, 0))
+                       return (it->util != NULL);
+       }
+
+       /*
+        * If we had any include, a path that does not match any rule is
+        * not used.  Otherwise, we saw bunch of exclude rules (or none)
+        * and such a path is used.
+        */
+       return !has_include;
 }
 
+
 static void prefix_one(char **name)
 {
        char *old_name = *name;
@@ -3157,10 +3173,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                        continue;
                }
                if (!prefixcmp(arg, "--exclude=")) {
-                       struct excludes *x = xmalloc(sizeof(*x));
-                       x->path = arg + 10;
-                       x->next = excludes;
-                       excludes = x;
+                       add_name_limit(arg + 10, 1);
+                       continue;
+               }
+               if (!prefixcmp(arg, "--include=")) {
+                       add_name_limit(arg + 10, 0);
+                       has_include = 1;
                        continue;
                }
                if (!prefixcmp(arg, "-p")) {
index 5ceec43..432ce2a 100644 (file)
@@ -111,6 +111,8 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
 {
        const char *remote = NULL;
 
+       git_config(git_default_config, NULL);
+
        remote = extract_remote_arg(&argc, argv);
        if (remote)
                return run_remote_archiver(remote, argc, argv);
index 90f8523..55b7aaf 100644 (file)
@@ -5,26 +5,26 @@
  *
  * Careful: order of argument flags does matter. For example,
  *
- *     git-checkout-index -a -f file.c
+ *     git checkout-index -a -f file.c
  *
  * Will first check out all files listed in the cache (but not
  * overwrite any old ones), and then force-checkout "file.c" a
  * second time (ie that one _will_ overwrite any old contents
  * with the same filename).
  *
- * Also, just doing "git-checkout-index" does nothing. You probably
- * meant "git-checkout-index -a". And if you want to force it, you
- * want "git-checkout-index -f -a".
+ * Also, just doing "git checkout-index" does nothing. You probably
+ * meant "git checkout-index -a". And if you want to force it, you
+ * want "git checkout-index -f -a".
  *
  * Intuitiveness is not the goal here. Repeatability is. The
  * reason for the "no arguments means no work" thing is that
  * from scripts you are supposed to be able to do things like
  *
- *     find . -name '*.h' -print0 | xargs -0 git-checkout-index -f --
+ *     find . -name '*.h' -print0 | xargs -0 git checkout-index -f --
  *
  * or:
  *
- *     find . -name '*.h' -print0 | git-checkout-index -f -z --stdin
+ *     find . -name '*.h' -print0 | git checkout-index -f -z --stdin
  *
  * which will force all existing *.h files to be replaced with
  * their cached copies. If an empty command line implied "all",
@@ -107,7 +107,7 @@ static int checkout_file(const char *name, int prefix_length)
        }
 
        if (!state.quiet) {
-               fprintf(stderr, "git-checkout-index: %s ", name);
+               fprintf(stderr, "git checkout-index: %s ", name);
                if (!has_same_name)
                        fprintf(stderr, "is not in the cache");
                else if (checkout_stage)
index efdb1e0..9a0c173 100644 (file)
@@ -76,6 +76,15 @@ static int read_tree_some(struct tree *tree, const char **pathspec)
        return 0;
 }
 
+static int skip_same_name(struct cache_entry *ce, int pos)
+{
+       while (++pos < active_nr &&
+              !strcmp(active_cache[pos]->name, ce->name))
+               ; /* skip */
+       return pos;
+}
+
+
 static int checkout_paths(struct tree *source_tree, const char **pathspec)
 {
        int pos;
@@ -107,6 +116,20 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
        if (report_path_error(ps_matched, pathspec, 0))
                return 1;
 
+       /* 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 (!ce_stage(ce))
+                               continue;
+                       errs = 1;
+                       error("path '%s' is unmerged", ce->name);
+                       pos = skip_same_name(ce, pos) - 1;
+               }
+       }
+       if (errs)
+               return 1;
+
        /* Now we are committed to check them out */
        memset(&state, 0, sizeof(state));
        state.force = 1;
@@ -114,7 +137,11 @@ static int checkout_paths(struct tree *source_tree, const char **pathspec)
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
                if (pathspec_match(pathspec, NULL, ce->name, 0)) {
-                       errs |= checkout_entry(ce, &state, NULL);
+                       if (!ce_stage(ce)) {
+                               errs |= checkout_entry(ce, &state, NULL);
+                               continue;
+                       }
+                       pos = skip_same_name(ce, pos) - 1;
                }
        }
 
@@ -242,6 +269,8 @@ static int merge_working_tree(struct checkout_opts *opts,
                }
 
                /* 2-way merge to the new branch */
+               topts.initial_checkout = (!active_nr &&
+                                         (old->commit == new->commit));
                topts.update = 1;
                topts.merge = 1;
                topts.gently = opts->merge;
@@ -299,7 +328,7 @@ static int merge_working_tree(struct checkout_opts *opts,
            commit_locked_index(lock_file))
                die("unable to write new index file");
 
-       if (!opts->force)
+       if (!opts->force && !opts->quiet)
                show_local_changes(&new->commit->object);
 
        return 0;
@@ -551,6 +580,18 @@ no_reference:
                return checkout_paths(source_tree, pathspec);
        }
 
+       if (opts.new_branch) {
+               struct strbuf buf;
+               strbuf_init(&buf, 0);
+               strbuf_addstr(&buf, "refs/heads/");
+               strbuf_addstr(&buf, opts.new_branch);
+               if (!get_sha1(buf.buf, rev))
+                       die("git checkout: branch %s already exists", opts.new_branch);
+               if (check_ref_format(buf.buf))
+                       die("git checkout: we do not like '%s' as a branch name.", opts.new_branch);
+               strbuf_release(&buf);
+       }
+
        if (new.name && !new.commit) {
                die("Cannot switch branch to a non-commit.");
        }
index c843529..5b40e07 100644 (file)
@@ -58,7 +58,7 @@ static struct option builtin_clone_options[] = {
        OPT_STRING(0, "reference", &option_reference, "repo",
                   "reference repository"),
        OPT_STRING('o', "origin", &option_origin, "branch",
-                  "use <branch> instead or 'origin' to track upstream"),
+                  "use <branch> instead of 'origin' to track upstream"),
        OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
                   "path to git-upload-pack on the remote"),
        OPT_STRING(0, "depth", &option_depth, "depth",
index 8a5ba4c..0453425 100644 (file)
@@ -24,7 +24,7 @@ static void check_valid(unsigned char *sha1, enum object_type expect)
                    typename(expect));
 }
 
-static const char commit_tree_usage[] = "git-commit-tree <sha1> [-p <sha1>]* < changelog";
+static const char commit_tree_usage[] = "git commit-tree <sha1> [-p <sha1>]* < changelog";
 
 static void new_parent(struct commit *parent, struct commit_list **parents_p)
 {
@@ -46,7 +46,8 @@ static const char commit_utf8_warn[] =
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
 int commit_tree(const char *msg, unsigned char *tree,
-               struct commit_list *parents, unsigned char *ret)
+               struct commit_list *parents, unsigned char *ret,
+               const char *author)
 {
        int result;
        int encoding_is_utf8;
@@ -74,7 +75,9 @@ int commit_tree(const char *msg, unsigned char *tree,
        }
 
        /* Person/date information */
-       strbuf_addf(&buffer, "author %s\n", git_author_info(IDENT_ERROR_ON_NO_NAME));
+       if (!author)
+               author = git_author_info(IDENT_ERROR_ON_NO_NAME);
+       strbuf_addf(&buffer, "author %s\n", author);
        strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
        if (!encoding_is_utf8)
                strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);
@@ -123,7 +126,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        if (strbuf_read(&buffer, 0, 0) < 0)
                die("git commit-tree: read returned %s", strerror(errno));
 
-       if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1)) {
+       if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
                printf("%s\n", sha1_to_hex(commit_sha1));
                return 0;
        }
index 8165bb3..55e1087 100644 (file)
@@ -667,14 +667,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
 }
 
 /*
- * Find out if the message starting at position 'start' in the strbuf
- * contains only whitespace and Signed-off-by lines.
+ * Find out if the message in the strbuf contains only whitespace and
+ * Signed-off-by lines.
  */
-static int message_is_empty(struct strbuf *sb, int start)
+static int message_is_empty(struct strbuf *sb)
 {
        struct strbuf tmpl;
        const char *nl;
-       int eol, i;
+       int eol, i, start = 0;
 
        if (cleanup_mode == CLEANUP_NONE && sb->len)
                return 0;
@@ -929,34 +929,14 @@ static const char commit_utf8_warn[] =
 "You may want to amend it after fixing the message, or set the config\n"
 "variable i18n.commitencoding to the encoding your project uses.\n";
 
-static void add_parent(struct strbuf *sb, const unsigned char *sha1)
-{
-       struct object *obj = parse_object(sha1);
-       const char *parent = sha1_to_hex(sha1);
-       const char *cp;
-
-       if (!obj)
-               die("Unable to find commit parent %s", parent);
-       if (obj->type != OBJ_COMMIT)
-               die("Parent %s isn't a proper commit", parent);
-
-       for (cp = sb->buf; cp && (cp = strstr(cp, "\nparent ")); cp += 8) {
-               if (!memcmp(cp + 8, parent, 40) && cp[48] == '\n') {
-                       error("duplicate parent %s ignored", parent);
-                       return;
-               }
-       }
-       strbuf_addf(sb, "parent %s\n", parent);
-}
-
 int cmd_commit(int argc, const char **argv, const char *prefix)
 {
-       int header_len;
        struct strbuf sb;
        const char *index_file, *reflog_msg;
        char *nl, *p;
        unsigned char commit_sha1[20];
        struct ref_lock *ref_lock;
+       struct commit_list *parents = NULL, **pptr = &parents;
 
        git_config(git_commit_config, NULL);
 
@@ -971,13 +951,6 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                return 1;
        }
 
-       /*
-        * The commit object
-        */
-       strbuf_init(&sb, 0);
-       strbuf_addf(&sb, "tree %s\n",
-                   sha1_to_hex(active_cache_tree->sha1));
-
        /* Determine parents */
        if (initial_commit) {
                reflog_msg = "commit (initial)";
@@ -991,13 +964,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                        die("could not parse HEAD commit");
 
                for (c = commit->parents; c; c = c->next)
-                       add_parent(&sb, c->item->object.sha1);
+                       pptr = &commit_list_insert(c->item, pptr)->next;
        } else if (in_merge) {
                struct strbuf m;
                FILE *fp;
 
                reflog_msg = "commit (merge)";
-               add_parent(&sb, head_sha1);
+               pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
                strbuf_init(&m, 0);
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
@@ -1007,24 +980,18 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                        unsigned char sha1[20];
                        if (get_sha1_hex(m.buf, sha1) < 0)
                                die("Corrupt MERGE_HEAD file (%s)", m.buf);
-                       add_parent(&sb, sha1);
+                       pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next;
                }
                fclose(fp);
                strbuf_release(&m);
        } else {
                reflog_msg = "commit";
-               strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
+               pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
-
-       strbuf_addf(&sb, "author %s\n",
-                   fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME));
-       strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME));
-       if (!is_encoding_utf8(git_commit_encoding))
-               strbuf_addf(&sb, "encoding %s\n", git_commit_encoding);
-       strbuf_addch(&sb, '\n');
+       parents = reduce_heads(parents);
 
        /* Finally, get the commit message */
-       header_len = sb.len;
+       strbuf_init(&sb, 0);
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
                rollback_index_files();
                die("could not read commit message");
@@ -1037,16 +1004,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
-       if (sb.len < header_len || message_is_empty(&sb, header_len)) {
+       if (message_is_empty(&sb)) {
                rollback_index_files();
                fprintf(stderr, "Aborting commit due to empty commit message.\n");
                exit(1);
        }
-       strbuf_addch(&sb, '\0');
-       if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf))
-               fprintf(stderr, commit_utf8_warn);
 
-       if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) {
+       if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1,
+                       fmt_ident(author_name, author_email, author_date,
+                               IDENT_ERROR_ON_NO_NAME))) {
                rollback_index_files();
                die("failed to write commit object");
        }
@@ -1055,12 +1021,11 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                                           initial_commit ? NULL : head_sha1,
                                           0);
 
-       nl = strchr(sb.buf + header_len, '\n');
+       nl = strchr(sb.buf, '\n');
        if (nl)
                strbuf_setlen(&sb, nl + 1 - sb.buf);
        else
                strbuf_addch(&sb, '\n');
-       strbuf_remove(&sb, 0, header_len);
        strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg));
        strbuf_insert(&sb, strlen(reflog_msg), ": ", 2);
 
index 6e80fe7..ab35b65 100644 (file)
@@ -126,6 +126,6 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
        }
        else
                printf("%lu objects, %lu kilobytes\n",
-                      loose, loose_size / 2);
+                      loose, loose_size / 1024);
        return 0;
 }
index 9bf10bb..2b578c7 100644 (file)
@@ -50,7 +50,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
            3 < rev.max_count)
                usage(diff_files_usage);
 
-       if (rev.max_count == -1 &&
+       /*
+        * "diff-files --base -p" should not combine merges because it
+        * was not asked to.  "diff-files -c -p" should not densify
+        * (the user should ask with "diff-files --cc" explicitly).
+        */
+       if (rev.max_count == -1 && !rev.combine_merges &&
            (rev.diffopt.output_format & DIFF_FORMAT_PATCH))
                rev.combine_merges = rev.dense_combined_merges = 1;
 
index 1138c2d..8ecefd4 100644 (file)
@@ -71,8 +71,9 @@ static int diff_tree_stdin(char *line)
        line[len-1] = 0;
        if (get_sha1_hex(line, sha1))
                return -1;
-       obj = lookup_object(sha1);
-       obj = obj ? obj : parse_object(sha1);
+       obj = lookup_unknown_object(sha1);
+       if (!obj || !obj->parsed)
+               obj = parse_object(sha1);
        if (!obj)
                return -1;
        if (obj->type == OBJ_COMMIT)
index 037c303..35da366 100644 (file)
@@ -74,6 +74,8 @@ static int builtin_diff_b_f(struct rev_info *revs,
        if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
                die("'%s': not a regular file or symlink", path);
 
+       diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
+
        if (blob[0].mode == S_IFINVALID)
                blob[0].mode = canon_mode(st.st_mode);
 
@@ -223,7 +225,13 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
                argv++; argc--;
        }
 
-       if (revs->max_count == -1 &&
+       /*
+        * "diff --base" should not combine merges because it was not
+        * asked to.  "diff -c" should not densify (if the user wants
+        * dense one, --cc can be explicitly asked for, or just rely
+        * on the default).
+        */
+       if (revs->max_count == -1 && !revs->combine_merges &&
            (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
                revs->combine_merges = revs->dense_combined_merges = 1;
 
index 459c6f0..4dfef29 100644 (file)
@@ -750,7 +750,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        if (!ret && nr_heads) {
                /* If the heads to pull were given, we should have
                 * consumed all of them by matching the remote.
-                * Otherwise, 'git-fetch remote no-such-ref' would
+                * Otherwise, 'git fetch remote no-such-ref' would
                 * silently succeed without issuing an error.
                 */
                for (i = 0; i < nr_heads; i++)
index 7eec4a0..ee93d3a 100644 (file)
@@ -86,10 +86,10 @@ static void add_merge_config(struct ref **head,
                /*
                 * Not fetched to a tracking branch?  We need to fetch
                 * it anyway to allow this branch's "branch.$name.merge"
-                * to be honored by git-pull, but we do not have to
+                * to be honored by 'git pull', but we do not have to
                 * fail if branch.$name.merge is misconfigured to point
                 * at a nonexisting branch.  If we were indeed called by
-                * git-pull, it will notice the misconfiguration because
+                * 'git pull', it will notice the misconfiguration because
                 * there is no entry in the resulting FETCH_HEAD marked
                 * for merging.
                 */
@@ -396,7 +396,7 @@ static int store_updated_refs(const char *url, const char *remote_name,
  * The refs we are going to fetch are in to_fetch (nr_heads in
  * total).  If running
  *
- *  $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
+ *  $ git rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
  *
  * does not error out, that means everything reachable from the
  * refs we are going to fetch exists and is connected to some of
index 21e92bb..e59bd80 100644 (file)
@@ -321,8 +321,8 @@ static const char *find_wholine(const char *who, int wholen, const char *buf, un
 static const char *copy_line(const char *buf)
 {
        const char *eol = strchr(buf, '\n');
-       if (!eol)
-               return "";
+       if (!eol) // simulate strchrnul()
+               eol = buf + strlen(buf);
        return xmemdupz(buf, eol - buf);
 }
 
@@ -545,6 +545,107 @@ static void grab_values(struct atom_value *val, int deref, struct object *obj, v
        }
 }
 
+/*
+ * generate a format suitable for scanf from a ref_rev_parse_rules
+ * rule, that is replace the "%.*s" spec with a "%s" spec
+ */
+static void gen_scanf_fmt(char *scanf_fmt, const char *rule)
+{
+       char *spec;
+
+       spec = strstr(rule, "%.*s");
+       if (!spec || strstr(spec + 4, "%.*s"))
+               die("invalid rule in ref_rev_parse_rules: %s", rule);
+
+       /* copy all until spec */
+       strncpy(scanf_fmt, rule, spec - rule);
+       scanf_fmt[spec - rule] = '\0';
+       /* copy new spec */
+       strcat(scanf_fmt, "%s");
+       /* copy remaining rule */
+       strcat(scanf_fmt, spec + 4);
+
+       return;
+}
+
+/*
+ * Shorten the refname to an non-ambiguous form
+ */
+static char *get_short_ref(struct refinfo *ref)
+{
+       int i;
+       static char **scanf_fmts;
+       static int nr_rules;
+       char *short_name;
+
+       /* pre generate scanf formats from ref_rev_parse_rules[] */
+       if (!nr_rules) {
+               size_t total_len = 0;
+
+               /* the rule list is NULL terminated, count them first */
+               for (; ref_rev_parse_rules[nr_rules]; nr_rules++)
+                       /* no +1 because strlen("%s") < strlen("%.*s") */
+                       total_len += strlen(ref_rev_parse_rules[nr_rules]);
+
+               scanf_fmts = xmalloc(nr_rules * sizeof(char *) + total_len);
+
+               total_len = 0;
+               for (i = 0; i < nr_rules; i++) {
+                       scanf_fmts[i] = (char *)&scanf_fmts[nr_rules]
+                                       + total_len;
+                       gen_scanf_fmt(scanf_fmts[i], ref_rev_parse_rules[i]);
+                       total_len += strlen(ref_rev_parse_rules[i]);
+               }
+       }
+
+       /* bail out if there are no rules */
+       if (!nr_rules)
+               return ref->refname;
+
+       /* buffer for scanf result, at most ref->refname must fit */
+       short_name = xstrdup(ref->refname);
+
+       /* skip first rule, it will always match */
+       for (i = nr_rules - 1; i > 0 ; --i) {
+               int j;
+               int short_name_len;
+
+               if (1 != sscanf(ref->refname, scanf_fmts[i], short_name))
+                       continue;
+
+               short_name_len = strlen(short_name);
+
+               /*
+                * check if the short name resolves to a valid ref,
+                * but use only rules prior to the matched one
+                */
+               for (j = 0; j < i; j++) {
+                       const char *rule = ref_rev_parse_rules[j];
+                       unsigned char short_objectname[20];
+
+                       /*
+                        * the short name is ambiguous, if it resolves
+                        * (with this previous rule) to a valid ref
+                        * read_ref() returns 0 on success
+                        */
+                       if (!read_ref(mkpath(rule, short_name_len, short_name),
+                                     short_objectname))
+                               break;
+               }
+
+               /*
+                * short name is non-ambiguous if all previous rules
+                * haven't resolved to a valid ref
+                */
+               if (j == i)
+                       return short_name;
+       }
+
+       free(short_name);
+       return ref->refname;
+}
+
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -570,13 +671,33 @@ static void populate_value(struct refinfo *ref)
        for (i = 0; i < used_atom_cnt; i++) {
                const char *name = used_atom[i];
                struct atom_value *v = &ref->value[i];
-               if (!strcmp(name, "refname"))
-                       v->s = ref->refname;
-               else if (!strcmp(name, "*refname")) {
-                       int len = strlen(ref->refname);
-                       char *s = xmalloc(len + 4);
-                       sprintf(s, "%s^{}", ref->refname);
-                       v->s = s;
+               int deref = 0;
+               if (*name == '*') {
+                       deref = 1;
+                       name++;
+               }
+               if (!prefixcmp(name, "refname")) {
+                       const char *formatp = strchr(name, ':');
+                       const char *refname = ref->refname;
+
+                       /* look for "short" refname format */
+                       if (formatp) {
+                               formatp++;
+                               if (!strcmp(formatp, "short"))
+                                       refname = get_short_ref(ref);
+                               else
+                                       die("unknown refname format %s",
+                                           formatp);
+                       }
+
+                       if (!deref)
+                               v->s = refname;
+                       else {
+                               int len = strlen(refname);
+                               char *s = xmalloc(len + 4);
+                               sprintf(s, "%s^{}", refname);
+                               v->s = s;
+                       }
                }
        }
 
index 3a06248..f3e63d7 100644 (file)
@@ -42,7 +42,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
                arg++;
        }
        if (argc < arg + 2 - commits_on_stdin) {
-               usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
+               usage("git http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
                return 1;
        }
        if (commits_on_stdin) {
@@ -53,7 +53,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
        }
        url = argv[arg];
        if (url && url[strlen(url)-1] != '/') {
-               rewritten_url = malloc(strlen(url)+2);
+               rewritten_url = xmalloc(strlen(url)+2);
                strcpy(rewritten_url, url);
                strcat(rewritten_url, "/");
                url = rewritten_url;
@@ -75,7 +75,7 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
                fprintf(stderr,
 "Some loose object were found to be corrupt, but they might be just\n"
 "a false '404 Not Found' error message sent with incorrect HTTP\n"
-"status code.  Suggest running git-fsck.\n");
+"status code.  Suggest running 'git fsck'.\n");
        }
 
        walker_free(walker);
index baf0d09..8140c12 100644 (file)
@@ -37,7 +37,7 @@ static void copy_templates_1(char *path, int baselen,
 
        /* Note: if ".git/hooks" file exists in the repository being
         * re-initialized, /etc/core-git/templates/hooks/update would
-        * cause git-init to fail here.  I think this is sane but
+        * cause "git init" to fail here.  I think this is sane but
         * it means that the set of templates we ship by default, along
         * with the way the namespace under .git/ is organized, should
         * be really carefully chosen.
index 1d3c5cb..fc5e4da 100644 (file)
@@ -14,7 +14,6 @@
 #include "tag.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
-#include "refs.h"
 #include "run-command.h"
 #include "shortlog.h"
 
@@ -25,31 +24,6 @@ static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
 
-static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
-{
-       int plen = strlen(prefix);
-       int nlen = strlen(name);
-       struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
-       memcpy(res->name, prefix, plen);
-       memcpy(res->name + plen, name, nlen + 1);
-       res->next = add_decoration(&name_decoration, obj, res);
-}
-
-static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
-{
-       struct object *obj = parse_object(sha1);
-       if (!obj)
-               return 0;
-       add_name_decoration("", refname, obj);
-       while (obj->type == OBJ_TAG) {
-               obj = ((struct tag *)obj)->tagged;
-               if (!obj)
-                       break;
-               add_name_decoration("tag: ", refname, obj);
-       }
-       return 0;
-}
-
 static void cmd_log_init(int argc, const char **argv, const char *prefix,
                      struct rev_info *rev)
 {
@@ -80,8 +54,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (!strcmp(arg, "--decorate")) {
-                       if (!decorate)
-                               for_each_ref(add_ref_decoration, NULL);
+                       load_ref_decorations();
                        decorate = 1;
                } else
                        die("unrecognized argument: %s", arg);
@@ -844,7 +817,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        endpos = strchr(committer, '>');
                        if (!endpos)
-                               die("bogos committer info %s\n", committer);
+                               die("bogus committer info %s\n", committer);
                        add_signoff = xmemdupz(committer, endpos - committer + 1);
                }
                else if (!strcmp(argv[i], "--attach")) {
index 9ad9791..1942e72 100644 (file)
@@ -476,6 +476,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
+               if (argc < 0)
+                       die("Bad branch.%s.mergeoptions string", branch);
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
@@ -691,7 +693,7 @@ static int merge_trivial(void)
        parent->next = xmalloc(sizeof(struct commit_list *));
        parent->next->item = remoteheads->item;
        parent->next->next = NULL;
-       commit_tree(merge_msg.buf, result_tree, parent, result_commit);
+       commit_tree(merge_msg.buf, result_tree, parent, result_commit, NULL);
        finish(result_commit, "In-index merge");
        drop_save();
        return 0;
@@ -720,7 +722,7 @@ static int finish_automerge(struct commit_list *common,
        }
        free_commit_list(remoteheads);
        strbuf_addch(&merge_msg, '\n');
-       commit_tree(merge_msg.buf, result_tree, parents, result_commit);
+       commit_tree(merge_msg.buf, result_tree, parents, result_commit, NULL);
        strbuf_addf(&buf, "Merge made by %s.", wt_strategy);
        finish(result_commit, buf.buf);
        strbuf_release(&buf);
index ba2cf00..1158e42 100644 (file)
@@ -23,7 +23,7 @@
 #endif
 
 static const char pack_usage[] = "\
-git-pack-objects [{ -q | --progress | --all-progress }] \n\
+git pack-objects [{ -q | --progress | --all-progress }] \n\
        [--max-pack-size=N] [--local] [--incremental] \n\
        [--window=N] [--window-memory=N] [--depth=N] \n\
        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
@@ -465,7 +465,7 @@ static void write_pack_file(void)
                        char tmpname[PATH_MAX];
                        int fd;
                        snprintf(tmpname, sizeof(tmpname),
-                                "%s/tmp_pack_XXXXXX", get_object_directory());
+                                "%s/pack/tmp_pack_XXXXXX", get_object_directory());
                        fd = xmkstemp(tmpname);
                        pack_tmp_name = xstrdup(tmpname);
                        f = sha1fd(fd, pack_tmp_name);
@@ -1725,6 +1725,14 @@ static void prepare_pack(int window, int depth)
                        if (entry->type < 0)
                                die("unable to get type of object %s",
                                    sha1_to_hex(entry->idx.sha1));
+               } else {
+                       if (entry->type < 0) {
+                               /*
+                                * This object is not found, but we
+                                * don't have to include it anyway.
+                                */
+                               continue;
+                       }
                }
 
                delta_list[n++] = entry;
@@ -1872,7 +1880,7 @@ static void mark_in_pack_object(struct object *object, struct packed_git *p, str
 
 /*
  * Compare the objects in the offset order, in order to emulate the
- * "git-rev-list --objects" output that produced the pack originally.
+ * "git rev-list --objects" output that produced the pack originally.
  */
 static int ofscmp(const void *a_, const void *b_)
 {
index c767a0a..1663f8b 100644 (file)
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
 static int show_only;
 static unsigned long expire;
 
-static int prune_tmp_object(char *path, const char *filename)
+static int prune_tmp_object(const char *path, const char *filename)
 {
        const char *fullpath = mkpath("%s/%s", path, filename);
        if (expire) {
@@ -110,24 +110,22 @@ static void prune_object_dir(const char *path)
 /*
  * Write errors (particularly out of space) can result in
  * failed temporary packs (and more rarely indexes and other
- * files begining with "tmp_") accumulating in the
- * object directory.
+ * files begining with "tmp_") accumulating in the object
+ * and the pack directories.
  */
-static void remove_temporary_files(void)
+static void remove_temporary_files(const char *path)
 {
        DIR *dir;
        struct dirent *de;
-       char* dirname=get_object_directory();
 
-       dir = opendir(dirname);
+       dir = opendir(path);
        if (!dir) {
-               fprintf(stderr, "Unable to open object directory %s\n",
-                       dirname);
+               fprintf(stderr, "Unable to open directory %s\n", path);
                return;
        }
        while ((de = readdir(dir)) != NULL)
                if (!prefixcmp(de->d_name, "tmp_"))
-                       prune_tmp_object(dirname, de->d_name);
+                       prune_tmp_object(path, de->d_name);
        closedir(dir);
 }
 
@@ -141,6 +139,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
                         "expire objects older than <time>"),
                OPT_END()
        };
+       char *s;
 
        save_commit_buffer = 0;
        init_revisions(&revs, prefix);
@@ -163,6 +162,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
        prune_object_dir(get_object_directory());
 
        prune_packed_objects(show_only);
-       remove_temporary_files();
+       remove_temporary_files(get_object_directory());
+       s = xstrdup(mkpath("%s/pack", get_object_directory()));
+       remove_temporary_files(s);
+       free(s);
        return 0;
 }
index dddc304..0706c95 100644 (file)
@@ -64,7 +64,7 @@ static void prime_cache_tree(void)
 
 }
 
-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 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;
 
@@ -206,6 +206,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        break;
                case 2:
                        opts.fn = twoway_merge;
+                       opts.initial_checkout = !active_nr;
                        break;
                case 3:
                default:
index 01945a8..4cb763f 100644 (file)
@@ -407,14 +407,15 @@ static int rm(int argc, const char **argv)
        return i;
 }
 
-static void show_list(const char *title, struct string_list *list)
+static void show_list(const char *title, struct string_list *list,
+                     const char *extra_arg)
 {
        int i;
 
        if (!list->nr)
                return;
 
-       printf(title, list->nr > 1 ? "es" : "");
+       printf(title, list->nr > 1 ? "es" : "", extra_arg);
        printf("\n    ");
        for (i = 0; i < list->nr; i++)
                printf("%s%s", i ? " " : "", list->items[i].string);
@@ -477,7 +478,6 @@ static int show(int argc, const char **argv)
 
        memset(&states, 0, sizeof(states));
        for (; argc; argc--, argv++) {
-               struct strbuf buf;
                int i;
 
                get_remote_ref_states(*argv, &states, !no_query);
@@ -503,18 +503,16 @@ static int show(int argc, const char **argv)
                }
 
                if (!no_query) {
-                       strbuf_init(&buf, 0);
-                       strbuf_addf(&buf, "  New remote branch%%s (next fetch "
-                               "will store in remotes/%s)", states.remote->name);
-                       show_list(buf.buf, &states.new);
-                       strbuf_release(&buf);
+                       show_list("  New remote branch%s (next fetch "
+                               "will store in remotes/%s)",
+                               &states.new, states.remote->name);
                        show_list("  Stale tracking branch%s (use 'git remote "
-                               "prune')", &states.stale);
+                               "prune')", &states.stale, "");
                }
 
                if (no_query)
                        for_each_ref(append_ref_to_tracked_list, &states);
-               show_list("  Tracked remote branch%s", &states.tracked);
+               show_list("  Tracked remote branch%s", &states.tracked, "");
 
                if (states.remote->push_refspec_nr) {
                        printf("  Local branch%s pushed with 'git push'\n   ",
index c023003..facaff2 100644 (file)
@@ -178,7 +178,7 @@ static void finish_object(struct object_array_entry *p)
 static void show_object(struct object_array_entry *p)
 {
        /* An object with name "foo\n0000000..." can be used to
-        * confuse downstream git-pack-objects very badly.
+        * confuse downstream "git pack-objects" very badly.
         */
        const char *ep = strchr(p->name, '\n');
 
index 6bd8211..fdac34f 100644 (file)
@@ -104,7 +104,7 @@ static int check_local_mod(unsigned char *head, int index_only)
                                     "from both the file and the HEAD\n"
                                     "(use -f to force removal)", name);
                else if (!index_only) {
-                       /* It's not dangerous to git-rm --cached a
+                       /* It's not dangerous to "git rm --cached" a
                         * file if the index matches the file or the
                         * HEAD, since it means the deleted content is
                         * still available somewhere.
index 7588d22..2af9f29 100644 (file)
@@ -43,7 +43,7 @@ static int pack_objects(int fd, struct ref *refs)
        po.out = fd;
        po.git_cmd = 1;
        if (start_command(&po))
-               die("git-pack-objects failed (%s)", strerror(errno));
+               die("git pack-objects failed (%s)", strerror(errno));
 
        /*
         * We feed the pack-objects we just spawned with revision
index cb7007e..0713bca 100644 (file)
@@ -9,26 +9,26 @@
 
 static const char tar_tree_usage[] =
 "git tar-tree [--remote=<repo>] <tree-ish> [basedir]\n"
-"*** Note that this command is now deprecated; use git-archive instead.";
+"*** Note that this command is now deprecated; use \"git archive\" instead.";
 
 int cmd_tar_tree(int argc, const char **argv, const char *prefix)
 {
        /*
-        * git-tar-tree is now a wrapper around git-archive --format=tar
+        * "git tar-tree" is now a wrapper around "git archive --format=tar"
         *
         * $0 --remote=<repo> arg... ==>
-        *      git-archive --format=tar --remote=<repo> arg...
+        *      git archive --format=tar --remote=<repo> arg...
         * $0 tree-ish ==>
-        *      git-archive --format=tar tree-ish
+        *      git archive --format=tar tree-ish
         * $0 tree-ish basedir ==>
-        *      git-archive --format-tar --prefix=basedir tree-ish
+        *      git archive --format-tar --prefix=basedir tree-ish
         */
        int i;
        const char **nargv = xcalloc(sizeof(*nargv), argc + 2);
        char *basedir_arg;
        int nargc = 0;
 
-       nargv[nargc++] = "git-archive";
+       nargv[nargc++] = "archive";
        nargv[nargc++] = "--format=tar";
 
        if (2 <= argc && !prefixcmp(argv[1], "--remote=")) {
@@ -53,8 +53,8 @@ int cmd_tar_tree(int argc, const char **argv, const char *prefix)
        nargv[nargc] = NULL;
 
        fprintf(stderr,
-               "*** git-tar-tree is now deprecated.\n"
-               "*** Running git-archive instead.\n***");
+               "*** \"git tar-tree\" is now deprecated.\n"
+               "*** Running \"git archive\" instead.\n***");
        for (i = 0; i < nargc; i++) {
                fputc(' ', stderr);
                sq_quote_print(stderr, nargv[i]);
index a891866..40b20f2 100644 (file)
@@ -13,7 +13,7 @@
 #include "fsck.h"
 
 static int dry_run, quiet, recover, has_errors, strict;
-static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
+static const char unpack_usage[] = "git unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
 
 /* We always read in 4kB chunks. */
 static unsigned char buffer[4096];
index ce83224..417f972 100644 (file)
@@ -14,7 +14,7 @@
  * Default to not allowing changes to the list of files. The
  * tool doesn't actually care, but this makes it harder to add
  * files to the revision control by mistake by doing something
- * like "git-update-index *" and suddenly having all the object
+ * like "git update-index *" and suddenly having all the object
  * files be revision controlled.
  */
 static int allow_add;
@@ -313,18 +313,18 @@ static void read_index_info(int line_termination)
                /* This reads lines formatted in one of three formats:
                 *
                 * (1) mode         SP sha1          TAB path
-                * The first format is what "git-apply --index-info"
+                * The first format is what "git apply --index-info"
                 * reports, and used to reconstruct a partial tree
                 * that is used for phony merge base tree when falling
                 * back on 3-way merge.
                 *
                 * (2) mode SP type SP sha1          TAB path
-                * The second format is to stuff git-ls-tree output
+                * The second format is to stuff "git ls-tree" output
                 * into the index file.
                 *
                 * (3) mode         SP sha1 SP stage TAB path
                 * This format is to put higher order stages into the
-                * index file and matches git-ls-files --stage output.
+                * index file and matches "git ls-files --stage" output.
                 */
                errno = 0;
                ul = strtoul(buf.buf, &ptr, 8);
index f3502d3..8893b3c 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -11,13 +11,14 @@ extern const char git_usage_string[];
 extern const char git_more_info_string[];
 
 extern void list_common_cmds_help(void);
-extern void help_unknown_cmd(const char *cmd);
+extern const char *help_unknown_cmd(const char *cmd);
 extern void prune_packed_objects(int);
 extern int read_line_with_nul(char *buf, int size, FILE *file);
 extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
        struct strbuf *out);
 extern int commit_tree(const char *msg, unsigned char *tree,
-               struct commit_list *parents, unsigned char *ret);
+               struct commit_list *parents, unsigned char *ret,
+               const char *author);
 extern int check_pager_config(const char *cmd);
 
 extern int cmd_add(int argc, const char **argv, const char *prefix);
index 0cf2a83..de83c69 100644 (file)
@@ -496,6 +496,18 @@ static int hunk_comment_line(const char *bol)
        return (isalpha(ch) || ch == '_' || ch == '$');
 }
 
+static void show_line_to_eol(const char *line, int len, const char *reset)
+{
+       int saw_cr_at_eol = 0;
+       if (len < 0)
+               len = strlen(line);
+       saw_cr_at_eol = (len && line[len-1] == '\r');
+
+       printf("%.*s%s%s\n", len - saw_cr_at_eol, line,
+              reset,
+              saw_cr_at_eol ? "\r" : "");
+}
+
 static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                       int use_color)
 {
@@ -589,7 +601,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                                        else
                                                putchar(' ');
                                }
-                               printf("%s%s\n", ll->line, c_reset);
+                               show_line_to_eol(ll->line, -1, c_reset);
                                ll = ll->next;
                        }
                        if (cnt < lno)
@@ -613,7 +625,7 @@ static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
                                        putchar(' ');
                                p_mask <<= 1;
                        }
-                       printf("%.*s%s\n", sl->len, sl->bol, c_reset);
+                       show_line_to_eol(sl->bol, sl->len, c_reset);
                }
        }
 }
@@ -671,9 +683,13 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
        int i, show_hunks;
        int working_tree_file = is_null_sha1(elem->sha1);
        int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+       const char *a_prefix, *b_prefix;
        mmfile_t result_file;
 
        context = opt->context;
+       a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
+       b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+
        /* Read the result of merge first */
        if (!working_tree_file)
                result = grab_blob(elem->sha1, &result_size);
@@ -849,13 +865,13 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
                        dump_quoted_path("--- ", "", "/dev/null",
                                         c_meta, c_reset);
                else
-                       dump_quoted_path("--- ", opt->a_prefix, elem->path,
+                       dump_quoted_path("--- ", a_prefix, elem->path,
                                         c_meta, c_reset);
                if (deleted)
                        dump_quoted_path("+++ ", "", "/dev/null",
                                         c_meta, c_reset);
                else
-                       dump_quoted_path("+++ ", opt->b_prefix, elem->path,
+                       dump_quoted_path("+++ ", b_prefix, elem->path,
                                         c_meta, c_reset);
                dump_sline(sline, cnt, num_parent,
                           DIFF_OPT_TST(opt, COLOR_DIFF));
similarity index 100%
rename from compat/fnmatch.c
rename to compat/fnmatch/fnmatch.c
similarity index 100%
rename from compat/fnmatch.h
rename to compat/fnmatch/fnmatch.h
similarity index 100%
rename from compat/regex.c
rename to compat/regex/regex.c
similarity index 100%
rename from compat/regex.h
rename to compat/regex/regex.h
index f67d673..17e9861 100644 (file)
@@ -4,6 +4,7 @@
 CC = @CC@
 CFLAGS = @CFLAGS@
 LDFLAGS = @LDFLAGS@
+CC_LD_DYNPATH = @CC_LD_DYNPATH@
 AR = @AR@
 TAR = @TAR@
 #INSTALL = @INSTALL@           # needs install-sh or install.sh in sources
index 7c2856e..27bab00 100644 (file)
@@ -103,6 +103,38 @@ GIT_PARSE_WITH(tcltk))
 AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
+# which switch to pass runtime path to dynamic libraries to the linker
+AC_CACHE_CHECK([if linker supports -R], ld_dashr, [
+   SAVE_LDFLAGS="${LDFLAGS}"
+   LDFLAGS="${SAVE_LDFLAGS} -R /"
+   AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_dashr=yes], [ld_dashr=no])
+   LDFLAGS="${SAVE_LDFLAGS}"
+])
+if test "$ld_dashr" = "yes"; then
+   AC_SUBST(CC_LD_DYNPATH, [-R])
+else
+   AC_CACHE_CHECK([if linker supports -Wl,-rpath,], ld_wl_rpath, [
+      SAVE_LDFLAGS="${LDFLAGS}"
+      LDFLAGS="${SAVE_LDFLAGS} -Wl,-rpath,/"
+      AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_wl_rpath=yes], [ld_wl_rpath=no])
+      LDFLAGS="${SAVE_LD_FLAGS}"
+   ])
+   if test "$ld_wl_rpath" = "yes"; then
+      AC_SUBST(CC_LD_DYNPATH, [-Wl,-rpath,])
+   else
+      AC_CACHE_CHECK([if linker supports -rpath], ld_rpath, [
+         SAVE_LDFLAGS="${LDFLAGS}"
+         LDFLAGS="${SAVE_LDFLAGS} -rpath /"
+         AC_LINK_IFELSE(AC_LANG_PROGRAM([], []), [ld_rpath=yes], [ld_rpath=no])
+         LDFLAGS="${SAVE_LD_FLAGS}"
+      ])
+      if test "$ld_rpath" = "yes"; then
+         AC_SUBST(CC_LD_DYNPATH, [-rpath])
+      else
+         AC_MSG_WARN([linker does not support runtime path to dynamic libraries])
+      fi
+   fi
+fi
 #AC_PROG_INSTALL               # needs install-sh or install.sh in sources
 AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
index d3fb6ae..93f0881 100755 (executable)
@@ -154,11 +154,8 @@ __git_heads ()
 {
        local cmd i is_hash=y dir="$(__gitdir "$1")"
        if [ -d "$dir" ]; then
-               for i in $(git --git-dir="$dir" \
-                       for-each-ref --format='%(refname)' \
-                       refs/heads ); do
-                       echo "${i#refs/heads/}"
-               done
+               git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+                       refs/heads
                return
        fi
        for i in $(git ls-remote "$1" 2>/dev/null); do
@@ -175,11 +172,8 @@ __git_tags ()
 {
        local cmd i is_hash=y dir="$(__gitdir "$1")"
        if [ -d "$dir" ]; then
-               for i in $(git --git-dir="$dir" \
-                       for-each-ref --format='%(refname)' \
-                       refs/tags ); do
-                       echo "${i#refs/tags/}"
-               done
+               git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+                       refs/tags
                return
        fi
        for i in $(git ls-remote "$1" 2>/dev/null); do
@@ -197,16 +191,8 @@ __git_refs ()
        local cmd i is_hash=y dir="$(__gitdir "$1")"
        if [ -d "$dir" ]; then
                if [ -e "$dir/HEAD" ]; then echo HEAD; fi
-               for i in $(git --git-dir="$dir" \
-                       for-each-ref --format='%(refname)' \
-                       refs/tags refs/heads refs/remotes); do
-                       case "$i" in
-                               refs/tags/*)    echo "${i#refs/tags/}" ;;
-                               refs/heads/*)   echo "${i#refs/heads/}" ;;
-                               refs/remotes/*) echo "${i#refs/remotes/}" ;;
-                               *)              echo "$i" ;;
-                       esac
-               done
+               git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+                       refs/tags refs/heads refs/remotes
                return
        fi
        for i in $(git ls-remote "$dir" 2>/dev/null); do
@@ -750,7 +736,7 @@ _git_commit ()
        --*)
                __gitcomp "
                        --all --author= --signoff --verify --no-verify
-                       --edit --amend --include --only
+                       --edit --amend --include --only --interactive
                        "
                return
        esac
index dab7c8e..a577ad0 100644 (file)
@@ -50,7 +50,7 @@ if ((@ARGV < 0) || !GetOptions(
                              )) { die $usage; }
 die $usage unless ($read_mode xor $write_mode);
 
-my $topdir = `git-rev-parse --show-cdup` or die "\n"; chomp $topdir;
+my $topdir = `git rev-parse --show-cdup` or die "\n"; chomp $topdir;
 my $gitdir = $topdir . '.git';
 my $gitmeta = $topdir . '.gitmeta';
 
@@ -155,7 +155,7 @@ elsif ($read_mode) {
        open (OUT, ">$gitmeta.tmp") or die "Could not open $gitmeta.tmp for writing: $!\n";
     }
 
-    my @files = `git-ls-files`;
+    my @files = `git ls-files`;
     my %dirs;
 
     foreach my $path (@files) {
index 2838954..bb70c75 100644 (file)
 #include "progress.h"
 #include "csum-file.h"
 
-static void sha1flush(struct sha1file *f, unsigned int count)
+static void sha1flush(struct sha1file *f, void *buf, unsigned int count)
 {
-       void *buf = f->buffer;
-
        for (;;) {
                int ret = xwrite(f->fd, buf, count);
                if (ret > 0) {
@@ -39,7 +37,7 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
 
        if (offset) {
                SHA1_Update(&f->ctx, f->buffer, offset);
-               sha1flush(f, offset);
+               sha1flush(f, f->buffer, offset);
                f->offset = 0;
        }
        SHA1_Final(f->buffer, &f->ctx);
@@ -47,7 +45,7 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
                hashcpy(result, f->buffer);
        if (flags & (CSUM_CLOSE | CSUM_FSYNC)) {
                /* write checksum and close fd */
-               sha1flush(f, 20);
+               sha1flush(f, f->buffer, 20);
                if (flags & CSUM_FSYNC)
                        fsync_or_die(f->fd, f->name);
                if (close(f->fd))
@@ -62,21 +60,30 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
 
 int sha1write(struct sha1file *f, void *buf, unsigned int count)
 {
-       if (f->do_crc)
-               f->crc32 = crc32(f->crc32, buf, count);
        while (count) {
                unsigned offset = f->offset;
                unsigned left = sizeof(f->buffer) - offset;
                unsigned nr = count > left ? left : count;
+               void *data;
+
+               if (f->do_crc)
+                       f->crc32 = crc32(f->crc32, buf, nr);
+
+               if (nr == sizeof(f->buffer)) {
+                       /* process full buffer directly without copy */
+                       data = buf;
+               } else {
+                       memcpy(f->buffer + offset, buf, nr);
+                       data = f->buffer;
+               }
 
-               memcpy(f->buffer + offset, buf, nr);
                count -= nr;
                offset += nr;
                buf = (char *) buf + nr;
                left -= nr;
                if (!left) {
-                       SHA1_Update(&f->ctx, f->buffer, offset);
-                       sha1flush(f, offset);
+                       SHA1_Update(&f->ctx, data, offset);
+                       sha1flush(f, data, offset);
                        offset = 0;
                }
                f->offset = offset;
index c315932..0e026f6 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -1083,7 +1083,8 @@ int main(int argc, char **argv)
                openlog("git-daemon", LOG_PID, LOG_DAEMON);
                set_die_routine(daemon_die);
        } else
-               setlinebuf(stderr); /* avoid splitting a message in the middle */
+               /* avoid splitting a message in the middle */
+               setvbuf(stderr, NULL, _IOLBF, 0);
 
        if (inetd_mode && (group_name || user_name))
                die("--user and --group are incompatible with --inetd");
index e7eaff9..ae96c64 100644 (file)
@@ -63,6 +63,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                              ? CE_MATCH_RACY_IS_DIRTY : 0);
        char symcache[PATH_MAX];
 
+       diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
+
        if (diff_unmerged_stage < 0)
                diff_unmerged_stage = 2;
        entries = active_nr;
@@ -469,6 +471,7 @@ int run_diff_index(struct rev_info *revs, int cached)
        if (unpack_trees(1, &t, &opts))
                exit(128);
 
+       diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
index 7d68b7f..b60d345 100644 (file)
@@ -252,6 +252,7 @@ void diff_no_index(struct rev_info *revs,
        if (queue_diff(&revs->diffopt, revs->diffopt.paths[0],
                       revs->diffopt.paths[1]))
                exit(1);
+       diff_set_mnemonic_prefix(&revs->diffopt, "1/", "2/");
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
 
diff --git a/diff.c b/diff.c
index cbd151b..a2f4850 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -24,6 +24,7 @@ static int diff_suppress_blank_empty;
 int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
+static int diff_mnemonic_prefix;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        "\033[m",       /* reset */
@@ -150,6 +151,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_auto_refresh_index = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "diff.mnemonicprefix")) {
+               diff_mnemonic_prefix = git_config_bool(var, value);
+               return 0;
+       }
        if (!strcmp(var, "diff.external"))
                return git_config_string(&external_diff_cmd_cfg, var, value);
        if (!prefixcmp(var, "diff.")) {
@@ -312,6 +317,15 @@ static void emit_rewrite_diff(const char *name_a,
        const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
        const char *reset = diff_get_color(color_diff, DIFF_RESET);
        static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
+       const char *a_prefix, *b_prefix;
+
+       if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
+               a_prefix = o->b_prefix;
+               b_prefix = o->a_prefix;
+       } else {
+               a_prefix = o->a_prefix;
+               b_prefix = o->b_prefix;
+       }
 
        name_a += (*name_a == '/');
        name_b += (*name_b == '/');
@@ -320,8 +334,8 @@ static void emit_rewrite_diff(const char *name_a,
 
        strbuf_reset(&a_name);
        strbuf_reset(&b_name);
-       quote_two_c_style(&a_name, o->a_prefix, name_a, 0);
-       quote_two_c_style(&b_name, o->b_prefix, name_b, 0);
+       quote_two_c_style(&a_name, a_prefix, name_a, 0);
+       quote_two_c_style(&b_name, b_prefix, name_b, 0);
 
        diff_populate_filespec(one, 0);
        diff_populate_filespec(two, 0);
@@ -513,13 +527,20 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
 
 static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
 {
-       int has_trailing_newline = (len > 0 && line[len-1] == '\n');
+       int has_trailing_newline, has_trailing_carriage_return;
+
+       has_trailing_newline = (len > 0 && line[len-1] == '\n');
        if (has_trailing_newline)
                len--;
+       has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+       if (has_trailing_carriage_return)
+               len--;
 
        fputs(set, file);
        fwrite(line, len, 1, file);
        fputs(reset, file);
+       if (has_trailing_carriage_return)
+               fputc('\r', file);
        if (has_trailing_newline)
                fputc('\n', file);
 }
@@ -1110,9 +1131,13 @@ static void show_dirstat(struct diff_options *options)
                /*
                 * Original minus copied is the removed material,
                 * added is the new material.  They are both damages
-                * made to the preimage.
+                * made to the preimage. In --dirstat-by-file mode, count
+                * damaged files, not damaged lines. This is done by
+                * counting only a single damaged line per file.
                 */
                damage = (p->one->size - copied) + added;
+               if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
+                       damage = 1;
 
                ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
                dir.files[dir.nr].name = name;
@@ -1402,6 +1427,7 @@ static struct builtin_funcname_pattern {
                        "\\|"
                        "^\\(.*=[ \t]*\\(class\\|record\\).*\\)$"
                        },
+       { "php", "^[\t ]*\\(\\(function\\|class\\).*\\)" },
        { "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" },
        { "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
        { "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
@@ -1439,6 +1465,14 @@ static const char *diff_funcname_pattern(struct diff_filespec *one)
        return NULL;
 }
 
+void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
+{
+       if (!options->a_prefix)
+               options->a_prefix = a;
+       if (!options->b_prefix)
+               options->b_prefix = b;
+}
+
 static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,
@@ -1452,9 +1486,19 @@ static void builtin_diff(const char *name_a,
        char *a_one, *b_two;
        const char *set = diff_get_color_opt(o, DIFF_METAINFO);
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
+       const char *a_prefix, *b_prefix;
 
-       a_one = quote_two(o->a_prefix, name_a + (*name_a == '/'));
-       b_two = quote_two(o->b_prefix, name_b + (*name_b == '/'));
+       diff_set_mnemonic_prefix(o, "a/", "b/");
+       if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
+               a_prefix = o->b_prefix;
+               b_prefix = o->a_prefix;
+       } else {
+               a_prefix = o->a_prefix;
+               b_prefix = o->b_prefix;
+       }
+
+       a_one = quote_two(a_prefix, name_a + (*name_a == '/'));
+       b_two = quote_two(b_prefix, name_b + (*name_b == '/'));
        lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
        lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
        fprintf(o->file, "%sdiff --git %s %s%s\n", set, a_one, b_two, reset);
@@ -2311,8 +2355,10 @@ void diff_setup(struct diff_options *options)
                DIFF_OPT_CLR(options, COLOR_DIFF);
        options->detect_rename = diff_detect_rename_default;
 
-       options->a_prefix = "a/";
-       options->b_prefix = "b/";
+       if (!diff_mnemonic_prefix) {
+               options->a_prefix = "a/";
+               options->b_prefix = "b/";
+       }
 }
 
 int diff_setup_done(struct diff_options *options)
@@ -2395,13 +2441,6 @@ int diff_setup_done(struct diff_options *options)
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
        }
 
-       /*
-        * If we postprocess in diffcore, we cannot simply return
-        * upon the first hit.  We need to run diff as usual.
-        */
-       if (options->pickaxe || options->filter)
-               DIFF_OPT_CLR(options, QUIET);
-
        return 0;
 }
 
@@ -2476,6 +2515,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--cumulative")) {
                options->output_format |= DIFF_FORMAT_DIRSTAT;
                DIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);
+       } else if (opt_arg(arg, 0, "dirstat-by-file",
+                          &options->dirstat_percent)) {
+               options->output_format |= DIFF_FORMAT_DIRSTAT;
+               DIFF_OPT_SET(options, DIRSTAT_BY_FILE);
        }
        else if (!strcmp(arg, "--check"))
                options->output_format |= DIFF_FORMAT_CHECKDIFF;
@@ -3389,10 +3432,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
 
 void diffcore_std(struct diff_options *options)
 {
-       if (DIFF_OPT_TST(options, QUIET))
-               return;
-
-       if (options->skip_stat_unmatch && !DIFF_OPT_TST(options, FIND_COPIES_HARDER))
+       if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
diff --git a/diff.h b/diff.h
index 7f53beb..a49d865 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -64,6 +64,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_OPT_RELATIVE_NAME       (1 << 17)
 #define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
 #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
+#define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
 #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
@@ -160,6 +161,8 @@ extern void diff_tree_combined(const unsigned char *sha1, const unsigned char pa
 
 extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);
 
+void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b);
+
 extern void diff_addremove(struct diff_options *,
                           int addremove,
                           unsigned mode,
index cc96c20..8ae3578 100644 (file)
@@ -92,7 +92,6 @@ extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
                                        struct diff_filespec *);
 extern void diff_q(struct diff_queue_struct *, struct diff_filepair *);
 
-extern void diffcore_pathspec(const char **pathspec);
 extern void diffcore_break(int);
 extern void diffcore_rename(struct diff_options *);
 extern void diffcore_merge_broken(void);
index ccdf2e5..ab6689a 100644 (file)
@@ -816,7 +816,7 @@ static void start_packfile(void)
        int pack_fd;
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/tmp_pack_XXXXXX", get_object_directory());
+               "%s/pack/tmp_pack_XXXXXX", get_object_directory());
        pack_fd = xmkstemp(tmpfile);
        p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
        strcpy(p->pack_name, tmpfile);
@@ -878,7 +878,7 @@ static char *create_index(void)
        }
 
        snprintf(tmpfile, sizeof(tmpfile),
-               "%s/tmp_idx_XXXXXX", get_object_directory());
+               "%s/pack/tmp_idx_XXXXXX", get_object_directory());
        idx_fd = xmkstemp(tmpfile);
        f = sha1fd(idx_fd, tmpfile);
        sha1write(f, array, 256 * sizeof(int));
index 97ac600..79de701 100755 (executable)
@@ -172,6 +172,25 @@ bisect_write() {
        test -n "$nolog" || echo "git bisect $state $rev" >>"$GIT_DIR/BISECT_LOG"
 }
 
+is_expected_rev() {
+       test -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
+       test "$1" = $(cat "$GIT_DIR/BISECT_EXPECTED_REV")
+}
+
+mark_expected_rev() {
+       echo "$1" > "$GIT_DIR/BISECT_EXPECTED_REV"
+}
+
+check_expected_revs() {
+       for _rev in "$@"; do
+               if ! is_expected_rev "$_rev"; then
+                       rm -f "$GIT_DIR/BISECT_ANCESTORS_OK"
+                       rm -f "$GIT_DIR/BISECT_EXPECTED_REV"
+                       return
+               fi
+       done
+}
+
 bisect_state() {
        bisect_autostart
        state=$1
@@ -181,7 +200,8 @@ bisect_state() {
        1,bad|1,good|1,skip)
                rev=$(git rev-parse --verify HEAD) ||
                        die "Bad rev input: HEAD"
-               bisect_write "$state" "$rev" ;;
+               bisect_write "$state" "$rev"
+               check_expected_revs "$rev" ;;
        2,bad|*,good|*,skip)
                shift
                eval=''
@@ -191,7 +211,8 @@ bisect_state() {
                                die "Bad rev input: $rev"
                        eval="$eval bisect_write '$state' '$sha'; "
                done
-               eval "$eval" ;;
+               eval "$eval"
+               check_expected_revs "$@" ;;
        *,bad)
                die "'git bisect bad' can take only one argument." ;;
        *)
@@ -243,33 +264,18 @@ bisect_auto_next() {
        bisect_next_check && bisect_next || :
 }
 
-eval_rev_list() {
-       _eval="$1"
-
-       eval $_eval
-       res=$?
-
-       if [ $res -ne 0 ]; then
-               echo >&2 "'git rev-list --bisect-vars' failed:"
-               echo >&2 "maybe you mistake good and bad revs?"
-               exit $res
-       fi
-
-       return $res
-}
-
 filter_skipped() {
        _eval="$1"
        _skip="$2"
 
        if [ -z "$_skip" ]; then
-               eval_rev_list "$_eval"
+               eval "$_eval"
                return
        fi
 
        # Let's parse the output of:
        # "git rev-list --bisect-vars --bisect-all ..."
-       eval_rev_list "$_eval" | while read hash line
+       eval "$_eval" | while read hash line
        do
                case "$VARS,$FOUND,$TRIED,$hash" in
                        # We display some vars.
@@ -332,20 +338,133 @@ exit_if_skipped_commits () {
        fi
 }
 
+bisect_checkout() {
+       _rev="$1"
+       _msg="$2"
+       echo "Bisecting: $_msg"
+       mark_expected_rev "$_rev"
+       git checkout -q "$_rev" || exit
+       git show-branch "$_rev"
+}
+
+is_among() {
+       _rev="$1"
+       _list="$2"
+       case "$_list" in *$_rev*) return 0 ;; esac
+       return 1
+}
+
+handle_bad_merge_base() {
+       _badmb="$1"
+       _good="$2"
+       if is_expected_rev "$_badmb"; then
+               cat >&2 <<EOF
+The merge base $_badmb is bad.
+This means the bug has been fixed between $_badmb and [$_good].
+EOF
+               exit 3
+       else
+               cat >&2 <<EOF
+Some good revs are not ancestor of the bad rev.
+git bisect cannot work properly in this case.
+Maybe you mistake good and bad revs?
+EOF
+               exit 1
+       fi
+}
+
+handle_skipped_merge_base() {
+       _mb="$1"
+       _bad="$2"
+       _good="$3"
+       cat >&2 <<EOF
+Warning: the merge base between $_bad and [$_good] must be skipped.
+So we cannot be sure the first bad commit is between $_mb and $_bad.
+We continue anyway.
+EOF
+}
+
+#
+# "check_merge_bases" checks that merge bases are not "bad".
+#
+# - If one is "good", that's good, we have nothing to do.
+# - If one is "bad", it means the user assumed something wrong
+# and we must exit.
+# - If one is "skipped", we can't know but we should warn.
+# - If we don't know, we should check it out and ask the user to test.
+#
+# In the last case we will return 1, and otherwise 0.
+#
+check_merge_bases() {
+       _bad="$1"
+       _good="$2"
+       _skip="$3"
+       for _mb in $(git merge-base --all $_bad $_good)
+       do
+               if is_among "$_mb" "$_good"; then
+                       continue
+               elif test "$_mb" = "$_bad"; then
+                       handle_bad_merge_base "$_bad" "$_good"
+               elif is_among "$_mb" "$_skip"; then
+                       handle_skipped_merge_base "$_mb" "$_bad" "$_good"
+               else
+                       bisect_checkout "$_mb" "a merge base must be tested"
+                       return 1
+               fi
+       done
+       return 0
+}
+
+#
+# "check_good_are_ancestors_of_bad" checks that all "good" revs are
+# ancestor of the "bad" rev.
+#
+# If that's not the case, we need to check the merge bases.
+# If a merge base must be tested by the user we return 1 and
+# otherwise 0.
+#
+check_good_are_ancestors_of_bad() {
+       test -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
+               return
+
+       _bad="$1"
+       _good=$(echo $2 | sed -e 's/\^//g')
+       _skip="$3"
+
+       # Bisecting with no good rev is ok
+       test -z "$_good" && return
+
+       _side=$(git rev-list $_good ^$_bad)
+       if test -n "$_side"; then
+               # Return if a checkout was done
+               check_merge_bases "$_bad" "$_good" "$_skip" || return
+       fi
+
+       : > "$GIT_DIR/BISECT_ANCESTORS_OK"
+
+       return 0
+}
+
 bisect_next() {
        case "$#" in 0) ;; *) usage ;; esac
        bisect_autostart
        bisect_next_check good
 
+       # Get bad, good and skipped revs
+       bad=$(git rev-parse --verify refs/bisect/bad) &&
+       good=$(git for-each-ref --format='^%(objectname)' \
+               "refs/bisect/good-*" | tr '\012' ' ') &&
        skip=$(git for-each-ref --format='%(objectname)' \
-               "refs/bisect/skip-*" | tr '\012' ' ') || exit
+               "refs/bisect/skip-*" | tr '\012' ' ') &&
+
+       # Maybe some merge bases must be tested first
+       check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
+       # Return now if a checkout has already been done
+       test "$?" -eq "1" && return
 
+       # Get bisection information
        BISECT_OPT=''
        test -n "$skip" && BISECT_OPT='--bisect-all'
-
-       bad=$(git rev-parse --verify refs/bisect/bad) &&
-       good=$(git for-each-ref --format='^%(objectname)' \
-               "refs/bisect/good-*" | tr '\012' ' ') &&
        eval="git rev-list --bisect-vars $BISECT_OPT $good $bad --" &&
        eval="$eval $(cat "$GIT_DIR/BISECT_NAMES")" &&
        eval=$(filter_skipped "$eval" "$skip") &&
@@ -366,9 +485,7 @@ bisect_next() {
        # commit is also a "skip" commit (see above).
        exit_if_skipped_commits "$bisect_rev"
 
-       echo "Bisecting: $bisect_nr revisions left to test after this"
-       git checkout -q "$bisect_rev" || exit
-       git show-branch "$bisect_rev"
+       bisect_checkout "$bisect_rev" "$bisect_nr revisions left to test after this"
 }
 
 bisect_visualize() {
@@ -415,6 +532,8 @@ bisect_clean_state() {
        do
                git update-ref -d $ref $hash || exit
        done
+       rm -f "$GIT_DIR/BISECT_EXPECTED_REV" &&
+       rm -f "$GIT_DIR/BISECT_ANCESTORS_OK" &&
        rm -f "$GIT_DIR/BISECT_LOG" &&
        rm -f "$GIT_DIR/BISECT_NAMES" &&
        rm -f "$GIT_DIR/BISECT_RUN" &&
diff --git a/git-gui/.gitattributes b/git-gui/.gitattributes
new file mode 100644 (file)
index 0000000..f96112d
--- /dev/null
@@ -0,0 +1,3 @@
+*           encoding=US-ASCII
+git-gui.sh  encoding=UTF-8
+/po/*.po    encoding=UTF-8
index 10d8a44..4085e8f 100755 (executable)
@@ -521,6 +521,19 @@ proc kill_file_process {fd} {
        }
 }
 
+proc gitattr {path attr default} {
+       if {[catch {set r [git check-attr $attr -- $path]}]} {
+               set r unspecified
+       } else {
+               set r [join [lrange [split $r :] 2 end] :]
+               regsub {^ } $r {} r
+       }
+       if {$r eq {unspecified}} {
+               return $default
+       }
+       return $r
+}
+
 proc sq {value} {
        regsub -all ' $value "'\\''" value
        return "'$value'"
@@ -665,6 +678,7 @@ set default_config(merge.verbosity) 2
 set default_config(user.name) {}
 set default_config(user.email) {}
 
+set default_config(gui.encoding) [encoding system]
 set default_config(gui.matchtrackingbranch) false
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
@@ -948,10 +962,32 @@ blame {
 }
 citool {
        enable_option singlecommit
+       enable_option retcode
 
        disable_option multicommit
        disable_option branch
        disable_option transport
+
+       while {[llength $argv] > 0} {
+               set a [lindex $argv 0]
+               switch -- $a {
+               --amend {
+                       enable_option initialamend
+               }
+               --nocommit {
+                       enable_option nocommit
+                       enable_option nocommitmsg
+               }
+               --commitmsg {
+                       disable_option nocommitmsg
+               }
+               default {
+                       break
+               }
+               }
+
+               set argv [lrange $argv 1 end]
+       }
 }
 }
 
@@ -1023,8 +1059,12 @@ set current_branch {}
 set is_detached 0
 set current_diff_path {}
 set is_3way_diff 0
+set is_conflict_diff 0
 set selected_commit_type new
 
+set nullid "0000000000000000000000000000000000000000"
+set nullid2 "0000000000000000000000000000000000000001"
+
 ######################################################################
 ##
 ## task management
@@ -1105,6 +1145,20 @@ proc PARENT {} {
        return $empty_tree
 }
 
+proc force_amend {} {
+       global selected_commit_type
+       global HEAD PARENT MERGE_HEAD commit_type
+
+       repository_state newType newHEAD newMERGE_HEAD
+       set HEAD $newHEAD
+       set PARENT $newHEAD
+       set MERGE_HEAD $newMERGE_HEAD
+       set commit_type $newType
+
+       set selected_commit_type amend
+       do_select_commit_type
+}
+
 proc rescan {after {honor_trustmtime 1}} {
        global HEAD PARENT MERGE_HEAD commit_type
        global ui_index ui_workdir ui_comm
@@ -1131,6 +1185,7 @@ proc rescan {after {honor_trustmtime 1}} {
                || [string trim [$ui_comm get 0.0 end]] eq {})} {
                if {[string match amend* $commit_type]} {
                } elseif {[load_message GITGUI_MSG]} {
+               } elseif {[run_prepare_commit_msg_hook]} {
                } elseif {[load_message MERGE_MSG]} {
                } elseif {[load_message SQUASH_MSG]} {
                }
@@ -1230,6 +1285,70 @@ proc load_message {file} {
        return 0
 }
 
+proc run_prepare_commit_msg_hook {} {
+       global pch_error
+
+       # prepare-commit-msg requires PREPARE_COMMIT_MSG exist.  From git-gui
+       # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
+       # empty file but existant file.
+
+       set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
+
+       if {[file isfile [gitdir MERGE_MSG]]} {
+               set pcm_source "merge"
+               set fd_mm [open [gitdir MERGE_MSG] r]
+               puts -nonewline $fd_pcm [read $fd_mm]
+               close $fd_mm
+       } elseif {[file isfile [gitdir SQUASH_MSG]]} {
+               set pcm_source "squash"
+               set fd_sm [open [gitdir SQUASH_MSG] r]
+               puts -nonewline $fd_pcm [read $fd_sm]
+               close $fd_sm
+       } else {
+               set pcm_source ""
+       }
+
+       close $fd_pcm
+
+       set fd_ph [githook_read prepare-commit-msg \
+                       [gitdir PREPARE_COMMIT_MSG] $pcm_source]
+       if {$fd_ph eq {}} {
+               catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+               return 0;
+       }
+
+       ui_status [mc "Calling prepare-commit-msg hook..."]
+       set pch_error {}
+
+       fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
+       fileevent $fd_ph readable \
+               [list prepare_commit_msg_hook_wait $fd_ph]
+
+       return 1;
+}
+
+proc prepare_commit_msg_hook_wait {fd_ph} {
+       global pch_error
+
+       append pch_error [read $fd_ph]
+       fconfigure $fd_ph -blocking 1
+       if {[eof $fd_ph]} {
+               if {[catch {close $fd_ph}]} {
+                       ui_status [mc "Commit declined by prepare-commit-msg hook."]
+                       hook_failed_popup prepare-commit-msg $pch_error
+                       catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+                       exit 1
+               } else {
+                       load_message PREPARE_COMMIT_MSG
+               }
+               set pch_error {}
+               catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+               return
+        }
+       fconfigure $fd_ph -blocking 0
+       catch {file delete [gitdir PREPARE_COMMIT_MSG]}
+}
+
 proc read_diff_index {fd after} {
        global buf_rdi
 
@@ -1751,11 +1870,19 @@ proc do_gitk {revs} {
 }
 
 set is_quitting 0
+set ret_code    1
+
+proc terminate_me {win} {
+       global ret_code
+       if {$win ne {.}} return
+       exit $ret_code
+}
 
-proc do_quit {} {
+proc do_quit {{rc {1}}} {
        global ui_comm is_quitting repo_config commit_type
        global GITGUI_BCK_exists GITGUI_BCK_i
        global ui_comm_spell
+       global ret_code
 
        if {$is_quitting} return
        set is_quitting 1
@@ -1810,6 +1937,7 @@ proc do_quit {} {
                }
        }
 
+       set ret_code $rc
        destroy .
 }
 
@@ -1951,19 +2079,21 @@ proc toggle_or_diff {w x y} {
        $ui_index tag remove in_sel 0.0 end
        $ui_workdir tag remove in_sel 0.0 end
 
-       # Do not stage files with conflicts
+       # Determine the state of the file
        if {[info exists file_states($path)]} {
                set state [lindex $file_states($path) 0]
        } else {
                set state {__}
        }
 
-       if {[string first {U} $state] >= 0} {
-               set col 1
-       }
-
        # Restage the file, or simply show the diff
        if {$col == 0 && $y > 1} {
+               # Conflicts need special handling
+               if {[string first {U} $state] >= 0} {
+                       merge_stage_workdir $path $w $lno
+                       return
+               }
+
                if {[string index $state 1] eq {O}} {
                        set mmask {}
                } else {
@@ -2212,26 +2342,36 @@ if {[is_enabled branch]} {
 
 # -- Commit Menu
 #
+proc commit_btn_caption {} {
+       if {[is_enabled nocommit]} {
+               return [mc "Done"]
+       } else {
+               return [mc Commit@@verb]
+       }
+}
+
 if {[is_enabled multicommit] || [is_enabled singlecommit]} {
        menu .mbar.commit
 
-       .mbar.commit add radiobutton \
-               -label [mc "New Commit"] \
-               -command do_select_commit_type \
-               -variable selected_commit_type \
-               -value new
-       lappend disable_on_lock \
-               [list .mbar.commit entryconf [.mbar.commit index last] -state]
+       if {![is_enabled nocommit]} {
+               .mbar.commit add radiobutton \
+                       -label [mc "New Commit"] \
+                       -command do_select_commit_type \
+                       -variable selected_commit_type \
+                       -value new
+               lappend disable_on_lock \
+                       [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-       .mbar.commit add radiobutton \
-               -label [mc "Amend Last Commit"] \
-               -command do_select_commit_type \
-               -variable selected_commit_type \
-               -value amend
-       lappend disable_on_lock \
-               [list .mbar.commit entryconf [.mbar.commit index last] -state]
+               .mbar.commit add radiobutton \
+                       -label [mc "Amend Last Commit"] \
+                       -command do_select_commit_type \
+                       -variable selected_commit_type \
+                       -value amend
+               lappend disable_on_lock \
+                       [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
-       .mbar.commit add separator
+               .mbar.commit add separator
+       }
 
        .mbar.commit add command -label [mc Rescan] \
                -command ui_do_rescan \
@@ -2273,11 +2413,13 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 
        .mbar.commit add separator
 
-       .mbar.commit add command -label [mc "Sign Off"] \
-               -command do_signoff \
-               -accelerator $M1T-S
+       if {![is_enabled nocommit]} {
+               .mbar.commit add command -label [mc "Sign Off"] \
+                       -command do_signoff \
+                       -accelerator $M1T-S
+       }
 
-       .mbar.commit add command -label [mc Commit@@verb] \
+       .mbar.commit add command -label [commit_btn_caption] \
                -command do_commit \
                -accelerator $M1T-Return
        lappend disable_on_lock \
@@ -2601,19 +2743,23 @@ pack .vpane.lower.commarea.buttons.incall -side top -fill x
 lappend disable_on_lock \
        {.vpane.lower.commarea.buttons.incall conf -state}
 
-button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
-       -command do_signoff
-pack .vpane.lower.commarea.buttons.signoff -side top -fill x
+if {![is_enabled nocommit]} {
+       button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
+               -command do_signoff
+       pack .vpane.lower.commarea.buttons.signoff -side top -fill x
+}
 
-button .vpane.lower.commarea.buttons.commit -text [mc Commit@@verb] \
+button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
        -command do_commit
 pack .vpane.lower.commarea.buttons.commit -side top -fill x
 lappend disable_on_lock \
        {.vpane.lower.commarea.buttons.commit conf -state}
 
-button .vpane.lower.commarea.buttons.push -text [mc Push] \
-       -command do_push_anywhere
-pack .vpane.lower.commarea.buttons.push -side top -fill x
+if {![is_enabled nocommit]} {
+       button .vpane.lower.commarea.buttons.push -text [mc Push] \
+               -command do_push_anywhere
+       pack .vpane.lower.commarea.buttons.push -side top -fill x
+}
 
 # -- Commit Message Buffer
 #
@@ -2621,20 +2767,24 @@ frame .vpane.lower.commarea.buffer
 frame .vpane.lower.commarea.buffer.header
 set ui_comm .vpane.lower.commarea.buffer.t
 set ui_coml .vpane.lower.commarea.buffer.header.l
-radiobutton .vpane.lower.commarea.buffer.header.new \
-       -text [mc "New Commit"] \
-       -command do_select_commit_type \
-       -variable selected_commit_type \
-       -value new
-lappend disable_on_lock \
-       [list .vpane.lower.commarea.buffer.header.new conf -state]
-radiobutton .vpane.lower.commarea.buffer.header.amend \
-       -text [mc "Amend Last Commit"] \
-       -command do_select_commit_type \
-       -variable selected_commit_type \
-       -value amend
-lappend disable_on_lock \
-       [list .vpane.lower.commarea.buffer.header.amend conf -state]
+
+if {![is_enabled nocommit]} {
+       radiobutton .vpane.lower.commarea.buffer.header.new \
+               -text [mc "New Commit"] \
+               -command do_select_commit_type \
+               -variable selected_commit_type \
+               -value new
+       lappend disable_on_lock \
+               [list .vpane.lower.commarea.buffer.header.new conf -state]
+       radiobutton .vpane.lower.commarea.buffer.header.amend \
+               -text [mc "Amend Last Commit"] \
+               -command do_select_commit_type \
+               -variable selected_commit_type \
+               -value amend
+       lappend disable_on_lock \
+               [list .vpane.lower.commarea.buffer.header.amend conf -state]
+}
+
 label $ui_coml \
        -anchor w \
        -justify left
@@ -2652,8 +2802,11 @@ proc trace_commit_type {varname args} {
 }
 trace add variable commit_type write trace_commit_type
 pack $ui_coml -side left -fill x
-pack .vpane.lower.commarea.buffer.header.amend -side right
-pack .vpane.lower.commarea.buffer.header.new -side right
+
+if {![is_enabled nocommit]} {
+       pack .vpane.lower.commarea.buffer.header.amend -side right
+       pack .vpane.lower.commarea.buffer.header.new -side right
+}
 
 text $ui_comm -background white -foreground black \
        -borderwidth 1 \
@@ -2860,6 +3013,14 @@ proc create_common_diff_popup {ctxm} {
                -command {incr_font_size font_diff 1}
        lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
        $ctxm add separator
+       set emenu $ctxm.enc
+       menu $emenu
+       build_encoding_menu $emenu [list force_diff_encoding]
+       $ctxm add cascade \
+               -label [mc "Encoding"] \
+               -menu $emenu
+       lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
+       $ctxm add separator
        $ctxm add command -label [mc "Options..."] \
                -command do_options
 }
@@ -3191,7 +3352,20 @@ lock_index begin-read
 if {![winfo ismapped .]} {
        wm deiconify .
 }
-after 1 do_rescan
+after 1 {
+       if {[is_enabled initialamend]} {
+               force_amend
+       } else {
+               do_rescan
+       }
+
+       if {[is_enabled nocommitmsg]} {
+               $ui_comm configure -state disabled -background gray
+       }
+}
 if {[is_enabled multicommit]} {
        after 1000 hint_gc
 }
+if {[is_enabled retcode]} {
+       bind . <Destroy> {+terminate_me %W}
+}
index 827c85d..eb61374 100644 (file)
@@ -256,9 +256,16 @@ constructor new {i_commit i_path i_jump} {
        $w.ctxm add command \
                -label [mc "Copy Commit"] \
                -command [cb _copycommit]
+       $w.ctxm add separator
+       menu $w.ctxm.enc
+       build_encoding_menu $w.ctxm.enc [cb _setencoding]
+       $w.ctxm add cascade \
+               -label [mc "Encoding"] \
+               -menu $w.ctxm.enc
        $w.ctxm add command \
                -label [mc "Do Full Copy Detection"] \
                -command [cb _fullcopyblame]
+       $w.ctxm add separator
        $w.ctxm add command \
                -label [mc "Show History Context"] \
                -command [cb _gitkcommit]
@@ -399,7 +406,10 @@ method _load {jump} {
        } else {
                set fd [git_read cat-file blob "$commit:$path"]
        }
-       fconfigure $fd -blocking 0 -translation lf -encoding binary
+       fconfigure $fd \
+               -blocking 0 \
+               -translation lf \
+               -encoding [get_path_encoding $path]
        fileevent $fd readable [cb _read_file $fd $jump]
        set current_fd $fd
 }
@@ -508,7 +518,7 @@ method _exec_blame {cur_w cur_d options cur_s} {
        }
        lappend options -- $path
        set fd [eval git_read --nice blame $options]
-       fconfigure $fd -blocking 0 -translation lf -encoding binary
+       fconfigure $fd -blocking 0 -translation lf -encoding utf-8
        fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
        set current_fd $fd
        set blame_lines 0
@@ -788,6 +798,16 @@ method _click {cur_w pos} {
        _showcommit $this $cur_w $lno
 }
 
+method _setencoding {enc} {
+       force_path_encoding $path $enc
+       _load $this [list \
+               $highlight_column \
+               $highlight_line \
+               [lindex [$w_file xview] 0] \
+               [lindex [$w_file yview] 0] \
+               ]
+}
+
 method _load_commit {cur_w cur_d pos} {
        upvar #0 $cur_d line_data
        set lno [lindex [split [$cur_w index $pos] .] 0]
@@ -881,12 +901,6 @@ method _showcommit {cur_w lno} {
                                set enc [tcl_encoding $enc]
                                if {$enc ne {}} {
                                        set msg [encoding convertfrom $enc $msg]
-                                       set author_name [encoding convertfrom $enc $author_name]
-                                       set committer_name [encoding convertfrom $enc $committer_name]
-                                       set header($cmit,author) $author_name
-                                       set header($cmit,committer) $committer_name
-                                       set header($cmit,summary) \
-                                       [encoding convertfrom $enc $header($cmit,summary)]
                                }
                                set msg [string trim $msg]
                        }
@@ -942,9 +956,20 @@ method _format_offset_date {base offset} {
 }
 
 method _gitkcommit {} {
+       global nullid
+
        set dat [_get_click_amov_info $this]
        if {$dat ne {}} {
                set cmit [lindex $dat 0]
+
+               # If the line belongs to the working copy, use HEAD instead
+               if {$cmit eq $nullid} {
+                       if {[catch {set cmit [git rev-parse --verify HEAD]} err]} {
+                               error_popup [strcat [mc "Cannot find HEAD commit:"] "\n\n$err"]
+                               return;
+                       }
+               }
+
                set radius [get_config gui.blamehistoryctx]
                set cmdline [list --select-commit=$cmit]
 
@@ -981,12 +1006,20 @@ method _gitkcommit {} {
 }
 
 method _blameparent {} {
+       global nullid
+
        set dat [_get_click_amov_info $this]
        if {$dat ne {}} {
                set cmit [lindex $dat 0]
                set new_path [lindex $dat 1]
 
-               if {[catch {set cparent [git rev-parse --verify "$cmit^"]}]} {
+               # Allow using Blame Parent on lines modified in the working copy
+               if {$cmit eq $nullid} {
+                       set parent_ref "HEAD"
+               } else {
+                       set parent_ref "$cmit^"
+               }
+               if {[catch {set cparent [git rev-parse --verify $parent_ref]} err]} {
                        error_popup [strcat [mc "Cannot find parent commit:"] "\n\n$err"]
                        return;
                }
@@ -996,8 +1029,12 @@ method _blameparent {} {
                # Generate a diff between the commit and its parent,
                # and use the hunks to update the line number.
                # Request zero context to simplify calculations.
-               if {[catch {set fd [eval git_read diff-tree \
-                               --unified=0 $cparent $cmit $new_path]} err]} {
+               if {$cmit eq $nullid} {
+                       set diffcmd [list diff-index --unified=0 $cparent -- $new_path]
+               } else {
+                       set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
+               }
+               if {[catch {set fd [eval git_read $diffcmd]} err]} {
                        $status stop [mc "Unable to display parent"]
                        error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
                        return
index 2977315..3345149 100644 (file)
@@ -168,7 +168,7 @@ File %s cannot be committed by this program.
                }
                }
        }
-       if {!$files_ready && ![string match *merge $curType]} {
+       if {!$files_ready && ![string match *merge $curType] && ![is_enabled nocommit]} {
                info_popup [mc "No changes to commit.
 
 You must stage at least 1 file before you can commit.
@@ -177,6 +177,8 @@ You must stage at least 1 file before you can commit.
                return
        }
 
+       if {[is_enabled nocommitmsg]} { do_quit 0 }
+
        # -- A message is required.
        #
        set msg [string trim [$ui_comm get 1.0 end]]
@@ -212,6 +214,8 @@ A good commit message has the following format:
        puts $msg_wt $msg
        close $msg_wt
 
+       if {[is_enabled nocommit]} { do_quit 0 }
+
        # -- Run the pre-commit hook.
        #
        set fd_ph [githook_read pre-commit]
@@ -410,7 +414,7 @@ A rescan will be automatically started now.
                set ::GITGUI_BCK_exists 0
        }
 
-       if {[is_enabled singlecommit]} do_quit
+       if {[is_enabled singlecommit]} { do_quit 0 }
 
        # -- Update in memory status
        #
index a30c80a..abe502d 100644 (file)
@@ -40,6 +40,15 @@ proc reshow_diff {} {
        }
 }
 
+proc force_diff_encoding {enc} {
+       global current_diff_path
+       
+       if {$current_diff_path ne {}} {
+               force_path_encoding $current_diff_path $enc
+               reshow_diff
+       }
+}
+
 proc handle_empty_diff {} {
        global current_diff_path file_states file_lists
 
@@ -60,9 +69,9 @@ A rescan will be automatically started to find other files which may have the sa
        rescan ui_ready 0
 }
 
-proc show_diff {path w {lno {}} {scroll_pos {}}} {
+proc show_diff {path w {lno {}} {scroll_pos {}} {callback {}}} {
        global file_states file_lists
-       global is_3way_diff diff_active repo_config
+       global is_3way_diff is_conflict_diff diff_active repo_config
        global ui_diff ui_index ui_workdir
        global current_diff_path current_diff_side current_diff_header
        global current_diff_queue
@@ -83,36 +92,42 @@ proc show_diff {path w {lno {}} {scroll_pos {}}} {
 
        set s $file_states($path)
        set m [lindex $s 0]
+       set is_conflict_diff 0
        set current_diff_path $path
        set current_diff_side $w
        set current_diff_queue {}
        ui_status [mc "Loading diff of %s..." [escape_path $path]]
 
+       set cont_info [list $scroll_pos $callback]
+
        if {[string first {U} $m] >= 0} {
-               merge_load_stages $path [list show_unmerged_diff $scroll_pos]
+               merge_load_stages $path [list show_unmerged_diff $cont_info]
        } elseif {$m eq {_O}} {
-               show_other_diff $path $w $m $scroll_pos
+               show_other_diff $path $w $m $cont_info
        } else {
-               start_show_diff $scroll_pos
+               start_show_diff $cont_info
        }
 }
 
-proc show_unmerged_diff {scroll_pos} {
+proc show_unmerged_diff {cont_info} {
        global current_diff_path current_diff_side
-       global merge_stages ui_diff
+       global merge_stages ui_diff is_conflict_diff
        global current_diff_queue
 
        if {$merge_stages(2) eq {}} {
+               set is_conflict_diff 1
                lappend current_diff_queue \
                        [list "LOCAL: deleted\nREMOTE:\n" d======= \
                            [list ":1:$current_diff_path" ":3:$current_diff_path"]]
        } elseif {$merge_stages(3) eq {}} {
+               set is_conflict_diff 1
                lappend current_diff_queue \
                        [list "REMOTE: deleted\nLOCAL:\n" d======= \
                            [list ":1:$current_diff_path" ":2:$current_diff_path"]]
        } elseif {[lindex $merge_stages(1) 0] eq {120000}
                || [lindex $merge_stages(2) 0] eq {120000}
                || [lindex $merge_stages(3) 0] eq {120000}} {
+               set is_conflict_diff 1
                lappend current_diff_queue \
                        [list "LOCAL:\n" d======= \
                            [list ":1:$current_diff_path" ":2:$current_diff_path"]]
@@ -120,14 +135,14 @@ proc show_unmerged_diff {scroll_pos} {
                        [list "REMOTE:\n" d======= \
                            [list ":1:$current_diff_path" ":3:$current_diff_path"]]
        } else {
-               start_show_diff $scroll_pos
+               start_show_diff $cont_info
                return
        }
 
-       advance_diff_queue $scroll_pos
+       advance_diff_queue $cont_info
 }
 
-proc advance_diff_queue {scroll_pos} {
+proc advance_diff_queue {cont_info} {
        global current_diff_queue ui_diff
 
        set item [lindex $current_diff_queue 0]
@@ -137,10 +152,10 @@ proc advance_diff_queue {scroll_pos} {
        $ui_diff insert end [lindex $item 0] [lindex $item 1]
        $ui_diff conf -state disabled
 
-       start_show_diff $scroll_pos [lindex $item 2]
+       start_show_diff $cont_info [lindex $item 2]
 }
 
-proc show_other_diff {path w m scroll_pos} {
+proc show_other_diff {path w m cont_info} {
        global file_states file_lists
        global is_3way_diff diff_active repo_config
        global ui_diff ui_index ui_workdir
@@ -165,7 +180,9 @@ proc show_other_diff {path w m scroll_pos} {
                                }
                                file {
                                        set fd [open $path r]
-                                       fconfigure $fd -eofchar {}
+                                       fconfigure $fd \
+                                               -eofchar {} \
+                                               -encoding [get_path_encoding $path]
                                        set content [read $fd $max_sz]
                                        close $fd
                                        set sz [file size $path]
@@ -217,16 +234,21 @@ proc show_other_diff {path w m scroll_pos} {
                $ui_diff conf -state disabled
                set diff_active 0
                unlock_index
+               set scroll_pos [lindex $cont_info 0]
                if {$scroll_pos ne {}} {
                        update
                        $ui_diff yview moveto $scroll_pos
                }
                ui_ready
+               set callback [lindex $cont_info 1]
+               if {$callback ne {}} {
+                       eval $callback
+               }
                return
        }
 }
 
-proc start_show_diff {scroll_pos {add_opts {}}} {
+proc start_show_diff {cont_info {add_opts {}}} {
        global file_states file_lists
        global is_3way_diff diff_active repo_config
        global ui_diff ui_index ui_workdir
@@ -279,14 +301,14 @@ proc start_show_diff {scroll_pos {add_opts {}}} {
        set ::current_diff_inheader 1
        fconfigure $fd \
                -blocking 0 \
-               -encoding binary \
-               -translation binary
-       fileevent $fd readable [list read_diff $fd $scroll_pos]
+               -encoding [get_path_encoding $path] \
+               -translation lf
+       fileevent $fd readable [list read_diff $fd $cont_info]
 }
 
-proc read_diff {fd scroll_pos} {
+proc read_diff {fd cont_info} {
        global ui_diff diff_active
-       global is_3way_diff current_diff_header
+       global is_3way_diff is_conflict_diff current_diff_header
        global current_diff_queue
 
        $ui_diff conf -state normal
@@ -334,6 +356,7 @@ proc read_diff {fd scroll_pos} {
                        {--} {set tags d_--}
                        {++} {
                                if {[regexp {^\+\+([<>]{7} |={7})} $line _g op]} {
+                                       set is_conflict_diff 1
                                        set line [string replace $line 0 1 {  }]
                                        set tags d$op
                                } else {
@@ -353,6 +376,7 @@ proc read_diff {fd scroll_pos} {
                        {-} {set tags d_-}
                        {+} {
                                if {[regexp {^\+([<>]{7} |={7})} $line _g op]} {
+                                       set is_conflict_diff 1
                                        set line [string replace $line 0 0 { }]
                                        set tags d$op
                                } else {
@@ -377,12 +401,13 @@ proc read_diff {fd scroll_pos} {
                close $fd
 
                if {$current_diff_queue ne {}} {
-                       advance_diff_queue $scroll_pos
+                       advance_diff_queue $cont_info
                        return
                }
 
                set diff_active 0
                unlock_index
+               set scroll_pos [lindex $cont_info 0]
                if {$scroll_pos ne {}} {
                        update
                        $ui_diff yview moveto $scroll_pos
@@ -392,6 +417,10 @@ proc read_diff {fd scroll_pos} {
                if {[$ui_diff index end] eq {2.0}} {
                        handle_empty_diff
                }
+               set callback [lindex $cont_info 1]
+               if {$callback ne {}} {
+                       eval $callback
+               }
        }
 }
 
@@ -432,8 +461,9 @@ proc apply_hunk {x y} {
        }
 
        if {[catch {
+               set enc [get_path_encoding $current_diff_path]
                set p [eval git_write $apply_cmd]
-               fconfigure $p -translation binary -encoding binary
+               fconfigure $p -translation binary -encoding $enc
                puts -nonewline $p $current_diff_header
                puts -nonewline $p [$ui_diff get $s_lno $e_lno]
                close $p} err]} {
@@ -601,8 +631,9 @@ proc apply_line {x y} {
        set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch"
 
        if {[catch {
+               set enc [get_path_encoding $current_diff_path]
                set p [eval git_write $apply_cmd]
-               fconfigure $p -translation binary -encoding binary
+               fconfigure $p -translation binary -encoding $enc
                puts -nonewline $p $current_diff_header
                puts -nonewline $p $patch
                close $p} err]} {
index 7f06b0d..32668fc 100644 (file)
@@ -206,7 +206,7 @@ set encoding_aliases {
     { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
     { GBK CP936 MS936 windows-936 }
     { JIS_Encoding csJISEncoding }
-    { Shift_JIS MS_Kanji csShiftJIS }
+    { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
     { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
       EUC-JP }
     { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
@@ -240,37 +240,227 @@ set encoding_aliases {
     { Big5 csBig5 }
 }
 
+set encoding_groups {
+    {"" ""
+       {"Unicode" UTF-8}
+       {"Western" ISO-8859-1}}
+    {we "West European"
+       {"Western" ISO-8859-15 CP-437 CP-850 MacRoman CP-1252 Windows-1252}
+       {"Celtic" ISO-8859-14}
+       {"Greek" ISO-8859-14 ISO-8859-7 CP-737 CP-869 MacGreek CP-1253 Windows-1253}
+       {"Icelandic" MacIceland MacIcelandic CP-861}
+       {"Nordic" ISO-8859-10 CP-865}
+       {"Portuguese" CP-860}
+       {"South European" ISO-8859-3}}
+    {ee "East European"
+       {"Baltic" CP-775 ISO-8859-4 ISO-8859-13 CP-1257 Windows-1257}
+       {"Central European" CP-852 ISO-8859-2 MacCE CP-1250 Windows-1250}
+       {"Croatian" MacCroatian}
+       {"Cyrillic" CP-855 ISO-8859-5 ISO-IR-111 KOI8-R MacCyrillic CP-1251 Windows-1251}
+       {"Russian" CP-866}
+       {"Ukrainian" KOI8-U MacUkraine MacUkrainian}
+       {"Romanian" ISO-8859-16 MacRomania MacRomanian}}
+    {ea "East Asian"
+       {"Generic" ISO-2022}
+       {"Chinese Simplified" GB2312 GB1988 GB12345 GB2312-RAW GBK EUC-CN GB18030 HZ ISO-2022-CN}
+       {"Chinese Traditional" Big5 Big5-HKSCS EUC-TW CP-950}
+       {"Japanese" EUC-JP ISO-2022-JP Shift-JIS JIS-0212 JIS-0208 JIS-0201 CP-932 MacJapan}
+       {"Korean" EUC-KR UHC JOHAB ISO-2022-KR CP-949 KSC5601}}
+    {sa "SE & SW Asian"
+       {"Armenian" ARMSCII-8}
+       {"Georgian" GEOSTD8}
+       {"Thai" TIS-620 ISO-8859-11 CP-874 Windows-874 MacThai}
+       {"Turkish" CP-857 CP857 ISO-8859-9 MacTurkish CP-1254 Windows-1254}
+       {"Vietnamese" TCVN VISCII VPS CP-1258 Windows-1258}
+       {"Hindi" MacDevanagari}
+       {"Gujarati" MacGujarati}
+       {"Gurmukhi" MacGurmukhi}}
+    {me "Middle Eastern"
+       {"Arabic" ISO-8859-6 Windows-1256 CP-1256 CP-864 MacArabic}
+       {"Farsi" MacFarsi}
+       {"Hebrew" ISO-8859-8-I Windows-1255 CP-1255 ISO-8859-8 CP-862 MacHebrew}}
+    {mi "Misc"
+       {"7-bit" ASCII}
+       {"16-bit" Unicode}
+       {"Legacy" CP-863 EBCDIC}
+       {"Symbol" Symbol Dingbats MacDingbats MacCentEuro}}
+}
+
+proc build_encoding_table {} {
+       global encoding_aliases encoding_lookup_table
+
+       # Prepare the lookup list; cannot use lsort -nocase because
+       # of compatibility issues with older Tcl (e.g. in msysgit)
+       set names [list]
+       foreach item [encoding names] {
+               lappend names [list [string tolower $item] $item]
+       }
+       set names [lsort -ascii -index 0 $names]
+       # neither can we use lsearch -index
+       set lnames [list]
+       foreach item $names {
+               lappend lnames [lindex $item 0]
+       }
+
+       foreach grp $encoding_aliases {
+               set target {}
+               foreach item $grp {
+                       set i [lsearch -sorted -ascii $lnames \
+                                       [string tolower $item]]
+                       if {$i >= 0} {
+                               set target [lindex $names $i 1]
+                               break
+                       }
+               }
+               if {$target eq {}} continue
+               foreach item $grp {
+                       set encoding_lookup_table([string tolower $item]) $target
+               }
+       }
+
+       foreach item $names {
+               set encoding_lookup_table([lindex $item 0]) [lindex $item 1]
+       }
+}
+
 proc tcl_encoding {enc} {
-    global encoding_aliases
-    set names [encoding names]
-    set lcnames [string tolower $names]
-    set enc [string tolower $enc]
-    set i [lsearch -exact $lcnames $enc]
-    if {$i < 0} {
-       # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
-       if {[regsub {^iso[-_]} $enc iso encx]} {
-           set i [lsearch -exact $lcnames $encx]
+       global encoding_lookup_table
+       if {$enc eq {}} {
+               return {}
+       }
+       if {![info exists encoding_lookup_table]} {
+               build_encoding_table
+       }
+       set enc [string tolower $enc]
+       if {![info exists encoding_lookup_table($enc)]} {
+               # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
+               if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
+                       set enc $encx
+               }
+       }
+       if {[info exists encoding_lookup_table($enc)]} {
+               return $encoding_lookup_table($enc)
+       } else {
+               return {}
+       }
+}
+
+proc force_path_encoding {path enc} {
+       global path_encoding_overrides last_encoding_override
+
+       set enc [tcl_encoding $enc]
+       if {$enc eq {}} {
+               catch { unset last_encoding_override }
+               catch { unset path_encoding_overrides($path) }
+       } else {
+               set last_encoding_override $enc
+               if {$path ne {}} {
+                       set path_encoding_overrides($path) $enc
+               }
+       }
+}
+
+proc get_path_encoding {path} {
+       global path_encoding_overrides last_encoding_override
+
+       if {[info exists last_encoding_override]} {
+               set tcl_enc $last_encoding_override
+       } else {
+               set tcl_enc [tcl_encoding [get_config gui.encoding]]
        }
-    }
-    if {$i < 0} {
-       foreach l $encoding_aliases {
-           set ll [string tolower $l]
-           if {[lsearch -exact $ll $enc] < 0} continue
-           # look through the aliases for one that tcl knows about
-           foreach e $ll {
-               set i [lsearch -exact $lcnames $e]
-               if {$i < 0} {
-                   if {[regsub {^iso[-_]} $e iso ex]} {
-                       set i [lsearch -exact $lcnames $ex]
-                   }
+       if {$tcl_enc eq {}} {
+               set tcl_enc [encoding system]
+       }
+       if {$path ne {}} {
+               if {[info exists path_encoding_overrides($path)]} {
+                       set enc2 $path_encoding_overrides($path)
+               } else {
+                       set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
+               }
+               if {$enc2 ne {}} {
+                       set tcl_enc $enc2
+               }
+       }
+       return $tcl_enc
+}
+
+proc build_encoding_submenu {parent grp cmd} {
+       global used_encodings
+
+       set mid [lindex $grp 0]
+       set gname [mc [lindex $grp 1]]
+
+       set smenu {}
+       foreach subset [lrange $grp 2 end] {
+               set name [mc [lindex $subset 0]]
+
+               foreach enc [lrange $subset 1 end] {
+                       set tcl_enc [tcl_encoding $enc]
+                       if {$tcl_enc eq {}} continue
+
+                       if {$smenu eq {}} {
+                               if {$mid eq {}} {
+                                       set smenu $parent
+                               } else {
+                                       set smenu "$parent.$mid"
+                                       menu $smenu
+                                       $parent add cascade \
+                                               -label $gname \
+                                               -menu $smenu
+                               }
+                       }
+
+                       if {$name ne {}} {
+                               set lbl "$name ($enc)"
+                       } else {
+                               set lbl $enc
+                       }
+                       $smenu add command \
+                               -label $lbl \
+                               -command [concat $cmd [list $tcl_enc]]
+
+                       lappend used_encodings $tcl_enc
+               }
+       }
+}
+
+proc popup_btn_menu {m b} {
+       tk_popup $m [winfo pointerx $b] [winfo pointery $b]
+}
+
+proc build_encoding_menu {emenu cmd {nodef 0}} {
+       $emenu configure -postcommand \
+               [list do_build_encoding_menu $emenu $cmd $nodef]
+}
+
+proc do_build_encoding_menu {emenu cmd {nodef 0}} {
+       global used_encodings encoding_groups
+
+       $emenu configure -postcommand {}
+
+       if {!$nodef} {
+               $emenu add command \
+                       -label [mc "Default"] \
+                       -command [concat $cmd [list {}]]
+       }
+       set sysenc [encoding system]
+       $emenu add command \
+               -label [mc "System (%s)" $sysenc] \
+               -command [concat $cmd [list $sysenc]]
+
+       # Main encoding tree
+       set used_encodings [list identity]
+       $emenu add separator
+       foreach grp $encoding_groups {
+               build_encoding_submenu $emenu $grp $cmd
+       }
+
+       # Add unclassified encodings
+       set unused_grp [list [mc Other]]
+       foreach enc [encoding names] {
+               if {[lsearch -exact $used_encodings $enc] < 0} {
+                       lappend unused_grp $enc
                }
-               if {$i >= 0} break
-           }
-           break
        }
-    }
-    if {$i >= 0} {
-       return [lindex $names $i]
-    }
-    return {}
+       build_encoding_submenu $emenu [list other [mc Other] $unused_grp] $cmd
 }
index 79c58bc..6ab5701 100644 (file)
@@ -5,24 +5,51 @@ proc merge_resolve_one {stage} {
        global current_diff_path
 
        switch -- $stage {
-               1 { set target [mc "the base version"] }
-               2 { set target [mc "this branch"] }
-               3 { set target [mc "the other branch"] }
+               1 { set targetquestion [mc "Force resolution to the base version?"] }
+               2 { set targetquestion [mc "Force resolution to this branch?"] }
+               3 { set targetquestion [mc "Force resolution to the other branch?"] }
        }
 
-       set op_question [mc "Force resolution to %s?
-Note that the diff shows only conflicting changes.
+       set op_question [strcat $targetquestion "\n" \
+[mc "Note that the diff shows only conflicting changes.
 
 %s will be overwritten.
 
 This operation can be undone only by restarting the merge." \
-               $target [short_path $current_diff_path]]
+               [short_path $current_diff_path]]]
 
        if {[ask_popup $op_question] eq {yes}} {
                merge_load_stages $current_diff_path [list merge_force_stage $stage]
        }
 }
 
+proc merge_stage_workdir {path w lno} {
+       global current_diff_path diff_active
+
+       if {$diff_active} return
+
+       if {$path ne $current_diff_path} {
+               show_diff $path $w $lno {} [list do_merge_stage_workdir $path]
+       } else {
+               do_merge_stage_workdir $path
+       }
+}
+
+proc do_merge_stage_workdir {path} {
+       global current_diff_path is_conflict_diff
+
+       if {$path ne $current_diff_path} return;
+
+       if {$is_conflict_diff} {
+               if {[ask_popup [mc "File %s seems to have unresolved conflicts, still stage?" \
+                               [short_path $path]]] ne {yes}} {
+                       return
+               }
+       }
+
+       merge_add_resolution $path
+}
+
 proc merge_add_resolution {path} {
        global current_diff_path ui_workdir
 
index 9b865f6..c80c939 100644 (file)
@@ -1,6 +1,28 @@
 # git-gui options editor
 # Copyright (C) 2006, 2007 Shawn Pearce
 
+proc config_check_encodings {} {
+       global repo_config_new global_config_new
+
+       set enc $global_config_new(gui.encoding)
+       if {$enc eq {}} {
+               set global_config_new(gui.encoding) [encoding system]
+       } elseif {[tcl_encoding $enc] eq {}} {
+               error_popup [mc "Invalid global encoding '%s'" $enc]
+               return 0
+       }
+
+       set enc $repo_config_new(gui.encoding)
+       if {$enc eq {}} {
+               set repo_config_new(gui.encoding) [encoding system]
+       } elseif {[tcl_encoding $enc] eq {}} {
+               error_popup [mc "Invalid repo encoding '%s'" $enc]
+               return 0
+       }
+
+       return 1
+}
+
 proc save_config {} {
        global default_config font_descs
        global repo_config global_config
@@ -130,6 +152,7 @@ proc do_options {} {
                {i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}}
                {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}}
                {t gui.newbranchtemplate {mc "New Branch Name Template"}}
+               {c gui.encoding {mc "Default File Contents Encoding"}}
                } {
                set type [lindex $option 0]
                set name [lindex $option 1]
@@ -159,6 +182,7 @@ proc do_options {} {
                                pack $w.$f.$optid.v -side right -anchor e -padx 5
                                pack $w.$f.$optid -side top -anchor w -fill x
                        }
+                       c -
                        t {
                                frame $w.$f.$optid
                                label $w.$f.$optid.l -text "$text:"
@@ -171,6 +195,16 @@ proc do_options {} {
                                pack $w.$f.$optid.v -side left -anchor w \
                                        -fill x -expand 1 \
                                        -padx 5
+                               if {$type eq {c}} {
+                                       menu $w.$f.$optid.m
+                                       build_encoding_menu $w.$f.$optid.m \
+                                               [list set ${f}_config_new($name)] 1
+                                       button $w.$f.$optid.b \
+                                               -text [mc "Change"] \
+                                               -command [list popup_btn_menu \
+                                                       $w.$f.$optid.m $w.$f.$optid.b]
+                                       pack $w.$f.$optid.b -side left -anchor w
+                               }
                                pack $w.$f.$optid -side top -anchor w -fill x
                        }
                        }
@@ -275,6 +309,7 @@ proc do_restore_defaults {} {
 }
 
 proc do_save_config {w} {
+       if {![config_check_encodings]} return
        if {[catch {save_config} err]} {
                error_popup [strcat [mc "Failed to completely save options:"] "\n\n$err"]
        }
index fa43947..793cca1 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-02 08:58+0200\n"
-"PO-Revision-Date: 2008-08-02 09:09+0200\n"
+"POT-Creation-Date: 2008-09-13 10:20+0200\n"
+"PO-Revision-Date: 2008-09-13 10:24+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -110,7 +110,15 @@ msgstr "Teilweise bereitgestellt zum Eintragen"
 msgid "Staged for commit, missing"
 msgstr "Bereitgestellt zum Eintragen, fehlend"
 
-#: git-gui.sh:1597
+#: git-gui.sh:1658
+msgid "File type changed, not staged"
+msgstr "Dateityp geändert, nicht bereitgestellt"
+
+#: git-gui.sh:1659
+msgid "File type changed, staged"
+msgstr "Dateityp geändert, bereitgestellt"
+
+#: git-gui.sh:1661
 msgid "Untracked, not staged"
 msgstr "Nicht unter Versionskontrolle, nicht bereitgestellt"
 
@@ -396,15 +404,7 @@ msgstr "Alle kopieren"
 msgid "File:"
 msgstr "Datei:"
 
-#: git-gui.sh:2589
-msgid "Apply/Reverse Hunk"
-msgstr "Kontext anwenden/umkehren"
-
-#: git-gui.sh:2696
-msgid "Apply/Reverse Line"
-msgstr "Zeile anwenden/umkehren"
-
-#: git-gui.sh:2711
+#: git-gui.sh:2834
 msgid "Refresh"
 msgstr "Aktualisieren"
 
@@ -416,7 +416,35 @@ msgstr "Schriftgröße verkleinern"
 msgid "Increase Font Size"
 msgstr "Schriftgröße vergrößern"
 
-#: git-gui.sh:2646
+#: git-gui.sh:2870
+msgid "Apply/Reverse Hunk"
+msgstr "Kontext anwenden/umkehren"
+
+#: git-gui.sh:2875
+msgid "Apply/Reverse Line"
+msgstr "Zeile anwenden/umkehren"
+
+#: git-gui.sh:2885
+msgid "Run Merge Tool"
+msgstr "Zusammenführungswerkzeug"
+
+#: git-gui.sh:2890
+msgid "Use Remote Version"
+msgstr "Entfernte Version benutzen"
+
+#: git-gui.sh:2894
+msgid "Use Local Version"
+msgstr "Lokale Version benutzen"
+
+#: git-gui.sh:2898
+msgid "Revert To Base"
+msgstr "Ursprüngliche Version benutzen"
+
+#: git-gui.sh:2906
+msgid "Stage Working Copy"
+msgstr "Arbeitskopie bereitstellen"
+
+#: git-gui.sh:2925
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
@@ -498,7 +526,15 @@ msgstr "Version kopieren"
 msgid "Do Full Copy Detection"
 msgstr "Volle Kopie-Erkennung"
 
-#: lib/blame.tcl:388
+#: lib/blame.tcl:263
+msgid "Show History Context"
+msgstr "Historien-Kontext anzeigen"
+
+#: lib/blame.tcl:266
+msgid "Blame Parent Commit"
+msgstr "Elternversion annotieren"
+
+#: lib/blame.tcl:394
 #, tcl-format
 msgid "Reading %s..."
 msgstr "%s lesen..."
@@ -547,7 +583,19 @@ msgstr "Eintragender:"
 msgid "Original File:"
 msgstr "Ursprüngliche Datei:"
 
-#: lib/blame.tcl:925
+#: lib/blame.tcl:990
+msgid "Cannot find parent commit:"
+msgstr "Elternversion kann nicht gefunden werden:"
+
+#: lib/blame.tcl:1001
+msgid "Unable to display parent"
+msgstr "Elternversion kann nicht angezeigt werden"
+
+#: lib/blame.tcl:1002 lib/diff.tcl:191
+msgid "Error loading diff:"
+msgstr "Fehler beim Laden des Vergleichs:"
+
+#: lib/blame.tcl:1142
 msgid "Originally By:"
 msgstr "Ursprünglich von:"
 
@@ -1494,11 +1542,7 @@ msgstr "Git-Projektarchiv (Unterprojekt)"
 msgid "* Binary file (not showing content)."
 msgstr "* Binärdatei (Inhalt wird nicht angezeigt)"
 
-#: lib/diff.tcl:185
-msgid "Error loading diff:"
-msgstr "Fehler beim Laden des Vergleichs:"
-
-#: lib/diff.tcl:303
+#: lib/diff.tcl:313
 msgid "Failed to unstage selected hunk."
 msgstr ""
 "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
@@ -1586,6 +1630,15 @@ msgstr ""
 msgid "Do Nothing"
 msgstr "Nichts tun"
 
+#: lib/index.tcl:419
+msgid "Reverting selected files"
+msgstr "Änderungen in gewählten Dateien verwerfen"
+
+#: lib/index.tcl:423
+#, tcl-format
+msgid "Reverting %s"
+msgstr "Änderungen in %s verwerfen"
+
 #: lib/merge.tcl:13
 msgid ""
 "Cannot merge while amending.\n"
@@ -1730,6 +1783,96 @@ msgstr "Abbruch fehlgeschlagen."
 msgid "Abort completed.  Ready."
 msgstr "Abbruch durchgeführt. Bereit."
 
+#: lib/mergetool.tcl:14
+msgid "Force resolution to the base version?"
+msgstr "Konflikt durch Basisversion ersetzen?"
+
+#: lib/mergetool.tcl:15
+msgid "Force resolution to this branch?"
+msgstr "Konflikt durch diesen Zweig ersetzen?"
+
+#: lib/mergetool.tcl:16
+msgid "Force resolution to the other branch?"
+msgstr "Konflikt durch anderen Zweig ersetzen?"
+
+#: lib/mergetool.tcl:20
+#, tcl-format
+msgid ""
+"Note that the diff shows only conflicting changes.\n"
+"\n"
+"%s will be overwritten.\n"
+"\n"
+"This operation can be undone only by restarting the merge."
+msgstr ""
+"Hinweis: Der Vergleich zeigt nur konfliktverursachende Änderungen an.\n"
+"\n"
+"»%s« wird überschrieben.\n"
+"\n"
+"Diese Operation kann nur rückgängig gemacht werden, wenn die\n"
+"Zusammenführung erneut gestartet wird."
+
+#: lib/mergetool.tcl:32
+#, tcl-format
+msgid "Adding resolution for %s"
+msgstr "Auflösung hinzugefügt für %s"
+
+#: lib/mergetool.tcl:119
+msgid "Cannot resolve deletion or link conflicts using a tool"
+msgstr ""
+"Konflikte durch gelöschte Dateien oder symbolische Links können nicht durch "
+"das Zusamenführungswerkzeug gelöst werden."
+
+#: lib/mergetool.tcl:124
+msgid "Conflict file does not exist"
+msgstr "Konflikt-Datei existiert nicht"
+
+#: lib/mergetool.tcl:236
+#, tcl-format
+msgid "Not a GUI merge tool: '%s'"
+msgstr "Kein GUI Zusammenführungswerkzeug: »%s«"
+
+#: lib/mergetool.tcl:240
+#, tcl-format
+msgid "Unsupported merge tool '%s'"
+msgstr "Unbekanntes Zusammenführungswerkzeug: »%s«"
+
+#: lib/mergetool.tcl:275
+msgid "Merge tool is already running, terminate it?"
+msgstr "Zusammenführungswerkzeug läuft bereits. Soll es abgebrochen werden?"
+
+#: lib/mergetool.tcl:295
+#, tcl-format
+msgid ""
+"Error retrieving versions:\n"
+"%s"
+msgstr ""
+"Fehler beim Abrufen der Dateiversionen:\n"
+"%s"
+
+#: lib/mergetool.tcl:315
+#, tcl-format
+msgid ""
+"Could not start the merge tool:\n"
+"\n"
+"%s"
+msgstr ""
+"Zusammenführungswerkzeug konnte nicht gestartet werden:\n"
+"\n"
+"%s"
+
+#: lib/mergetool.tcl:319
+msgid "Running merge tool..."
+msgstr "Zusammenführungswerkzeug starten..."
+
+#: lib/mergetool.tcl:347 lib/mergetool.tcl:363
+msgid "Merge tool failed."
+msgstr "Zusammenführungswerkzeug fehlgeschlagen."
+
+#: lib/mergetool.tcl:353
+#, tcl-format
+msgid "File %s unchanged, still accept as resolved?"
+msgstr "Datei »%s« unverändert. Trotzdem Konflikt als gelöst akzeptieren?"
+
 #: lib/option.tcl:95
 msgid "Restore Defaults"
 msgstr "Voreinstellungen wiederherstellen"
@@ -1767,7 +1910,11 @@ msgstr "Ausführlichkeit der Zusammenführen-Meldungen"
 msgid "Show Diffstat After Merge"
 msgstr "Vergleichsstatistik nach Zusammenführen anzeigen"
 
-#: lib/option.tcl:123
+#: lib/option.tcl:122
+msgid "Use Merge Tool"
+msgstr "Zusammenführungswerkzeug"
+
+#: lib/option.tcl:124
 msgid "Trust File Modification Timestamps"
 msgstr "Auf Dateiänderungsdatum verlassen"
 
@@ -1788,6 +1935,10 @@ msgid "Minimum Letters To Blame Copy On"
 msgstr "Mindestzahl Zeichen für Kopie-Annotieren"
 
 #: lib/option.tcl:128
+msgid "Blame History Context Radius (days)"
+msgstr "Anzahl Tage für Historien-Kontext"
+
+#: lib/option.tcl:129
 msgid "Number of Diff Context Lines"
 msgstr "Anzahl der Kontextzeilen beim Vergleich"
 
index 929d681..edb6ec6 100755 (executable)
@@ -284,7 +284,7 @@ do_next () {
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
                make_patch $sha1
-               : > "$DOTEST"/amend
+               git rev-parse --verify HEAD > "$DOTEST"/amend
                warn "Stopped at $sha1... $rest"
                warn "You can amend the commit now, with"
                warn
@@ -427,14 +427,22 @@ do
                else
                        . "$DOTEST"/author-script ||
                                die "Cannot find the author identity"
+                       amend=
                        if test -f "$DOTEST"/amend
                        then
+                               amend=$(git rev-parse --verify HEAD)
+                               test "$amend" = $(cat "$DOTEST"/amend) ||
+                               die "\
+You have uncommitted changes in your working tree. Please, commit them
+first and then run 'git rebase --continue' again."
                                git reset --soft HEAD^ ||
                                die "Cannot rewind the HEAD"
                        fi
                        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE &&
-                       git commit --no-verify -F "$DOTEST"/message -e ||
-                       die "Could not commit staged changes."
+                       git commit --no-verify -F "$DOTEST"/message -e || {
+                               test -n "$amend" && git reset --soft $amend
+                               die "Could not commit staged changes."
+                       }
                fi
 
                require_clean_work_tree
index 683960b..d39eb6c 100755 (executable)
@@ -10,7 +10,7 @@ git repack [options]
 a               pack everything in a single pack
 A               same as -a, and turn unreachable objects loose
 d               remove redundant packs, and run git-prune-packed
-f               pass --no-reuse-delta to git-pack-objects
+f               pass --no-reuse-object to git-pack-objects
 n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
index d799c76..6bd2572 100755 (executable)
@@ -144,7 +144,14 @@ show_stash () {
        then
                flags=--stat
        fi
-       s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@")
+
+       if test $# = 0
+       then
+               set x "$ref_stash@{0}"
+               shift
+       fi
+
+       s=$(git rev-parse --revs-only --no-flags "$@")
 
        w_commit=$(git rev-parse --verify "$s") &&
        b_commit=$(git rev-parse --verify "$s^") &&
@@ -163,13 +170,19 @@ apply_stash () {
                shift
        esac
 
+       if test $# = 0
+       then
+               set x "$ref_stash@{0}"
+               shift
+       fi
+
        # current index state
        c_tree=$(git write-tree) ||
                die 'Cannot apply a stash in the middle of a merge'
 
        # stash records the work tree, and is a merge between the
        # base commit (first parent) and the index tree (second parent).
-       s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
+       s=$(git rev-parse --revs-only --no-flags "$@") &&
        w_tree=$(git rev-parse --verify "$s:") &&
        b_tree=$(git rev-parse --verify "$s^1:") &&
        i_tree=$(git rev-parse --verify "$s^2:") ||
index 1c39b59..92be0fe 100755 (executable)
@@ -634,6 +634,14 @@ cmd_sync()
        do
                name=$(module_name "$path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
+
+               # Possibly a url relative to parent
+               case "$url" in
+               ./*|../*)
+                       url=$(resolve_relative_url "$url") || exit
+                       ;;
+               esac
+
                if test -e "$path"/.git
                then
                (
index ee3f5ed..80a5728 100755 (executable)
@@ -2626,9 +2626,9 @@ sub rebuild_from_rev_db {
 sub rebuild {
        my ($self) = @_;
        my $map_path = $self->map_path;
-       return if (-e $map_path && ! -z $map_path);
+       my $partial = (-e $map_path && ! -z $map_path);
        return unless ::verify_ref($self->refname.'^0');
-       if ($self->use_svm_props || $self->no_metadata) {
+       if (!$partial && ($self->use_svm_props || $self->no_metadata)) {
                my $rev_db = $self->rev_db_path;
                $self->rebuild_from_rev_db($rev_db);
                if ($self->use_svm_props) {
@@ -2638,10 +2638,13 @@ sub rebuild {
                $self->unlink_rev_db_symlink;
                return;
        }
-       print "Rebuilding $map_path ...\n";
+       print "Rebuilding $map_path ...\n" if (!$partial);
+       my ($base_rev, $head) = ($partial ? $self->rev_map_max_norebuild(1) :
+               (undef, undef));
        my ($log, $ctx) =
            command_output_pipe(qw/rev-list --pretty=raw --no-color --reverse/,
-                               $self->refname, '--');
+                               ($head ? "$head.." : "") . $self->refname,
+                               '--');
        my $metadata_url = $self->metadata_url;
        remove_username($metadata_url);
        my $svn_uuid = $self->ra_uuid;
@@ -2664,12 +2667,17 @@ sub rebuild {
                    ($metadata_url && $url && ($url ne $metadata_url))) {
                        next;
                }
+               if ($partial && $head) {
+                       print "Partial-rebuilding $map_path ...\n";
+                       print "Currently at $base_rev = $head\n";
+                       $head = undef;
+               }
 
                $self->rev_map_set($rev, $c);
                print "r$rev = $c\n";
        }
        command_close_pipe($log, $ctx);
-       print "Done rebuilding $map_path\n";
+       print "Done rebuilding $map_path\n" if (!$partial || !$head);
        my $rev_db_path = $self->rev_db_path;
        if (-f $self->rev_db_path) {
                unlink $self->rev_db_path or croak "unlink: $!";
@@ -2809,6 +2817,12 @@ sub rev_map_set {
 sub rev_map_max {
        my ($self, $want_commit) = @_;
        $self->rebuild;
+       my ($r, $c) = $self->rev_map_max_norebuild($want_commit);
+       $want_commit ? ($r, $c) : $r;
+}
+
+sub rev_map_max_norebuild {
+       my ($self, $want_commit) = @_;
        my $map_path = $self->map_path;
        stat $map_path or return $want_commit ? (0, undef) : 0;
        sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
@@ -3304,7 +3318,7 @@ sub close_file {
                                        my $out = syswrite($tmp_fh, $str, $res);
                                        defined($out) && $out == $res
                                                or croak("write ",
-                                                       $tmp_fh->filename,
+                                                       Git::temp_path($tmp_fh),
                                                        ": $!\n");
                                }
                                defined $res or croak $!;
@@ -3315,7 +3329,7 @@ sub close_file {
                }
 
                $hash = $::_repository->hash_and_insert_object(
-                               $fh->filename);
+                               Git::temp_path($fh));
                $hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
 
                Git::temp_release($fb->{base}, 1);
@@ -4010,21 +4024,21 @@ sub gs_do_switch {
        my $old_url = $full_url;
        $full_url .= '/' . escape_uri_only($path) if length $path;
        my ($ra, $reparented);
-       if ($old_url ne $full_url) {
-               if ($old_url !~ m#^svn(\+ssh)?://#) {
-                       SVN::_Ra::svn_ra_reparent($self->{session}, $full_url,
-                                                 $pool);
-                       $self->{url} = $full_url;
-                       $reparented = 1;
-               } else {
-                       $_[0] = undef;
-                       $self = undef;
-                       $RA = undef;
-                       $ra = Git::SVN::Ra->new($full_url);
-                       $ra_invalid = 1;
-               }
+
+       if ($old_url =~ m#^svn(\+ssh)?://#) {
+               $_[0] = undef;
+               $self = undef;
+               $RA = undef;
+               $ra = Git::SVN::Ra->new($full_url);
+               $ra_invalid = 1;
+       } elsif ($old_url ne $full_url) {
+               SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool);
+               $self->{url} = $full_url;
+               $reparented = 1;
        }
+
        $ra ||= $self;
+       $url_b = escape_url($url_b);
        my $reporter = $ra->do_switch($rev_b, '', 1, $url_b, $editor, $pool);
        my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
        $reporter->set_path('', $rev_a, 0, @lock, $pool);
@@ -4424,7 +4438,7 @@ sub config_pager {
 
 sub run_pager {
        return unless -t *STDOUT && defined $pager;
-       pipe my $rfd, my $wfd or return;
+       pipe my ($rfd, $wfd) or return;
        defined(my $pid = fork) or ::fatal "Can't fork: $!";
        if (!$pid) {
                open STDOUT, '>&', $wfd or
index 384148a..78d236b 100755 (executable)
@@ -31,7 +31,7 @@ valid_custom_tool()
 
 valid_tool() {
        case "$1" in
-               firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open)
+               firefox | iceweasel | konqueror | w3m | links | lynx | dillo | open | start)
                        ;; # happy
                *)
                        valid_custom_tool "$1" || return 1
@@ -114,6 +114,10 @@ if test -z "$browser" ; then
     if test -n "$SECURITYSESSIONID"; then
        browser_candidates="open $browser_candidates"
     fi
+    # /bin/start indicates MinGW
+    if test -n /bin/start; then
+       browser_candidates="start $browser_candidates"
+    fi
 
     for i in $browser_candidates; do
        init_browser_path $i
@@ -157,7 +161,7 @@ case "$browser" in
                ;;
        esac
        ;;
-    w3m|links|lynx|open)
+    w3m|links|lynx|open|start)
        eval "$browser_path" "$@"
        ;;
     dillo)
diff --git a/git.c b/git.c
index fdb0f71..00f5dd1 100644 (file)
--- a/git.c
+++ b/git.c
@@ -162,6 +162,8 @@ static int handle_alias(int *argcp, const char ***argv)
                            alias_string + 1, alias_command);
                }
                count = split_cmdline(alias_string, &new_argv);
+               if (count < 0)
+                       die("Bad alias.%s string", alias_command);
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
@@ -364,7 +366,7 @@ static void handle_internal_command(int argc, const char **argv)
        if (sizeof(ext) > 1) {
                i = strlen(argv[0]) - strlen(ext);
                if (i > 0 && !strcmp(argv[0] + i, ext)) {
-                       char *argv0 = strdup(argv[0]);
+                       char *argv0 = xstrdup(argv[0]);
                        argv[0] = cmd = argv0;
                        argv0[i] = '\0';
                }
@@ -499,7 +501,9 @@ int main(int argc, const char **argv)
                                cmd, argv[0]);
                        exit(1);
                }
-               help_unknown_cmd(cmd);
+               argv[0] = help_unknown_cmd(cmd);
+               handle_internal_command(argc, argv);
+               execv_dashed_external(argv);
        }
 
        fprintf(stderr, "Failed to run command '%s': %s\n",
index c6492e5..6733b6f 100644 (file)
@@ -145,6 +145,7 @@ rm -rf $RPM_BUILD_ROOT
 %files cvs
 %defattr(-,root,root)
 %doc Documentation/*git-cvs*.txt
+%{_bindir}/git-cvsserver
 %{_libexecdir}/git-core/*cvs*
 %{!?_without_docs: %{_mandir}/man1/*cvs*.1*}
 %{!?_without_docs: %doc Documentation/*git-cvs*.html }
@@ -188,6 +189,9 @@ rm -rf $RPM_BUILD_ROOT
 # No files for you!
 
 %changelog
+* Fri Sep 12 2008 Quy Tonthat <qtonthat@gmail.com>
+- move git-cvsserver to bindir.
+
 * Sun Jun 15 2008 Junio C Hamano <gitster@pobox.com>
 - Remove curl from Requires list.
 
index 29e2156..18e70a3 100755 (executable)
@@ -2123,7 +2123,7 @@ sub parse_commit_text {
                        last;
                }
        }
-       if ($co{'title'} eq "") {
+       if (! defined $co{'title'} || $co{'title'} eq "") {
                $co{'title'} = $co{'title_short'} = '(no commit message)';
        }
        # remove added spaces
@@ -5498,7 +5498,11 @@ sub git_shortlog {
        }
        my $refs = git_get_references();
 
-       my @commitlist = parse_commits($hash, 101, (100 * $page));
+       my $commit_hash = $hash;
+       if (defined $hash_parent) {
+               $commit_hash = "$hash_parent..$hash";
+       }
+       my @commitlist = parse_commits($commit_hash, 101, (100 * $page));
 
        my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#commitlist >= 100);
        my $next_link = '';
diff --git a/graph.c b/graph.c
index e2633f8..5f82170 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -4,6 +4,43 @@
 #include "diff.h"
 #include "revision.h"
 
+/* Internal API */
+
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Output a padding line in the graph.
+ * This is similar to graph_next_line().  However, it is guaranteed to
+ * never print the current commit line.  Instead, if the commit line is
+ * next, it will simply output a line of vertical padding, extending the
+ * branch lines downwards, but leaving them otherwise unchanged.
+ */
+static void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
+
+/*
+ * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
+ * first will be prefixed with the graph output.
+ *
+ * If the strbuf ends with a newline, the output will end after this
+ * newline.  A new graph line will not be printed after the final newline.
+ * If the strbuf is empty, no output will be printed.
+ *
+ * Since the first line will not include the graph ouput, the caller is
+ * responsible for printing this line's graph (perhaps via
+ * graph_show_commit() or graph_show_oneline()) before calling
+ * graph_show_strbuf().
+ */
+static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
+
 /*
  * TODO:
  * - Add colors to the graph.
@@ -180,14 +217,6 @@ struct git_graph *graph_init(struct rev_info *opt)
        return graph;
 }
 
-void graph_release(struct git_graph *graph)
-{
-       free(graph->columns);
-       free(graph->new_columns);
-       free(graph->mapping);
-       free(graph);
-}
-
 static void graph_update_state(struct git_graph *graph, enum graph_state s)
 {
        graph->prev_state = graph->state;
@@ -685,7 +714,7 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
        strbuf_addch(sb, '*');
 }
 
-void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
 {
        int seen_this = 0;
        int i, j;
@@ -760,7 +789,7 @@ void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
                graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
-void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
 {
        int seen_this = 0;
        int i, j;
@@ -801,7 +830,7 @@ void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
                graph_update_state(graph, GRAPH_COLLAPSING);
 }
 
-void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
 {
        int i;
        int *tmp_mapping;
@@ -906,7 +935,7 @@ void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
                graph_update_state(graph, GRAPH_PADDING);
 }
 
-int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
        switch (graph->state) {
        case GRAPH_PADDING:
@@ -933,7 +962,7 @@ int graph_next_line(struct git_graph *graph, struct strbuf *sb)
        return 0;
 }
 
-void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
+static void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
 {
        int i, j;
 
@@ -1055,7 +1084,7 @@ int graph_show_remainder(struct git_graph *graph)
 }
 
 
-void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
+static void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
 {
        char *p;
 
diff --git a/graph.h b/graph.h
index eab4e3d..bc30d68 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -10,11 +10,6 @@ struct git_graph;
  */
 struct git_graph *graph_init(struct rev_info *opt);
 
-/*
- * Destroy a struct git_graph and free associated memory.
- */
-void graph_release(struct git_graph *graph);
-
 /*
  * Update a git_graph with a new commit.
  * This will cause the graph to begin outputting lines for the new commit
@@ -26,26 +21,6 @@ void graph_release(struct git_graph *graph);
  */
 void graph_update(struct git_graph *graph, struct commit *commit);
 
-/*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf.  It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
-/*
- * Output a padding line in the graph.
- * This is similar to graph_next_line().  However, it is guaranteed to
- * never print the current commit line.  Instead, if the commit line is
- * next, it will simply output a line of vertical padding, extending the
- * branch lines downwards, but leaving them otherwise unchanged.
- */
-void graph_padding_line(struct git_graph *graph, struct strbuf *sb);
-
 /*
  * Determine if a graph has finished outputting lines for the current
  * commit.
@@ -89,21 +64,6 @@ void graph_show_padding(struct git_graph *graph);
  */
 int graph_show_remainder(struct git_graph *graph);
 
-/*
- * Print a strbuf to stdout.  If the graph is non-NULL, all lines but the
- * first will be prefixed with the graph output.
- *
- * If the strbuf ends with a newline, the output will end after this
- * newline.  A new graph line will not be printed after the final newline.
- * If the strbuf is empty, no output will be printed.
- *
- * Since the first line will not include the graph ouput, the caller is
- * responsible for printing this line's graph (perhaps via
- * graph_show_commit() or graph_show_oneline()) before calling
- * graph_show_strbuf().
- */
-void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb);
-
 /*
  * Print a commit message strbuf and the remainder of the graph to stdout.
  *
diff --git a/help.c b/help.c
index b278257..fd87bb5 100644 (file)
--- a/help.c
+++ b/help.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
+#include "levenshtein.h"
 #include "help.h"
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
@@ -37,6 +38,16 @@ void add_cmdname(struct cmdnames *cmds, const char *name, int len)
        cmds->names[cmds->cnt++] = ent;
 }
 
+static void clean_cmdnames(struct cmdnames *cmds)
+{
+       int i;
+       for (i = 0; i < cmds->cnt; ++i)
+               free(cmds->names[i]);
+       free(cmds->names);
+       cmds->cnt = 0;
+       cmds->alloc = 0;
+}
+
 static int cmdname_compare(const void *a_, const void *b_)
 {
        struct cmdname *a = *(struct cmdname **)a_;
@@ -193,8 +204,8 @@ void load_command_list(const char *prefix,
                while (1) {
                        if ((colon = strchr(path, PATH_SEP)))
                                *colon = 0;
-
-                       list_commands_in_dir(other_cmds, path, prefix);
+                       if (!exec_path || strcmp(path, exec_path))
+                               list_commands_in_dir(other_cmds, path, prefix);
 
                        if (!colon)
                                break;
@@ -250,9 +261,101 @@ int is_in_cmdlist(struct cmdnames *c, const char *s)
        return 0;
 }
 
-void help_unknown_cmd(const char *cmd)
+static int autocorrect;
+static struct cmdnames aliases;
+
+static int git_unknown_cmd_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "help.autocorrect"))
+               autocorrect = git_config_int(var,value);
+       /* Also use aliases for command lookup */
+       if (!prefixcmp(var, "alias."))
+               add_cmdname(&aliases, var + 6, strlen(var + 6));
+
+       return git_default_config(var, value, cb);
+}
+
+static int levenshtein_compare(const void *p1, const void *p2)
+{
+       const struct cmdname *const *c1 = p1, *const *c2 = p2;
+       const char *s1 = (*c1)->name, *s2 = (*c2)->name;
+       int l1 = (*c1)->len;
+       int l2 = (*c2)->len;
+       return l1 != l2 ? l1 - l2 : strcmp(s1, s2);
+}
+
+static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old)
 {
+       int i;
+       ALLOC_GROW(cmds->names, cmds->cnt + old->cnt, cmds->alloc);
+
+       for (i = 0; i < old->cnt; i++)
+               cmds->names[cmds->cnt++] = old->names[i];
+       free(old->names);
+       old->cnt = 0;
+       old->names = NULL;
+}
+
+const char *help_unknown_cmd(const char *cmd)
+{
+       int i, n, best_similarity = 0;
+       struct cmdnames main_cmds, other_cmds;
+
+       memset(&main_cmds, 0, sizeof(main_cmds));
+       memset(&other_cmds, 0, sizeof(main_cmds));
+       memset(&aliases, 0, sizeof(aliases));
+
+       git_config(git_unknown_cmd_config, NULL);
+
+       load_command_list("git-", &main_cmds, &other_cmds);
+
+       add_cmd_list(&main_cmds, &aliases);
+       add_cmd_list(&main_cmds, &other_cmds);
+       qsort(main_cmds.names, main_cmds.cnt,
+             sizeof(main_cmds.names), cmdname_compare);
+       uniq(&main_cmds);
+
+       /* This reuses cmdname->len for similarity index */
+       for (i = 0; i < main_cmds.cnt; ++i)
+               main_cmds.names[i]->len =
+                       levenshtein(cmd, main_cmds.names[i]->name, 0, 2, 1, 4);
+
+       qsort(main_cmds.names, main_cmds.cnt,
+             sizeof(*main_cmds.names), levenshtein_compare);
+
+       if (!main_cmds.cnt)
+               die ("Uh oh. Your system reports no Git commands at all.");
+
+       best_similarity = main_cmds.names[0]->len;
+       n = 1;
+       while (n < main_cmds.cnt && best_similarity == main_cmds.names[n]->len)
+               ++n;
+       if (autocorrect && n == 1) {
+               const char *assumed = main_cmds.names[0]->name;
+               main_cmds.names[0] = NULL;
+               clean_cmdnames(&main_cmds);
+               fprintf(stderr, "WARNING: You called a Git program named '%s', "
+                       "which does not exist.\n"
+                       "Continuing under the assumption that you meant '%s'\n",
+                       cmd, assumed);
+               if (autocorrect > 0) {
+                       fprintf(stderr, "in %0.1f seconds automatically...\n",
+                               (float)autocorrect/10.0);
+                       poll(NULL, 0, autocorrect * 100);
+               }
+               return assumed;
+       }
+
        fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
+
+       if (best_similarity < 6) {
+               fprintf(stderr, "\nDid you mean %s?\n",
+                       n < 2 ? "this": "one of these");
+
+               for (i = 0; i < n; i++)
+                       fprintf(stderr, "\t%s\n", main_cmds.names[i]->name);
+       }
+
        exit(1);
 }
 
diff --git a/help.h b/help.h
index 2733433..56bc154 100644 (file)
--- a/help.h
+++ b/help.h
@@ -5,7 +5,7 @@ struct cmdnames {
        int alloc;
        int cnt;
        struct cmdname {
-               size_t len;
+               size_t len; /* also used for similarity index in help.c */
                char name[FLEX_ARRAY];
        } **names;
 };
index 6805288..c9dd9a1 100644 (file)
@@ -2237,7 +2237,7 @@ int main(int argc, char **argv)
        no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
 
        if (remote->url && remote->url[strlen(remote->url)-1] != '/') {
-               rewritten_url = malloc(strlen(remote->url)+2);
+               rewritten_url = xmalloc(strlen(remote->url)+2);
                strcpy(rewritten_url, remote->url);
                strcat(rewritten_url, "/");
                remote->url = rewritten_url;
diff --git a/http.c b/http.c
index 1108ab4..ed59b79 100644 (file)
--- a/http.c
+++ b/http.c
@@ -165,7 +165,16 @@ static CURL* get_curl_handle(void)
 {
        CURL* result = curl_easy_init();
 
-       curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, curl_ssl_verify);
+       if (!curl_ssl_verify) {
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
+       } else {
+               /* Verify authenticity of the peer's certificate */
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
+               /* The name in the cert must match whom we tried to connect */
+               curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
+       }
+
 #if LIBCURL_VERSION_NUM >= 0x070907
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
@@ -402,7 +411,7 @@ static struct fill_chain *fill_cfg = NULL;
 
 void add_fill_function(void *data, int (*fill)(void *))
 {
-       struct fill_chain *new = malloc(sizeof(*new));
+       struct fill_chain *new = xmalloc(sizeof(*new));
        struct fill_chain **linkp = &fill_cfg;
        new->data = data;
        new->fill = fill;
index a6e91fe..530d820 100644 (file)
@@ -172,7 +172,7 @@ static char *open_pack_file(char *pack_name)
                if (!pack_name) {
                        static char tmpfile[PATH_MAX];
                        snprintf(tmpfile, sizeof(tmpfile),
-                                "%s/tmp_pack_XXXXXX", get_object_directory());
+                                "%s/pack/tmp_pack_XXXXXX", get_object_directory());
                        output_fd = xmkstemp(tmpfile);
                        pack_name = xstrdup(tmpfile);
                } else
diff --git a/levenshtein.c b/levenshtein.c
new file mode 100644 (file)
index 0000000..db52f2c
--- /dev/null
@@ -0,0 +1,47 @@
+#include "cache.h"
+#include "levenshtein.h"
+
+int levenshtein(const char *string1, const char *string2,
+               int w, int s, int a, int d)
+{
+       int len1 = strlen(string1), len2 = strlen(string2);
+       int *row0 = xmalloc(sizeof(int) * (len2 + 1));
+       int *row1 = xmalloc(sizeof(int) * (len2 + 1));
+       int *row2 = xmalloc(sizeof(int) * (len2 + 1));
+       int i, j;
+
+       for (j = 0; j <= len2; j++)
+               row1[j] = j * a;
+       for (i = 0; i < len1; i++) {
+               int *dummy;
+
+               row2[0] = (i + 1) * d;
+               for (j = 0; j < len2; j++) {
+                       /* substitution */
+                       row2[j + 1] = row1[j] + s * (string1[i] != string2[j]);
+                       /* swap */
+                       if (i > 0 && j > 0 && string1[i - 1] == string2[j] &&
+                                       string1[i] == string2[j - 1] &&
+                                       row2[j + 1] > row0[j - 1] + w)
+                               row2[j + 1] = row0[j - 1] + w;
+                       /* deletion */
+                       if (j + 1 < len2 && row2[j + 1] > row1[j + 1] + d)
+                               row2[j + 1] = row1[j + 1] + d;
+                       /* insertion */
+                       if (row2[j + 1] > row2[j] + a)
+                               row2[j + 1] = row2[j] + a;
+               }
+
+               dummy = row0;
+               row0 = row1;
+               row1 = row2;
+               row2 = dummy;
+       }
+
+       i = row1[len2];
+       free(row0);
+       free(row1);
+       free(row2);
+
+       return i;
+}
diff --git a/levenshtein.h b/levenshtein.h
new file mode 100644 (file)
index 0000000..0173abe
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef LEVENSHTEIN_H
+#define LEVENSHTEIN_H
+
+int levenshtein(const char *string1, const char *string2,
+       int swap_penalty, int substition_penalty,
+       int insertion_penalty, int deletion_penalty);
+
+#endif
index 30cd5bb..2c1f3e6 100644 (file)
@@ -1,12 +1,48 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
+#include "tag.h"
 #include "graph.h"
 #include "log-tree.h"
 #include "reflog-walk.h"
+#include "refs.h"
 
 struct decoration name_decoration = { "object names" };
 
+static void add_name_decoration(const char *prefix, const char *name, struct object *obj)
+{
+       int plen = strlen(prefix);
+       int nlen = strlen(name);
+       struct name_decoration *res = xmalloc(sizeof(struct name_decoration) + plen + nlen);
+       memcpy(res->name, prefix, plen);
+       memcpy(res->name + plen, name, nlen + 1);
+       res->next = add_decoration(&name_decoration, obj, res);
+}
+
+static int add_ref_decoration(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct object *obj = parse_object(sha1);
+       if (!obj)
+               return 0;
+       add_name_decoration("", refname, obj);
+       while (obj->type == OBJ_TAG) {
+               obj = ((struct tag *)obj)->tagged;
+               if (!obj)
+                       break;
+               add_name_decoration("tag: ", refname, obj);
+       }
+       return 0;
+}
+
+void load_ref_decorations(void)
+{
+       static int loaded;
+       if (!loaded) {
+               loaded = 1;
+               for_each_ref(add_ref_decoration, NULL);
+       }
+}
+
 static void show_parents(struct commit *commit, int abbrev)
 {
        struct commit_list *p;
index 59ba4c4..3c8127b 100644 (file)
@@ -17,5 +17,6 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
                             const char **subject_p,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p);
+void load_ref_decorations(void);
 
 #endif
index 036bd66..d962ff1 100644 (file)
--- a/object.h
+++ b/object.h
@@ -41,7 +41,18 @@ extern int type_from_string(const char *str);
 extern unsigned int get_max_object_index(void);
 extern struct object *get_indexed_object(unsigned int);
 
-/** Internal only **/
+/*
+ * This can be used to see if we have heard of the object before, but
+ * it can return "yes we have, and here is a half-initialised object"
+ * for an object that we haven't loaded/parsed yet.
+ *
+ * When parsing a commit to create an in-core commit object, its
+ * parents list holds commit objects that represent its parents, but
+ * they are expected to be lazily initialized and do not know what
+ * their trees or parents are yet.  When this function returns such a
+ * half-initialised objects, the caller is expected to initialize them
+ * by calling parse_object() on them.
+ */
 struct object *lookup_object(const unsigned char *sha1);
 
 extern void *create_object(const unsigned char *sha1, int type, void *obj);
index 939ed56..3621f1d 100644 (file)
@@ -45,7 +45,7 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
        if (!index_name) {
                static char tmpfile[PATH_MAX];
                snprintf(tmpfile, sizeof(tmpfile),
-                        "%s/tmp_idx_XXXXXX", get_object_directory());
+                        "%s/pack/tmp_idx_XXXXXX", get_object_directory());
                fd = xmkstemp(tmpfile);
                index_name = xstrdup(tmpfile);
        } else {
index 102e6a4..6aab712 100644 (file)
@@ -58,7 +58,7 @@ require Exporter;
                 command_bidi_pipe command_close_bidi_pipe
                 version exec_path hash_object git_cmd_try
                 remote_refs
-                temp_acquire temp_release temp_reset);
+                temp_acquire temp_release temp_reset temp_path);
 
 
 =head1 DESCRIPTION
@@ -937,7 +937,7 @@ sub _close_cat_blob {
 
 { # %TEMP_* Lexical Context
 
-my (%TEMP_LOCKS, %TEMP_FILES);
+my (%TEMP_FILEMAP, %TEMP_FILES);
 
 =item temp_acquire ( NAME )
 
@@ -965,7 +965,7 @@ sub temp_acquire {
 
        my $temp_fd = _temp_cache($name);
 
-       $TEMP_LOCKS{$temp_fd} = 1;
+       $TEMP_FILES{$temp_fd}{locked} = 1;
        $temp_fd;
 }
 
@@ -991,16 +991,16 @@ the same string.
 sub temp_release {
        my ($self, $temp_fd, $trunc) = _maybe_self(@_);
 
-       if (ref($temp_fd) ne 'File::Temp') {
+       if (exists $TEMP_FILEMAP{$temp_fd}) {
                $temp_fd = $TEMP_FILES{$temp_fd};
        }
-       unless ($TEMP_LOCKS{$temp_fd}) {
+       unless ($TEMP_FILES{$temp_fd}{locked}) {
                carp "Attempt to release temp file '",
                        $temp_fd, "' that has not been locked";
        }
        temp_reset($temp_fd) if $trunc and $temp_fd->opened;
 
-       $TEMP_LOCKS{$temp_fd} = 0;
+       $TEMP_FILES{$temp_fd}{locked} = 0;
        undef;
 }
 
@@ -1009,9 +1009,9 @@ sub _temp_cache {
 
        _verify_require();
 
-       my $temp_fd = \$TEMP_FILES{$name};
+       my $temp_fd = \$TEMP_FILEMAP{$name};
        if (defined $$temp_fd and $$temp_fd->opened) {
-               if ($TEMP_LOCKS{$$temp_fd}) {
+               if ($TEMP_FILES{$$temp_fd}{locked}) {
                        throw Error::Simple("Temp file with moniker '",
                                $name, "' already in use");
                }
@@ -1021,12 +1021,13 @@ sub _temp_cache {
                        carp "Temp file '", $name,
                                "' was closed. Opening replacement.";
                }
-               $$temp_fd = File::Temp->new(
-                       TEMPLATE => 'Git_XXXXXX',
-                       DIR => File::Spec->tmpdir
+               my $fname;
+               ($$temp_fd, $fname) = File::Temp->tempfile(
+                       'Git_XXXXXX', UNLINK => 1
                        ) or throw Error::Simple("couldn't open new temp file");
                $$temp_fd->autoflush;
                binmode $$temp_fd;
+               $TEMP_FILES{$$temp_fd}{fname} = $fname;
        }
        $$temp_fd;
 }
@@ -1053,8 +1054,25 @@ sub temp_reset {
                or throw Error::Simple("expected file position to be reset");
 }
 
+=item temp_path ( NAME )
+
+=item temp_path ( FILEHANDLE )
+
+Returns the filename associated with the given tempfile.
+
+=cut
+
+sub temp_path {
+       my ($self, $temp_fd) = _maybe_self(@_);
+
+       if (exists $TEMP_FILEMAP{$temp_fd}) {
+               $temp_fd = $TEMP_FILEMAP{$temp_fd};
+       }
+       $TEMP_FILES{$temp_fd}{fname};
+}
+
 sub END {
-       unlink values %TEMP_FILES if %TEMP_FILES;
+       unlink values %TEMP_FILEMAP if %TEMP_FILEMAP;
 }
 
 } # %TEMP_* Lexical Context
index a29c290..8beafa0 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -5,6 +5,7 @@
 #include "revision.h"
 #include "string-list.h"
 #include "mailmap.h"
+#include "log-tree.h"
 
 static char *user_format;
 
@@ -481,6 +482,23 @@ static void parse_commit_header(struct format_commit_context *context)
        context->commit_header_parsed = 1;
 }
 
+static void format_decoration(struct strbuf *sb, const struct commit *commit)
+{
+       struct name_decoration *d;
+       const char *prefix = " (";
+
+       load_ref_decorations();
+       d = lookup_decoration(&name_decoration, &commit->object);
+       while (d) {
+               strbuf_addstr(sb, prefix);
+               prefix = ", ";
+               strbuf_addstr(sb, d->name);
+               d = d->next;
+       }
+       if (prefix[0] == ',')
+               strbuf_addch(sb, ')');
+}
+
 static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                void *context)
 {
@@ -573,6 +591,9 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
                                 ? '<'
                                 : '>');
                return 1;
+       case 'd':
+               format_decoration(sb, commit);
+               return 1;
        }
 
        /* For the rest we have to parse the commit header. */
index 5150c1e..5b1b3ad 100644 (file)
@@ -1253,6 +1253,7 @@ int discard_index(struct index_state *istate)
        istate->cache_nr = 0;
        istate->cache_changed = 0;
        istate->timestamp = 0;
+       istate->name_hash_initialized = 0;
        free_hash(&istate->name_hash);
        cache_tree_free(&(istate->cache_tree));
        free(istate->alloc);
diff --git a/refs.c b/refs.c
index 39a3b23..b680750 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -390,6 +390,18 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
        return retval;
 }
 
+/*
+ * If the "reading" argument is set, this function finds out what _object_
+ * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
+ * has to exist, and if it is symbolic, it has to point at an existing ref,
+ * because the "read" goes through the symref to the ref it points at.
+ *
+ * The access that is not "reading" may often be "writing", but does not
+ * have to; it can be merely checking _where it leads to_. If it is a
+ * prelude to "writing" to the ref, a write to a symref that points at
+ * yet-to-be-born ref will create the real ref pointed by the symref.
+ * reading=0 allows the caller to check where such a symref leads to.
+ */
 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
        int depth = MAXDEPTH;
@@ -409,13 +421,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                if (--depth < 0)
                        return NULL;
 
-               /* Special case: non-existing file.
-                * Not having the refs/heads/new-branch is OK
-                * if we are writing into it, so is .git/HEAD
-                * that points at refs/heads/master still to be
-                * born.  It is NOT OK if we are resolving for
-                * reading.
-                */
+               /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
                        struct ref_list *list = get_packed_refs();
                        while (list) {
index 3ef09a4..c45d96e 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -69,7 +69,7 @@ static const char *alias_url(const char *url)
        if (!longest)
                return url;
 
-       ret = malloc(rewrite[longest_i]->baselen +
+       ret = xmalloc(rewrite[longest_i]->baselen +
                     (strlen(url) - longest->len) + 1);
        strcpy(ret, rewrite[longest_i]->base);
        strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
@@ -152,7 +152,7 @@ static struct branch *make_branch(const char *name, int len)
                ret->name = xstrndup(name, len);
        else
                ret->name = xstrdup(name);
-       refname = malloc(strlen(name) + strlen("refs/heads/") + 1);
+       refname = xmalloc(strlen(name) + strlen("refs/heads/") + 1);
        strcpy(refname, "refs/heads/");
        strcpy(refname + strlen("refs/heads/"), ret->name);
        ret->refname = refname;
@@ -455,7 +455,7 @@ static int verify_refname(char *name, int is_glob)
  *          and dst pointers are always freeable pointers as well
  *          as the refspec pointer itself.
  */
-void free_refspecs(struct refspec *refspec, int nr_refspec)
+static void free_refspecs(struct refspec *refspec, int nr_refspec)
 {
        int i;
 
@@ -613,7 +613,7 @@ struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
        return parse_refspec_internal(nr_refspec, refspec, 1, 0);
 }
 
-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 {
        return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
@@ -783,7 +783,7 @@ struct ref *copy_ref_list(const struct ref *ref)
        return ret;
 }
 
-void free_ref(struct ref *ref)
+static void free_ref(struct ref *ref)
 {
        if (!ref)
                return;
index 2601f6e..c6163ff 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -77,8 +77,6 @@ void ref_remove_duplicates(struct ref *ref_map);
 
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
-struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
-void free_refspecs(struct refspec *refspec, int nr_refspec);
 
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
               int nr_refspec, const char **refspec, int all);
index 499f0e0..2f646de 100644 (file)
@@ -1793,26 +1793,6 @@ static struct commit *get_revision_internal(struct rev_info *revs)
                return c;
        }
 
-       if (revs->reverse) {
-               int limit = -1;
-
-               if (0 <= revs->max_count) {
-                       limit = revs->max_count;
-                       if (0 < revs->skip_count)
-                               limit += revs->skip_count;
-               }
-               l = NULL;
-               while ((c = get_revision_1(revs))) {
-                       commit_list_insert(c, &l);
-                       if ((0 < limit) && !--limit)
-                               break;
-               }
-               revs->commits = l;
-               revs->reverse = 0;
-               revs->max_count = -1;
-               c = NULL;
-       }
-
        /*
         * Now pick up what they want to give us
         */
@@ -1885,7 +1865,23 @@ static struct commit *get_revision_internal(struct rev_info *revs)
 
 struct commit *get_revision(struct rev_info *revs)
 {
-       struct commit *c = get_revision_internal(revs);
+       struct commit *c;
+       struct commit_list *reversed;
+
+       if (revs->reverse) {
+               reversed = NULL;
+               while ((c = get_revision_internal(revs))) {
+                       commit_list_insert(c, &reversed);
+               }
+               revs->commits = reversed;
+               revs->reverse = 0;
+               revs->reverse_output_stage = 1;
+       }
+
+       if (revs->reverse_output_stage)
+               return pop_commit(&revs->commits);
+
+       c = get_revision_internal(revs);
        if (c && revs->graph)
                graph_update(revs->graph, c);
        return c;
index fc23522..2fdb2dd 100644 (file)
@@ -54,6 +54,7 @@ struct rev_info {
                        rewrite_parents:1,
                        print_parents:1,
                        reverse:1,
+                       reverse_output_stage:1,
                        cherry_pick:1,
                        first_parent_only:1;
 
index 9ee1ed1..7d4f24d 100644 (file)
@@ -99,7 +99,11 @@ int safe_create_leading_directories(char *path)
                pos = strchr(pos, '/');
                if (!pos)
                        break;
-               *pos = 0;
+               while (*++pos == '/')
+                       ;
+               if (!*pos)
+                       break;
+               *--pos = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
                        if (!S_ISDIR(st.st_mode)) {
@@ -2136,7 +2140,9 @@ static void write_sha1_file_prepare(const void *buf, unsigned long len,
  */
 int move_temp_to_file(const char *tmpfile, const char *filename)
 {
-       int ret = link(tmpfile, filename);
+       int ret = 0;
+       if (link(tmpfile, filename))
+               ret = errno;
 
        /*
         * Coda hack - coda doesn't like cross-directory links,
index a841df2..67c431d 100644 (file)
@@ -1,8 +1,11 @@
 . ./test-lib.sh
 
+remotes_git_svn=remotes/git""-svn
+git_svn_id=git""-svn-id
+
 if test -n "$NO_SVN_TESTS"
 then
-       test_expect_success 'skipping git-svn tests, NO_SVN_TESTS defined' :
+       test_expect_success 'skipping git svn tests, NO_SVN_TESTS defined' :
        test_done
        exit
 fi
@@ -14,7 +17,7 @@ SVN_TREE=$GIT_SVN_DIR/svn-tree
 svn >/dev/null 2>&1
 if test $? -ne 1
 then
-    test_expect_success 'skipping git-svn tests, svn not found' :
+    test_expect_success 'skipping git svn tests, svn not found' :
     test_done
     exit
 fi
@@ -88,7 +91,7 @@ start_httpd () {
        mkdir "$GIT_DIR"/logs
 
        cat > "$GIT_DIR/httpd.conf" <<EOF
-ServerName "git-svn test"
+ServerName "git svn test"
 ServerRoot "$GIT_DIR"
 DocumentRoot "$GIT_DIR"
 PidFile "$GIT_DIR/httpd.pid"
@@ -135,3 +138,20 @@ close $wr or die $!;
 close $rd or die $!;
 EOF
 }
+
+require_svnserve () {
+    if test -z "$SVNSERVE_PORT"
+    then
+        say 'skipping svnserve test. (set $SVNSERVE_PORT to enable)'
+        test_done
+        exit
+    fi
+}
+
+start_svnserve () {
+    svnserve --listen-port $SVNSERVE_PORT \
+             --root "$rawsvnrepo" \
+             --listen-once \
+             --listen-host 127.0.0.1 &
+}
+
diff --git a/t/t0024-crlf-archive.sh b/t/t0024-crlf-archive.sh
new file mode 100644 (file)
index 0000000..e533039
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='respect crlf in git archive'
+
+. ./test-lib.sh
+UNZIP=${UNZIP:-unzip}
+
+test_expect_success setup '
+
+       git config core.autocrlf true
+
+       printf "CRLF line ending\r\nAnd another\r\n" > sample &&
+       git add sample &&
+
+       test_tick &&
+       git commit -m Initial
+
+'
+
+test_expect_success 'tar archive' '
+
+       git archive --format=tar HEAD |
+       ( mkdir untarred && cd untarred && "$TAR" -xf - )
+
+       test_cmp sample untarred/sample
+
+'
+
+"$UNZIP" -v >/dev/null 2>&1
+if [ $? -eq 127 ]; then
+       echo "Skipping ZIP test, because unzip was not found"
+       test_done
+       exit
+fi
+
+test_expect_success 'zip archive' '
+
+       git archive --format=zip HEAD >test.zip &&
+
+       ( mkdir unzipped && cd unzipped && unzip ../test.zip ) &&
+
+       test_cmp sample unzipped/sample
+
+'
+
+test_done
index 64567fb..11b82f4 100755 (executable)
@@ -741,4 +741,14 @@ test_expect_success 'symlinked configuration' '
 
 '
 
+test_expect_success 'check split_cmdline return' "
+       git config alias.split-cmdline-fix 'echo \"' &&
+       test_must_fail git split-cmdline-fix &&
+       echo foo > foo &&
+       git add foo &&
+       git commit -m 'initial commit' &&
+       git config branch.master.mergeoptions 'echo \"' &&
+       test_must_fail git merge master
+       "
+
 test_done
index 833d6cb..18bcd97 100755 (executable)
@@ -57,4 +57,10 @@ test_expect_success 'last regexp must not be negated' '
        test_must_fail git diff --no-index Beer.java Beer-correct.java
 '
 
+test_expect_success 'alternation in pattern' '
+       git config diff.java.funcname "^[       ]*\\(\\(public\\|static\\).*\\)$"
+       git diff --no-index Beer.java Beer-correct.java |
+       grep "^@@.*@@ public static void main("
+'
+
 test_done
index 7eae1f4..84a1fe3 100755 (executable)
@@ -178,4 +178,16 @@ test_expect_success 'trailing empty lines (2)' '
 
 '
 
+test_expect_success 'do not color trailing cr in context' '
+       git config --unset core.whitespace
+       rm -f .gitattributes &&
+       echo AAAQ | tr Q "\015" >G &&
+       git add G &&
+       echo BBBQ | tr Q "\015" >>G
+       git diff --color G | tr "\015" Q >output &&
+       grep "BBB.*${blue_grep}Q" output &&
+       grep "AAA.*\[mQ" output
+
+'
+
 test_done
index 3a0ef87..b335c6b 100755 (executable)
@@ -272,7 +272,8 @@ test_expect_success \
 
 test_expect_success \
     'make sure index-pack detects the SHA1 collision' \
-    'test_must_fail git index-pack -o bad.idx test-3.pack'
+    'test_must_fail git index-pack -o bad.idx test-3.pack 2>msg &&
+     grep "SHA1 COLLISION FOUND" msg'
 
 test_expect_success \
     'honor pack.packSizeLimit' \
index de26c20..9aae496 100755 (executable)
@@ -303,4 +303,24 @@ test_expect_success 'pushing nonexistent branch by mistake should not segv' '
 
 '
 
+test_expect_success 'auto tag following fetches minimum' '
+
+       cd "$D" &&
+       git clone .git follow &&
+       git checkout HEAD^0 &&
+       (
+               for i in 1 2 3 4 5 6 7
+               do
+                       echo $i >>file &&
+                       git commit -m $i -a &&
+                       git tag -a -m $i excess-$i || exit 1
+               done
+       ) &&
+       git checkout master &&
+       (
+               cd follow &&
+               git fetch
+       )
+'
+
 test_done
diff --git a/t/t6013-rev-list-reverse-parents.sh b/t/t6013-rev-list-reverse-parents.sh
new file mode 100755 (executable)
index 0000000..59fc2f0
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='--reverse combines with --parents'
+
+. ./test-lib.sh
+
+
+commit () {
+       test_tick &&
+       echo $1 > foo &&
+       git add foo &&
+       git commit -m "$1"
+}
+
+test_expect_success 'set up --reverse example' '
+       commit one &&
+       git tag root &&
+       commit two &&
+       git checkout -b side HEAD^ &&
+       commit three &&
+       git checkout master &&
+       git merge -s ours side &&
+       commit five
+       '
+
+test_expect_success '--reverse --parents --full-history combines correctly' '
+       git rev-list --parents --full-history master -- foo |
+               perl -e "print reverse <>" > expected &&
+       git rev-list --reverse --parents --full-history master -- foo \
+               > actual &&
+       test_cmp actual expected
+       '
+
+test_expect_success '--boundary does too' '
+       git rev-list --boundary --parents --full-history master ^root -- foo |
+               perl -e "print reverse <>" > expected &&
+       git rev-list --boundary --reverse --parents --full-history \
+               master ^root -- foo > actual &&
+       test_cmp actual expected
+       '
+
+test_done
index 42620e0..5e18d68 100755 (executable)
@@ -150,8 +150,8 @@ test_expect_success 'MERGE_ZEALOUS simplifies non-conflicts' '
 
 '
 
-sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit;/" < new6.txt > new8.txt
-sed -e 's/deerit./&\n\n\n\n/' -e "s/locavit,/locavit --/" < new7.txt > new9.txt
+sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit;/"< new6.txt | tr '%' '\012' > new8.txt
+sed -e 's/deerit./&%%%%/' -e "s/locavit,/locavit --/" < new7.txt | tr '%' '\012' > new9.txt
 
 test_expect_success 'ZEALOUS_ALNUM' '
 
index 36c9a69..85fa39c 100755 (executable)
@@ -350,6 +350,120 @@ test_expect_success 'bisect does not create a "bisect" branch' '
        git branch -D bisect
 '
 
+# This creates a "side" branch to test "siblings" cases.
+#
+# H1-H2-H3-H4-H5-H6-H7  <--other
+#            \
+#             S5-S6-S7  <--side
+#
+test_expect_success 'side branch creation' '
+       git bisect reset &&
+       git checkout -b side $HASH4 &&
+       add_line_into_file "5(side): first line on a side branch" hello2 &&
+       SIDE_HASH5=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "6(side): second line on a side branch" hello2 &&
+       SIDE_HASH6=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "7(side): third line on a side branch" hello2 &&
+       SIDE_HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'good merge base when good and bad are siblings' '
+       git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
+       grep "merge base must be tested" my_bisect_log.txt &&
+       grep $HASH4 my_bisect_log.txt &&
+       git bisect good > my_bisect_log.txt &&
+       test_must_fail grep "merge base must be tested" my_bisect_log.txt &&
+       grep $HASH6 my_bisect_log.txt &&
+       git bisect reset
+'
+test_expect_success 'skipped merge base when good and bad are siblings' '
+       git bisect start "$SIDE_HASH7" "$HASH7" > my_bisect_log.txt &&
+       grep "merge base must be tested" my_bisect_log.txt &&
+       grep $HASH4 my_bisect_log.txt &&
+       git bisect skip > my_bisect_log.txt 2>&1 &&
+       grep "Warning" my_bisect_log.txt &&
+       grep $SIDE_HASH6 my_bisect_log.txt &&
+       git bisect reset
+'
+
+test_expect_success 'bad merge base when good and bad are siblings' '
+       git bisect start "$HASH7" HEAD > my_bisect_log.txt &&
+       grep "merge base must be tested" my_bisect_log.txt &&
+       grep $HASH4 my_bisect_log.txt &&
+       test_must_fail git bisect bad > my_bisect_log.txt 2>&1 &&
+       grep "merge base $HASH4 is bad" my_bisect_log.txt &&
+       grep "fixed between $HASH4 and \[$SIDE_HASH7\]" my_bisect_log.txt &&
+       git bisect reset
+'
+
+# This creates a few more commits (A and B) to test "siblings" cases
+# when a good and a bad rev have many merge bases.
+#
+# We should have the following:
+#
+# H1-H2-H3-H4-H5-H6-H7
+#            \  \     \
+#             S5-A     \
+#              \        \
+#               S6-S7----B
+#
+# And there A and B have 2 merge bases (S5 and H5) that should be
+# reported by "git merge-base --all A B".
+#
+test_expect_success 'many merge bases creation' '
+       git checkout "$SIDE_HASH5" &&
+       git merge -m "merge HASH5 and SIDE_HASH5" "$HASH5" &&
+       A_HASH=$(git rev-parse --verify HEAD) &&
+       git checkout side &&
+       git merge -m "merge HASH7 and SIDE_HASH7" "$HASH7" &&
+       B_HASH=$(git rev-parse --verify HEAD) &&
+       git merge-base --all "$A_HASH" "$B_HASH" > merge_bases.txt &&
+       test $(wc -l < merge_bases.txt) = "2" &&
+       grep "$HASH5" merge_bases.txt &&
+       grep "$SIDE_HASH5" merge_bases.txt
+'
+
+test_expect_success 'good merge bases when good and bad are siblings' '
+       git bisect start "$B_HASH" "$A_HASH" > my_bisect_log.txt &&
+       grep "merge base must be tested" my_bisect_log.txt &&
+       git bisect good > my_bisect_log2.txt &&
+       grep "merge base must be tested" my_bisect_log2.txt &&
+       {
+               {
+                       grep "$SIDE_HASH5" my_bisect_log.txt &&
+                       grep "$HASH5" my_bisect_log2.txt
+               } || {
+                       grep "$SIDE_HASH5" my_bisect_log2.txt &&
+                       grep "$HASH5" my_bisect_log.txt
+               }
+       } &&
+       git bisect reset
+'
+
+check_trace() {
+       grep "$1" "$GIT_TRACE" | grep "\^$2" | grep "$3" >/dev/null
+}
+
+test_expect_success 'optimized merge base checks' '
+       GIT_TRACE="$(pwd)/trace.log" &&
+       export GIT_TRACE &&
+       git bisect start "$HASH7" "$SIDE_HASH7" > my_bisect_log.txt &&
+       grep "merge base must be tested" my_bisect_log.txt &&
+       grep "$HASH4" my_bisect_log.txt &&
+       check_trace "rev-list" "$HASH7" "$SIDE_HASH7" &&
+       git bisect good > my_bisect_log2.txt &&
+       test -f ".git/BISECT_ANCESTORS_OK" &&
+       test "$HASH6" = $(git rev-parse --verify HEAD) &&
+       : > "$GIT_TRACE" &&
+       git bisect bad > my_bisect_log3.txt &&
+       test_must_fail check_trace "rev-list" "$HASH6" "$SIDE_HASH7" &&
+       git bisect good "$A_HASH" > my_bisect_log4.txt &&
+       grep "merge base must be tested" my_bisect_log4.txt &&
+       test_must_fail test -f ".git/BISECT_ANCESTORS_OK" &&
+       check_trace "rev-list" "$HASH6" "$A_HASH" &&
+       unset GIT_TRACE
+'
+
 #
 #
 test_done
index 26995b3..8bfae44 100755 (executable)
@@ -262,6 +262,50 @@ for i in "--perl --shell" "-s --python" "--python --tcl" "--tcl --perl"; do
        "
 done
 
+cat >expected <<\EOF
+master
+testtag
+EOF
+
+test_expect_success 'Check short refname format' '
+       (git for-each-ref --format="%(refname:short)" refs/heads &&
+       git for-each-ref --format="%(refname:short)" refs/tags) >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'Check for invalid refname format' '
+       test_must_fail git for-each-ref --format="%(refname:INVALID)"
+'
+
+cat >expected <<\EOF
+heads/master
+master
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs' '
+       git checkout -b newtag &&
+       echo "Using $datestamp" > one &&
+       git add one &&
+       git commit -m "Branch" &&
+       setdate_and_increment &&
+       git tag -m "Tagging at $datestamp" master &&
+       git for-each-ref --format "%(refname:short)" refs/heads/master refs/tags/master >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<\EOF
+heads/ambiguous
+ambiguous
+EOF
+
+test_expect_success 'Check ambiguous head and tag refs II' '
+       git checkout master &&
+       git tag ambiguous testtag^0 &&
+       git branch ambiguous testtag^0 &&
+       git for-each-ref --format "%(refname:short)" refs/heads/ambiguous refs/tags/ambiguous >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'an unusual tag with an incomplete line' '
 
        git tag -m "bogo" bogo &&
index 543b1c2..62d73f9 100755 (executable)
@@ -369,4 +369,36 @@ test_expect_success \
     'checkout with --track, but without -b, fails with too short tracked name' '
     test_must_fail git checkout --track renamer'
 
+test_expect_success 'checkout an unmerged path should fail' '
+       rm -f .git/index &&
+       O=$(echo original | git hash-object -w --stdin) &&
+       A=$(echo ourside | git hash-object -w --stdin) &&
+       B=$(echo theirside | git hash-object -w --stdin) &&
+       (
+               echo "100644 $A 0       fild" &&
+               echo "100644 $O 1       file" &&
+               echo "100644 $A 2       file" &&
+               echo "100644 $B 3       file" &&
+               echo "100644 $A 0       filf"
+       ) | git update-index --index-info &&
+       echo "none of the above" >sample &&
+       cat sample >fild &&
+       cat sample >file &&
+       cat sample >filf &&
+       test_must_fail git checkout fild file filf &&
+       test_cmp sample fild &&
+       test_cmp sample filf &&
+       test_cmp sample file
+'
+
+test_expect_success 'failing checkout -b should not break working tree' '
+       git reset --hard master &&
+       git symbolic-ref HEAD refs/heads/master &&
+       test_must_fail git checkout -b renamer side^ &&
+       test $(git symbolic-ref HEAD) = refs/heads/master &&
+       git diff --exit-code &&
+       git diff --cached --exit-code
+
+'
+
 test_done
index 469bff8..63bfc6d 100755 (executable)
@@ -141,7 +141,7 @@ EOF
 
 test_expect_success \
     'validate git rev-list output.' \
-    'diff current expected'
+    'test_cmp expected current'
 
 test_expect_success 'partial commit that involves removal (1)' '
 
@@ -151,7 +151,7 @@ test_expect_success 'partial commit that involves removal (1)' '
        git commit -m "Partial: add elif" elif &&
        git diff-tree --name-status HEAD^ HEAD >current &&
        echo "A elif" >expected &&
-       diff expected current
+       test_cmp expected current
 
 '
 
@@ -160,7 +160,7 @@ test_expect_success 'partial commit that involves removal (2)' '
        git commit -m "Partial: remove file" file &&
        git diff-tree --name-status HEAD^ HEAD >current &&
        echo "D file" >expected &&
-       diff expected current
+       test_cmp expected current
 
 '
 
@@ -171,7 +171,7 @@ test_expect_success 'partial commit that involves removal (3)' '
        git commit -m "Partial: modify elif" elif &&
        git diff-tree --name-status HEAD^ HEAD >current &&
        echo "M elif" >expected &&
-       diff expected current
+       test_cmp expected current
 
 '
 
@@ -187,7 +187,7 @@ test_expect_success 'amend commit to fix author' '
                expected &&
        git commit --amend --author="$author" &&
        git cat-file -p HEAD > current &&
-       diff expected current
+       test_cmp expected current
 
 '
 
@@ -256,7 +256,7 @@ test_expect_success 'amend commit to fix author' '
                expected &&
        git commit --amend --author="$author" &&
        git cat-file -p HEAD > current &&
-       diff expected current
+       test_cmp expected current
 
 '
 
index b47b7b9..7e17eb4 100755 (executable)
@@ -60,4 +60,57 @@ test_expect_success 'merge c1 with c2, c3, c4, c5' '
        test -f c5.c
 '
 
+test_expect_success 'setup' '
+       for i in A B C D E
+       do
+               echo $i > $i.c &&
+               git add $i.c &&
+               git commit -m $i &&
+               git tag $i
+       done &&
+       git reset --hard A &&
+       for i in F G H I
+       do
+               echo $i > $i.c &&
+               git add $i.c &&
+               git commit -m $i &&
+               git tag $i
+       done
+'
+
+test_expect_success 'merge E and I' '
+       git reset --hard A &&
+       git merge E I
+'
+
+test_expect_success 'verify merge result' '
+       test $(git rev-parse HEAD^1) = $(git rev-parse E) &&
+       test $(git rev-parse HEAD^2) = $(git rev-parse I)
+'
+
+test_expect_success 'add conflicts' '
+       git reset --hard E &&
+       echo foo > file.c &&
+       git add file.c &&
+       git commit -m E2 &&
+       git tag E2 &&
+       git reset --hard I &&
+       echo bar >file.c &&
+       git add file.c &&
+       git commit -m I2 &&
+       git tag I2
+'
+
+test_expect_success 'merge E2 and I2, causing a conflict and resolve it' '
+       git reset --hard A &&
+       test_must_fail git merge E2 I2 &&
+       echo baz > file.c &&
+       git add file.c &&
+       git commit -m "resolve conflict"
+'
+
+test_expect_success 'verify merge result' '
+       test $(git rev-parse HEAD^1) = $(git rev-parse E2) &&
+       test $(git rev-parse HEAD^2) = $(git rev-parse I2)
+'
 test_done
index de9b6ed..52a451d 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='git-merge
+test_description='git merge
 
 Testing a custom strategy.'
 
index 843a501..9b238c3 100755 (executable)
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-test_description='git-svn basic tests'
+test_description='git svn basic tests'
 GIT_SVN_LC_ALL=${LC_ALL:-$LANG}
 
 case "$GIT_SVN_LC_ALL" in
@@ -17,10 +17,10 @@ esac
 
 . ./lib-git-svn.sh
 
-say 'define NO_SVN_TESTS to skip git-svn tests'
+say 'define NO_SVN_TESTS to skip git svn tests'
 
 test_expect_success \
-    'initialize git-svn' '
+    'initialize git svn' '
        mkdir import &&
        cd import &&
        echo foo > foo &&
@@ -31,26 +31,26 @@ test_expect_success \
        echo "zzz" > bar/zzz &&
        echo "#!/bin/sh" > exec.sh &&
        chmod +x exec.sh &&
-       svn import -m "import for git-svn" . "$svnrepo" >/dev/null &&
+       svn import -m "import for git svn" . "$svnrepo" >/dev/null &&
        cd .. &&
        rm -rf import &&
-       git-svn init "$svnrepo"'
+       git svn init "$svnrepo"'
 
 test_expect_success \
     'import an SVN revision into git' \
-    'git-svn fetch'
+    'git svn fetch'
 
 test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
 name='try a deep --rmdir with a commit'
 test_expect_success "$name" '
-       git checkout -f -b mybranch remotes/git-svn &&
+       git checkout -f -b mybranch ${remotes_git_svn} &&
        mv dir/a/b/c/d/e/file dir/file &&
        cp dir/file file &&
        git update-index --add --remove dir/a/b/c/d/e/file dir/file file &&
        git commit -m "$name" &&
-       git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch &&
+       git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch &&
        svn up "$SVN_TREE" &&
        test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
 
@@ -63,61 +63,61 @@ test_expect_success "$name" "
        git update-index --remove dir/file &&
        git update-index --add dir/file/file &&
        git commit -m '$name' &&
-       test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch" || true
+       test_must_fail git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch" || true
 
 
 name='detect node change from directory to file #1'
 test_expect_success "$name" '
        rm -rf dir "$GIT_DIR"/index &&
-       git checkout -f -b mybranch2 remotes/git-svn &&
+       git checkout -f -b mybranch2 ${remotes_git_svn} &&
        mv bar/zzz zzz &&
        rm -rf bar &&
        mv zzz bar &&
        git update-index --remove -- bar/zzz &&
        git update-index --add -- bar &&
        git commit -m "$name" &&
-       test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch2' || true
+       test_must_fail git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch2' || true
 
 
 name='detect node change from file to directory #2'
 test_expect_success "$name" '
        rm -f "$GIT_DIR"/index &&
-       git checkout -f -b mybranch3 remotes/git-svn &&
+       git checkout -f -b mybranch3 ${remotes_git_svn} &&
        rm bar/zzz &&
        git update-index --remove bar/zzz &&
        mkdir bar/zzz &&
        echo yyy > bar/zzz/yyy &&
        git update-index --add bar/zzz/yyy &&
        git commit -m "$name" &&
-       test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch3' || true
+       test_must_fail git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch3' || true
 
 
 name='detect node change from directory to file #2'
 test_expect_success "$name" '
        rm -f "$GIT_DIR"/index &&
-       git checkout -f -b mybranch4 remotes/git-svn &&
+       git checkout -f -b mybranch4 ${remotes_git_svn} &&
        rm -rf dir &&
        git update-index --remove -- dir/file &&
        touch dir &&
        echo asdf > dir &&
        git update-index --add -- dir &&
        git commit -m "$name" &&
-       test_must_fail git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch4' || true
+       test_must_fail git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch4' || true
 
 
 name='remove executable bit from a file'
 test_expect_success "$name" '
        rm -f "$GIT_DIR"/index &&
-       git checkout -f -b mybranch5 remotes/git-svn &&
+       git checkout -f -b mybranch5 ${remotes_git_svn} &&
        chmod -x exec.sh &&
        git update-index exec.sh &&
        git commit -m "$name" &&
-       git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch5 &&
+       git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch5 &&
        svn up "$SVN_TREE" &&
        test ! -x "$SVN_TREE"/exec.sh'
 
@@ -127,8 +127,8 @@ test_expect_success "$name" '
        chmod +x exec.sh &&
        git update-index exec.sh &&
        git commit -m "$name" &&
-       git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch5 &&
+       git svn set-tree --find-copies-harder --rmdir \
+               ${remotes_git_svn}..mybranch5 &&
        svn up "$SVN_TREE" &&
        test -x "$SVN_TREE"/exec.sh'
 
@@ -139,8 +139,8 @@ test_expect_success "$name" '
        ln -s bar/zzz exec.sh &&
        git update-index exec.sh &&
        git commit -m "$name" &&<