Merge branch 'pb/push'
authorJunio C Hamano <gitster@pobox.com>
Fri, 23 May 2008 23:06:07 +0000 (16:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 23 May 2008 23:06:07 +0000 (16:06 -0700)
* pb/push:
add special "matching refs" refspec

201 files changed:
Documentation/Makefile
Documentation/config.txt
Documentation/cvs-migration.txt
Documentation/git-add.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-cherry-pick.txt
Documentation/git-commit.txt
Documentation/git-cvsexportcommit.txt
Documentation/git-daemon.txt
Documentation/git-describe.txt
Documentation/git-filter-branch.txt
Documentation/git-fmt-merge-msg.txt
Documentation/git-format-patch.txt
Documentation/git-help.txt
Documentation/git-merge.txt
Documentation/git-prune.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-remote.txt
Documentation/git-repack.txt
Documentation/git-rev-parse.txt
Documentation/git-revert.txt
Documentation/git-show.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-web--browse.txt
Documentation/git.txt
Documentation/gitcli.txt
Documentation/githooks.txt [new file with mode: 0644]
Documentation/hooks.txt [deleted file]
Documentation/merge-config.txt [new file with mode: 0644]
Documentation/merge-options.txt
Documentation/repository-layout.txt
Documentation/rev-list-options.txt
Documentation/technical/api-history-graph.txt [new file with mode: 0644]
Documentation/user-manual.txt
GIT-VERSION-GEN
INSTALL
Makefile
branch.c
builtin-add.c
builtin-apply.c
builtin-branch.c
builtin-checkout.c
builtin-commit.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-gc.c
builtin-init-db.c
builtin-log.c
builtin-merge-recursive.c
builtin-mv.c
builtin-pack-objects.c
builtin-push.c
builtin-read-tree.c
builtin-remote.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
cache.h
compat/fopen.c
config.c
contrib/completion/git-completion.bash
diff-lib.c
diff.c
diff.h
diffcore-rename.c
dir.c
environment.c
fast-import.c
git-am.sh
git-bisect.sh
git-clone.sh
git-compat-util.h
git-cvsexportcommit.perl
git-filter-branch.sh
git-gui/git-gui.sh
git-gui/lib/branch_create.tcl
git-gui/lib/branch_delete.tcl
git-gui/lib/checkout_op.tcl
git-gui/lib/database.tcl
git-gui/lib/spellcheck.tcl
git-gui/po/de.po
git-merge.sh
git-mergetool.sh
git-pull.sh
git-rebase.sh
git-repack.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
gitk-git/Makefile
gitk-git/gitk
gitk-git/gitk-git/po/es.po [new file with mode: 0644]
gitk-git/po/de.po
gitk-git/po/sv.po [new file with mode: 0644]
gitweb/gitweb.css
gitweb/gitweb.perl
graph.c [new file with mode: 0644]
graph.h [new file with mode: 0644]
help.c
http-push.c
http-walker.c
http.c
http.h
ident.c
log-tree.c
name-hash.c [new file with mode: 0644]
read-cache.c
refs.c
remote.c
remote.h
revision.c
revision.h
setup.c
sha1_file.c
symlinks.c
t/.gitignore
t/Makefile
t/README
t/lib-git-svn.sh
t/lib-httpd.sh
t/t0000-basic.sh
t/t0002-gitfile.sh [new file with mode: 0755]
t/t0050-filesystem.sh
t/t1020-subdirectory.sh
t/t1303-wacky-config.sh
t/t1500-rev-parse.sh
t/t1501-worktree.sh
t/t1503-rev-parse-verify.sh [new file with mode: 0755]
t/t3050-subprojects-fetch.sh
t/t3200-branch.sh
t/t3201-branch-contains.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3407-rebase-abort.sh
t/t3500-cherry.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t4027-diff-submodule.sh
t/t5000-tar-tree.sh
t/t5302-pack-index.sh
t/t5500-fetch-pack.sh
t/t5512-ls-remote.sh
t/t5516-fetch-push.sh
t/t5517-push-mirror.sh
t/t5520-pull.sh
t/t5700-clone-reference.sh
t/t5710-info-alternate.sh
t/t6000lib.sh
t/t6010-merge-base.sh
t/t6030-bisect-porcelain.sh
t/t6032-merge-large-rename.sh [new file with mode: 0755]
t/t6200-fmt-merge-msg.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7010-setup.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7501-commit.sh
t/t7502-commit.sh
t/t7504-commit-msg-hook.sh
t/t7505-prepare-commit-msg-hook.sh
t/t7506-status-submodule.sh [new file with mode: 0755]
t/t7600-merge.sh
t/t7701-repack-unpack-unreachable.sh [new file with mode: 0755]
t/t9001-send-email.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/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/t9120-git-svn-clone-with-percent-escapes.sh
t/t9121-git-svn-fetch-renamed-dir.sh
t/t9400-git-cvsserver-server.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9600-cvsimport.sh
t/test-lib.sh
templates/hooks--prepare-commit-msg
transport.c
unpack-trees.c
unpack-trees.h
walker.c
walker.h
wt-status.c
wt-status.h
index 43781fb2486eefb09d9249150c15e286a3668b55..4144d1e086d5e8443da41a0939e5bead4efcafba 100644 (file)
@@ -2,8 +2,8 @@ MAN1_TXT= \
        $(filter-out $(addsuffix .txt, $(ARTICLES) $(SP_ARTICLES)), \
                $(wildcard git-*.txt)) \
        gitk.txt
-MAN5_TXT=gitattributes.txt gitignore.txt gitcli.txt gitmodules.txt
-MAN7_TXT=git.txt
+MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt
+MAN7_TXT=git.txt gitcli.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
 MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@ -18,7 +18,6 @@ ARTICLES += cvs-migration
 ARTICLES += diffcore
 ARTICLES += howto-index
 ARTICLES += repository-layout
-ARTICLES += hooks
 ARTICLES += everyday
 ARTICLES += git-tools
 ARTICLES += glossary
index 824e416e9f8157d54d3f37e0e520fff5f9a125c0..002a06689343df9201d8c08beff8147c8a55e8b7 100644 (file)
@@ -399,6 +399,21 @@ branch.autosetupmerge::
        done when the starting point is either a local branch or remote
        branch. This option defaults to true.
 
+branch.autosetuprebase::
+       When a new branch is created with `git-branch` or `git-checkout`
+       that tracks another branch, this variable tells git to set
+       up pull to rebase instead of merge (see "branch.<name>.rebase").
+       When `never`, rebase is never automatically set to true.
+       When `local`, rebase is set to true for tracked branches of
+       other local branches.
+       When `remote`, rebase is set to true for tracked branches of
+       remote branches.
+       When `always`, rebase will be set to true for all tracking
+       branches.
+       See "branch.autosetupmerge" for details on how to set up a
+       branch to track another branch.
+       This option defaults to never.
+
 branch.<name>.remote::
        When in branch <name>, it tells `git fetch` which remote to fetch.
        If this option is not given, `git fetch` defaults to remote "origin".
@@ -426,7 +441,8 @@ branch.<name>.mergeoptions::
 
 branch.<name>.rebase::
        When true, rebase the branch <name> on top of the fetched branch,
-       instead of merging the default branch from the default remote.
+       instead of merging the default branch from the default remote when
+       "git pull" is run.
        *NOTE*: this is a possibly dangerous operation; do *not* use
        it unless you understand the implications (see linkgit:git-rebase[1]
        for details).
@@ -507,8 +523,10 @@ color.status.<slot>::
        one of `header` (the header text of the status message),
        `added` or `updated` (files which are added but not committed),
        `changed` (files which are changed but not added in the index),
-       or `untracked` (files which are not tracked by git). The values of
-       these variables may be specified as in color.branch.<slot>.
+       `untracked` (files which are not tracked by git), or
+       `nobranch` (the color the 'no branch' warning is shown in, defaulting
+       to red). The values of these variables may be specified as in
+       color.branch.<slot>.
 
 commit.template::
        Specify a file to use as the template for new commit messages.
@@ -684,6 +702,36 @@ specified as 'gitcvs.<access_method>.<varname>' (where 'access_method'
 is one of "ext" and "pserver") to make them apply only for the given
 access method.
 
+gui.commitmsgwidth::
+       Defines how wide the commit message window is in the
+       linkgit:git-gui[1]. "75" is the default.
+
+gui.diffcontext::
+       Specifies how many context lines should be used in calls to diff
+       made by the linkgit:git-gui[1]. The default is "5".
+
+gui.matchtrackingbranch::
+       Determines if new branches created with linkgit:git-gui[1] should
+       default to tracking remote branches with matching names or
+       not. Default: "false".
+
+gui.newbranchtemplate::
+       Is used as suggested name when creating new branches using the
+       linkgit:git-gui[1].
+
+gui.pruneduringfetch::
+       "true" if linkgit:git-gui[1] should prune tracking branches when
+       performing a fetch. The default value is "false".
+
+gui.trustmtime::
+       Determines if linkgit:git-gui[1] should trust the file modification
+       timestamp or not. By default the timestamps are not trusted.
+
+gui.spellingdictionary::
+       Specifies the dictionary used for spell checking commit messages in
+       the linkgit:git-gui[1]. When set to "none" spell checking is turned
+       off.
+
 help.browser::
        Specify the browser that will be used to display help in the
        'web' format. See linkgit:git-help[1].
@@ -769,6 +817,12 @@ instaweb.port::
        The port number to bind the gitweb httpd to. See
        linkgit:git-instaweb[1].
 
+log.date::
+       Set default date-time mode for the log command. Setting log.date
+       value is similar to using git log's --date option. The value is one of
+       following alternatives: {relative,local,default,iso,rfc,short}.
+       See linkgit:git-log[1].
+
 log.showroot::
        If true, the initial commit will be shown as a big creation event.
        This is equivalent to a diff against an empty tree.
@@ -779,37 +833,16 @@ man.viewer::
        Specify the programs that may be used to display help in the
        'man' format. See linkgit:git-help[1].
 
-merge.summary::
-       Whether to include summaries of merged commits in newly created
-       merge commit messages. False by default.
-
-merge.tool::
-       Controls which merge resolution program is used by
-       linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
-       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
-       "opendiff".  Any other value is treated is custom merge tool
-       and there must be a corresponing mergetool.<tool>.cmd option.
-
-merge.verbosity::
-       Controls the amount of output shown by the recursive merge
-       strategy.  Level 0 outputs nothing except a final error
-       message if conflicts were detected. Level 1 outputs only
-       conflicts, 2 outputs conflicts and file changes.  Level 5 and
-       above outputs debugging information.  The default is level 2.
-       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
-
-merge.<driver>.name::
-       Defines a human readable name for a custom low-level
-       merge driver.  See linkgit:gitattributes[5] for details.
-
-merge.<driver>.driver::
-       Defines the command that implements a custom low-level
-       merge driver.  See linkgit:gitattributes[5] for details.
-
-merge.<driver>.recursive::
-       Names a low-level merge driver to be used when
-       performing an internal merge between common ancestors.
-       See linkgit:gitattributes[5] for details.
+include::merge-config.txt[]
+
+man.<tool>.cmd::
+       Specify the command to invoke the specified man viewer. The
+       specified command is evaluated in shell with the man page
+       passed as argument. (See linkgit:git-help[1].)
+
+man.<tool>.path::
+       Override the path for the given tool that may be used to
+       display help in the 'man' format. See linkgit:git-help[1].
 
 mergetool.<tool>.path::
        Override the path for the given tool.  This is useful in case
@@ -921,6 +954,10 @@ remote.<name>.push::
        The default set of "refspec" for linkgit:git-push[1]. See
        linkgit:git-push[1].
 
+remote.<name>.mirror::
+       If true, pushing to this remote will automatically behave
+       as if the `\--mirror` option was given on the command line.
+
 remote.<name>.skipDefaultUpdate::
        If true, this remote will be skipped by default when updating
        using the update subcommand of linkgit:git-remote[1].
index 00f2e36b2ec162c6ef6d9c731ff549643ecb3ce7..374bc87b105dfb04003f1aa3c0b1e2a8ea7ac805 100644 (file)
@@ -137,7 +137,7 @@ Advanced Shared Repository Management
 
 Git allows you to specify scripts called "hooks" to be run at certain
 points.  You can use these, for example, to send all commits to the shared
-repository to a mailing list.  See link:hooks.html[Hooks used by git].
+repository to a mailing list.  See linkgit:githooks[5][Hooks used by git].
 
 You can enforce finer grained permissions using update hooks.  See
 link:howto/update-hook-example.txt[Controlling access to branches using
index 35e67a06e4248182c05c98b4b9b421bb3e02940a..bb4abe26bb5d12916e8f396e3b94573f42e37a29 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git-add' [-n] [-v] [-f] [--interactive | -i] [--patch | -p] [-u] [--refresh]
-          [--] <filepattern>...
+         [--ignore-errors] [--] <filepattern>...
 
 DESCRIPTION
 -----------
@@ -71,7 +71,9 @@ OPTIONS
        the specified filepatterns before exiting.
 
 -u::
-       Update only files that git already knows about. This is similar
+       Update only files that git already knows about, staging modified
+       content for commit and marking deleted files for removal. This
+       is similar
        to what "git commit -a" does in preparation for making a commit,
        except that the update is limited to paths specified on the
        command line. If no paths are specified, all tracked files in the
@@ -81,6 +83,11 @@ OPTIONS
        Don't add the file(s), but only refresh their stat()
        information in the index.
 
+\--ignore-errors::
+       If some files could not be added because of errors indexing
+       them, do not abort the operation, but continue adding the
+       others. The command shall still exit with non-zero status.
+
 \--::
        This option can be used to separate command-line options from
        the list of files, (useful when filenames might be mistaken
@@ -98,21 +105,27 @@ those in info/exclude.  See link:repository-layout.html[repository layout].
 
 EXAMPLES
 --------
-git-add Documentation/\\*.txt::
 
-       Adds content from all `\*.txt` files under `Documentation`
-       directory and its subdirectories.
+* Adds content from all `\*.txt` files under `Documentation` directory
+and its subdirectories:
++
+------------
+$ git add Documentation/\\*.txt
+------------
 +
 Note that the asterisk `\*` is quoted from the shell in this
 example; this lets the command to include the files from
 subdirectories of `Documentation/` directory.
 
-git-add git-*.sh::
-
-       Considers adding content from all git-*.sh scripts.
-       Because this example lets shell expand the asterisk
-       (i.e. you are listing the files explicitly), it does not
-       consider `subdir/git-foo.sh`.
+* Considers adding content from all git-*.sh scripts:
++
+------------
+$ git add git-*.sh
+------------
++
+Because this example lets shell expand the asterisk (i.e. you are
+listing the files explicitly), it does not consider
+`subdir/git-foo.sh`.
 
 Interactive mode
 ----------------
index 698ffde7ce844e38abe2719b2f8043783742f134..0b8b0ebba758871ac9c79e729664b5057128e9ac 100644 (file)
@@ -85,7 +85,7 @@ Oh, and then after you want to reset to the original head, do a
 $ git bisect reset
 ------------------------------------------------
 
-to get back to the master branch, instead of being in one of the
+to get back to the original branch, instead of being in one of the
 bisection branches ("git bisect start" will do that for you too,
 actually: it will reset the bisection state, and before it does that
 it checks that you're not using some old bisection branch).
@@ -224,6 +224,55 @@ tree to the pristine state.  Finally the "run" script can exit with
 the status of the real test to let "git bisect run" command loop to
 know the outcome.
 
+EXAMPLES
+--------
+
+* Automatically bisect a broken build between v1.2 and HEAD:
++
+------------
+$ git bisect start HEAD v1.2 --      # HEAD is bad, v1.2 is good
+$ git bisect run make                # "make" builds the app
+------------
+
+* Automatically bisect a broken test suite:
++
+------------
+$ cat ~/test.sh
+#!/bin/sh
+make || exit 125                   # this "skip"s broken builds
+make test                          # "make test" runs the test suite
+$ git bisect start v1.3 v1.1 --    # v1.3 is bad, v1.1 is good
+$ git bisect run ~/test.sh
+------------
++
+Here we use a "test.sh" custom script. In this script, if "make"
+fails, we "skip" the current commit.
++
+It's safer to use a custom script outside the repo to prevent
+interactions between the bisect, make and test processes and the
+script.
++
+And "make test" should "exit 0", if the test suite passes, and
+"exit 1" (for example) otherwise.
+
+* Automatically bisect a broken test case:
++
+------------
+$ cat ~/test.sh
+#!/bin/sh
+make || exit 125                     # this "skip"s broken builds
+~/check_test_case.sh                 # does the test case passes ?
+$ git bisect start HEAD HEAD~10 --   # culprit is among the last 10
+$ git bisect run ~/test.sh
+------------
++
+Here "check_test_case.sh" should "exit 0", if the test case passes,
+and "exit 1" (for example) otherwise.
++
+It's safer if both "test.sh" and "check_test_case.sh" scripts are
+outside the repo to prevent interactions between the bisect, make and
+test processes and the scripts.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 6f07a17a2c310a67f23034c520ab10670407c006..c824d887420754f98db0553a006865d31d01cf1d 100644 (file)
@@ -8,7 +8,7 @@ git-branch - List, create, or delete branches
 SYNOPSIS
 --------
 [verse]
-'git-branch' [--color | --no-color] [-r | -a]
+'git-branch' [--color | --no-color] [-r | -a] [--merged | --no-merged]
           [-v [--abbrev=<length> | --no-abbrev]]
           [--contains <commit>]
 'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
@@ -24,6 +24,8 @@ and option `-a` shows both.
 With `--contains <commit>`, shows only the branches that
 contains the named commit (in other words, the branches whose
 tip commits are descendant of the named commit).
+With `--merged`, only branches merged into HEAD will be listed, and
+with `--no-merged` only branches not merged into HEAD will be listed.
 
 In its second form, a new branch named <branchname> will be created.
 It will start out with a head equal to the one given as <start-point>.
@@ -118,6 +120,15 @@ OPTIONS
 --no-track::
        Ignore the branch.autosetupmerge configuration variable.
 
+--contains <commit>::
+       Only list branches which contain the specified commit.
+
+--merged::
+       Only list branches which are fully contained by HEAD.
+
+--no-merged::
+       Do not list branches which are fully contained by HEAD.
+
 <branchname>::
        The name of the branch to create or delete.
        The new branch name must pass all checks defined by
@@ -175,6 +186,18 @@ If you are creating a branch that you want to immediately checkout, it's
 easier to use the git checkout command with its `-b` option to create
 a branch and check it out with a single command.
 
+The options `--contains`, `--merged` and `--no-merged` serves three related
+but different purposes:
+
+- `--contains <commit>` is used to find all branches which will need
+  special attention if <commit> were to be rebased or amended, since those
+  branches contain the specified <commit>.
+
+- `--merged` is used to find all branches which can be safely deleted,
+  since those branches are fully contained by HEAD.
+
+- `--no-merged` is used to find branches which are candidates for merging
+  into HEAD, since those branches are not fully contained by HEAD.
 
 Author
 ------
index f0beb412e655f0e188b8a1427b41f72b6cc61239..ca048f46f664a52eb5b0174ff4177eba5b20e702 100644 (file)
@@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit
 
 SYNOPSIS
 --------
-'git-cherry-pick' [--edit] [-n] [-m parent-number] [-x] <commit>
+'git-cherry-pick' [--edit] [-n] [-m parent-number] [-s] [-x] <commit>
 
 DESCRIPTION
 -----------
@@ -64,6 +64,9 @@ OPTIONS
 This is useful when cherry-picking more than one commits'
 effect to your working tree in a row.
 
+-s|--signoff::
+       Add Signed-off-by line at the end of the commit message.
+
 
 Author
 ------
index 4bb51cc06ec5e1f47b748b14353e49a0acfdc775..c3c9f5b9021e60f815d41a0454b0238865251175 100644 (file)
@@ -87,7 +87,7 @@ OPTIONS
 
 --no-verify::
        This option bypasses the pre-commit and commit-msg hooks.
-       See also link:hooks.html[hooks].
+       See also linkgit:githooks[5][hooks].
 
 --allow-empty::
        Usually recording a commit that has the exact same tree as its
@@ -292,7 +292,7 @@ order).
 HOOKS
 -----
 This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
-and `post-commit` hooks.  See link:hooks.html[hooks] for more
+and `post-commit` hooks.  See linkgit:githooks[5][hooks] for more
 information.
 
 
index 9a47b4c397cec8e6782962bfe6766eee48b78ce1..363c36d694231c5876527ff0d062fbd385d66630 100644 (file)
@@ -65,11 +65,17 @@ OPTIONS
 -w::
        Specify the location of the CVS checkout to use for the export. This
        option does not require GIT_DIR to be set before execution if the
-       current directory is within a git repository.
+       current directory is within a git repository.  The default is the
+       value of 'cvsexportcommit.cvsdir'.
 
 -v::
        Verbose.
 
+CONFIGURATION
+-------------
+cvsexportcommit.cvsdir::
+       The default location of the CVS checkout to use for the export.
+
 EXAMPLES
 --------
 
index fd83bc7833312b20a7ef045a9dfd128fc2539838..cf261dd40d98a95d8eda4ee1008451ca67029bd3 100644 (file)
@@ -174,7 +174,7 @@ upload-pack::
 upload-archive::
        This serves `git-archive --remote`.  It is disabled by
        default, but a repository can enable it by setting
-       `daemon.uploadarchive` configuration item to `true`.
+       `daemon.uploadarch` configuration item to `true`.
 
 receive-pack::
        This serves `git-send-pack` clients, allowing anonymous
@@ -257,7 +257,7 @@ selectively enable/disable services per repository::
 ----------------------------------------------------------------
        [daemon]
                uploadpack = false
-               uploadarchive = true
+               uploadarch = true
 ----------------------------------------------------------------
 
 
index d9aa2f29809eb07385468edd2587daf55339389a..69e1ab750d99defc98f671317ae6ae65debd92b0 100644 (file)
@@ -13,9 +13,10 @@ SYNOPSIS
 DESCRIPTION
 -----------
 The command finds the most recent tag that is reachable from a
-commit, and if the commit itself is pointed at by the tag, shows
-the tag.  Otherwise, it suffixes the tag name with the number of
-additional commits and the abbreviated object name of the commit.
+commit.  If the tag points to the commit, then only the tag is
+shown.  Otherwise, it suffixes the tag name with the number of
+additional commits on top of the tagged object and the
+abbreviated object name of the most recent commit.
 
 
 OPTIONS
index 6454e49bf2805fcf0a000ed6f96ff9f73d718299..506c37af7021e2e16caab5ffe72bca9bc2f28927 100644 (file)
@@ -133,10 +133,16 @@ use "--tag-name-filter cat" to simply update the tags.  In this
 case, be very careful and make sure you have the old tags
 backed up in case the conversion has run afoul.
 +
-Note that there is currently no support for proper rewriting of
-tag objects; in layman terms, if the tag has a message or signature
-attached, the rewritten tag won't have it.  Sorry.  (It is by
-definition impossible to preserve signatures at any rate.)
+Nearly proper rewriting of tag objects is supported. If the tag has
+a message attached, a new tag object will be created with the same message,
+author, and timestamp. If the tag has a signature attached, the
+signature will be stripped. It is by definition impossible to preserve
+signatures. The reason this is "nearly" proper, is because ideally if
+the tag did not change (points to the same object, has the same name, etc.)
+it should retain any signature. That is not the case, signatures will always
+be removed, buyer beware. There is also no support for changing the
+author or timestamp (or the tag message for that matter). Tags which point
+to other tags will be rewritten to point to the underlying commit.
 
 --subdirectory-filter <directory>::
        Only look at the history which touches the given subdirectory.
@@ -177,6 +183,10 @@ or copyright violation) from all commits:
 git filter-branch --tree-filter 'rm filename' HEAD
 -------------------------------------------------------
 
+However, if the file is absent from the tree of some commit,
+a simple `rm filename` will fail for that tree and commit.
+Thus you may instead want to use `rm -f filename` as the script.
+
 A significantly faster version:
 
 --------------------------------------------------------------------------
index 8615ae353e91fa1de0d17c7c3f8ea777ac62e209..457cf425619dc1680fae510aceb20574b02be602 100644 (file)
@@ -9,8 +9,8 @@ git-fmt-merge-msg - Produce a merge commit message
 SYNOPSIS
 --------
 [verse]
-git-fmt-merge-msg [--summary | --no-summary] <$GIT_DIR/FETCH_HEAD
-git-fmt-merge-msg [--summary | --no-summary] -F <file>
+git-fmt-merge-msg [--log | --no-log] <$GIT_DIR/FETCH_HEAD
+git-fmt-merge-msg [--log | --no-log] -F <file>
 
 DESCRIPTION
 -----------
@@ -24,15 +24,19 @@ automatically invoking `git-merge`.
 OPTIONS
 -------
 
---summary::
+--log::
        In addition to branch names, populate the log message with
        one-line descriptions from the actual commits that are being
        merged.
 
---no-summary::
+--no-log::
        Do not list one-line descriptions from the actual commits being
        merged.
 
+--summary,--no-summary::
+       Synonyms to --log and --no-log; these are deprecated and will be
+       removed in the future.
+
 --file <file>, -F <file>::
        Take the list of merged objects from <file> instead of
        stdin.
@@ -40,10 +44,14 @@ OPTIONS
 CONFIGURATION
 -------------
 
-merge.summary::
+merge.log::
        Whether to include summaries of merged commits in newly
        merge commit messages. False by default.
 
+merge.summary::
+       Synonym to `merge.log`; this is deprecated and will be removed in
+       the future.
+
 SEE ALSO
 --------
 linkgit:git-merge[1]
index b5207b76040d02e34452175779340658b943a84e..c60ce123ec745e1903da0b5ab50e4efabeeb5dc3 100644 (file)
@@ -156,6 +156,12 @@ want a filename like `0001-description-of-my-change.patch`, and
 the first letter does not have to be a dot.  Leaving it empty would
 not add any suffix.
 
+--no-binary::
+       Don't output contents of changes in binary files, just take note
+       that they differ.  Note that this disable the patch to be properly
+       applied.  By default the contents of changes in those files are
+       encoded in the patch.
+
 CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message
@@ -168,38 +174,54 @@ and file suffix, and number patches when outputting more than one.
         subjectprefix = CHANGE
         suffix = .txt
         numbered = auto
+       cc = <email>
 ------------
 
 
 EXAMPLES
 --------
 
-git-format-patch -k --stdout R1..R2 | git-am -3 -k::
-       Extract commits between revisions R1 and R2, and apply
-       them on top of the current branch using `git-am` to
-       cherry-pick them.
-
-git-format-patch origin::
-       Extract all commits which are in the current branch but
-       not in the origin branch.  For each commit a separate file
-       is created in the current directory.
-
-git-format-patch \--root origin::
-       Extract all commits that lead to 'origin' since the
-       inception of the project.
-
-git-format-patch -M -B origin::
-       The same as the previous one.  Additionally, it detects
-       and handles renames and complete rewrites intelligently to
-       produce a renaming patch.  A renaming patch reduces the
-       amount of text output, and generally makes it easier to
-       review it.  Note that the "patch" program does not
-       understand renaming patches, so use it only when you know
-       the recipient uses git to apply your patch.
-
-git-format-patch -3::
-       Extract three topmost commits from the current branch
-       and format them as e-mailable patches.
+* Extract commits between revisions R1 and R2, and apply them on top of
+the current branch using `git-am` to cherry-pick them:
++
+------------
+$ git format-patch -k --stdout R1..R2 | git-am -3 -k
+------------
+
+* Extract all commits which are in the current branch but not in the
+origin branch:
++
+------------
+$ git format-patch origin
+------------
++
+For each commit a separate file is created in the current directory.
+
+* Extract all commits that lead to 'origin' since the inception of the
+project:
++
+------------
+$ git format-patch \--root origin
+------------
+
+* The same as the previous one:
++
+------------
+$ git format-patch -M -B origin
+------------
++
+Additionally, it detects and handles renames and complete rewrites
+intelligently to produce a renaming patch.  A renaming patch reduces
+the amount of text output, and generally makes it easier to review it.
+Note that the "patch" program does not understand renaming patches, so
+use it only when you know the recipient uses git to apply your patch.
+
+* Extract three topmost commits from the current branch and format them
+as e-mailable patches:
++
+------------
+$ git format-patch -3
+------------
 
 See Also
 --------
index be2ae53b90610d6c89fdf281634d06609562101b..bfbba9e235495b0a7e31b4d3ed558bef105ad02d 100644 (file)
@@ -82,28 +82,75 @@ man.viewer
 ~~~~~~~~~~
 
 The 'man.viewer' config variable will be checked if the 'man' format
-is chosen. Only the following values are currently supported:
+is chosen. The following values are currently supported:
 
 * "man": use the 'man' program as usual,
 * "woman": use 'emacsclient' to launch the "woman" mode in emacs
 (this only works starting with emacsclient versions 22),
-* "konqueror": use a man KIO slave in konqueror.
+* "konqueror": use 'kfmclient' to open the man page in a new konqueror
+tab (see 'Note about konqueror' below).
 
-Multiple values may be given to this configuration variable. Their
-corresponding programs will be tried in the order listed in the
-configuration file.
+Values for other tools can be used if there is a corresponding
+'man.<tool>.cmd' configuration entry (see below).
+
+Multiple values may be given to the 'man.viewer' configuration
+variable. Their corresponding programs will be tried in the order
+listed in the configuration file.
 
 For example, this configuration:
 
+------------------------------------------------
        [man]
                viewer = konqueror
                viewer = woman
+------------------------------------------------
 
 will try to use konqueror first. But this may fail (for example if
 DISPLAY is not set) and in that case emacs' woman mode will be tried.
 
 If everything fails the 'man' program will be tried anyway.
 
+man.<tool>.path
+~~~~~~~~~~~~~~~
+
+You can explicitly provide a full path to your preferred man viewer by
+setting the configuration variable 'man.<tool>.path'. For example, you
+can configure the absolute path to konqueror by setting
+'man.konqueror.path'. Otherwise, 'git help' assumes the tool is
+available in PATH.
+
+man.<tool>.cmd
+~~~~~~~~~~~~~~
+
+When the man viewer, specified by the 'man.viewer' configuration
+variables, is not among the supported ones, then the corresponding
+'man.<tool>.cmd' configuration variable will be looked up. If this
+variable exists then the specified tool will be treated as a custom
+command and a shell eval will be used to run the command with the man
+page passed as arguments.
+
+Note about konqueror
+~~~~~~~~~~~~~~~~~~~~
+
+When 'konqueror' is specified in the 'man.viewer' configuration
+variable, we launch 'kfmclient' to try to open the man page on an
+already opened konqueror in a new tab if possible.
+
+For consistency, we also try such a trick if 'man.konqueror.path' is
+set to something like 'A_PATH_TO/konqueror'. That means we will try to
+launch 'A_PATH_TO/kfmclient' instead.
+
+If you really want to use 'konqueror', then you can use something like
+the following:
+
+------------------------------------------------
+       [man]
+               viewer = konq
+
+       [man "konq"]
+               cmd = A_PATH_TO/konqueror
+------------------------------------------------
+
 Note about git config --global
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index c136b1069230af55b62ba95a1b40bdcc019b7b42..ef1f055c8574e928c8e05c820f2f1cc962e3d422 100644 (file)
@@ -9,7 +9,7 @@ git-merge - Join two or more development histories together
 SYNOPSIS
 --------
 [verse]
-'git-merge' [-n] [--summary] [--no-commit] [--squash] [-s <strategy>]...
+'git-merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
        [-m <msg>] <remote> <remote>...
 'git-merge' <msg> HEAD <remote>...
 
@@ -46,18 +46,7 @@ linkgit:git-reset[1].
 
 CONFIGURATION
 -------------
-
-merge.summary::
-       Whether to include summaries of merged commits in newly
-       created merge commit. False by default.
-
-merge.verbosity::
-       Controls the amount of output shown by the recursive merge
-       strategy.  Level 0 outputs nothing except a final error
-       message if conflicts were detected. Level 1 outputs only
-       conflicts, 2 outputs conflicts and file changes.  Level 5 and
-       above outputs debugging information.  The default is level 2.
-       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
+include::merge-config.txt[]
 
 branch.<name>.mergeoptions::
        Sets default options for merging into branch <name>. The syntax and
index f92bb8cfa069283b0e8b78b717eedbef81bbe647..3178bc44ca4640e77a799128c9de631168e40c8a 100644 (file)
@@ -18,12 +18,15 @@ git-prune. See the section "NOTES", below.
 
 This runs `git-fsck --unreachable` using all the refs
 available in `$GIT_DIR/refs`, optionally with additional set of
-objects specified on the command line, and prunes all
+objects specified on the command line, and prunes all unpacked
 objects unreachable from any of these head objects from the object database.
 In addition, it
 prunes the unpacked objects that are also found in packs by
 running `git prune-packed`.
 
+Note that unreachable, packed objects will remain.  If this is
+not desired, see linkgit:git-repack[1].
+
 OPTIONS
 -------
 
index 3405ca09e85e9c81b9d48c753a2f117e7edca4eb..66304f02557bf57721af215e6593db16ae0c40dc 100644 (file)
@@ -111,40 +111,58 @@ rules apply:
 EXAMPLES
 --------
 
-git pull, git pull origin::
-       Update the remote-tracking branches for the repository
-       you cloned from, then merge one of them into your
-       current branch.  Normally the branch merged in is
-       the HEAD of the remote repository, but the choice is
-       determined by the branch.<name>.remote and
-       branch.<name>.merge options; see linkgit:git-config[1]
-       for details.
-
-git pull origin next::
-       Merge into the current branch the remote branch `next`;
-       leaves a copy of `next` temporarily in FETCH_HEAD, but
-       does not update any remote-tracking branches.
-
-git pull . fixes enhancements::
-       Bundle local branch `fixes` and `enhancements` on top of
-       the current branch, making an Octopus merge.  This `git pull .`
-       syntax is equivalent to `git merge`.
-
-git pull -s ours . obsolete::
-       Merge local branch `obsolete` into the current branch,
-       using `ours` merge strategy.
-
-git pull --no-commit . maint::
-       Merge local branch `maint` into the current branch, but
-       do not make a commit automatically.  This can be used
-       when you want to include further changes to the merge,
-       or want to write your own merge commit message.
+* Update the remote-tracking branches for the repository
+  you cloned from, then merge one of them into your
+  current branch:
++
+------------------------------------------------
+$ git pull, git pull origin
+------------------------------------------------
++
+Normally the branch merged in is the HEAD of the remote repository,
+but the choice is determined by the branch.<name>.remote and
+branch.<name>.merge options; see linkgit:git-config[1] for details.
+
+* Merge into the current branch the remote branch `next`:
++
+------------------------------------------------
+$ git pull origin next
+------------------------------------------------
++
+This leaves a copy of `next` temporarily in FETCH_HEAD, but
+does not update any remote-tracking branches.
+
+* Bundle local branch `fixes` and `enhancements` on top of
+  the current branch, making an Octopus merge:
++
+------------------------------------------------
+$ git pull . fixes enhancements
+------------------------------------------------
++
+This `git pull .` syntax is equivalent to `git merge`.
+
+* Merge local branch `obsolete` into the current branch, using `ours`
+  merge strategy:
++
+------------------------------------------------
+$ git pull -s ours . obsolete
+------------------------------------------------
+
+* Merge local branch `maint` into the current branch, but do not make
+  a commit automatically:
++
+------------------------------------------------
+$ git pull --no-commit . maint
+------------------------------------------------
++
+This can be used when you want to include further changes to the
+merge, or want to write your own merge commit message.
 +
 You should refrain from abusing this option to sneak substantial
 changes into a merge commit.  Small fixups like bumping
 release/version name would be acceptable.
 
-Command line pull of multiple branches from one repository::
+* Command line pull of multiple branches from one repository:
 +
 ------------------------------------------------
 $ git checkout master
@@ -152,12 +170,12 @@ $ git fetch origin +pu:pu maint:tmp
 $ git pull . tmp
 ------------------------------------------------
 +
-This updates (or creates, as necessary) branches `pu` and `tmp`
-in the local repository by fetching from the branches
-(respectively) `pu` and `maint` from the remote repository.
+This updates (or creates, as necessary) branches `pu` and `tmp` in
+the local repository by fetching from the branches (respectively)
+`pu` and `maint` from the remote repository.
 +
-The `pu` branch will be updated even if it is does not
-fast-forward; the others will not be.
+The `pu` branch will be updated even if it is does not fast-forward;
+the others will not be.
 +
 The final command then merges the newly fetched `tmp` into master.
 
index 7d31263aa213e943bea1a0c0ef4bd3fff583322d..0cc44d79993735a8978af9e191e6846c6ceddb74 100644 (file)
@@ -71,7 +71,9 @@ nor in any Push line of the corresponding remotes file---see below).
        be mirrored to the remote repository.  Newly created local
        refs will be pushed to the remote end, locally updated refs
        will be force updated on the remote end, and deleted refs
-       will be removed from the remote end.
+       will be removed from the remote end.  This is the default
+       if the configuration option `remote.<remote>.mirror` is
+       set.
 
 \--dry-run::
        Do everything except actually send the updates.
index 2cbd1f764b7d4d8e8c026f50b727c6cc70f5158e..b20e851973ae6ae1cee17b76f3002f77cd3a3329 100644 (file)
@@ -47,9 +47,11 @@ With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch instead of whatever
 branch the `HEAD` at the remote repository actually points at.
 +
-In mirror mode, enabled with `--mirror`, the refs will not be stored
+In mirror mode, enabled with `\--mirror`, the refs will not be stored
 in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
-only makes sense in bare repositories.
+only makes sense in bare repositories.  If a remote uses mirror
+mode, furthermore, `git push` will always behave as if `\--mirror`
+was passed.
 
 'rm'::
 
index 3d957492f8aea7c2760f7274605d71f5a25349b2..f81a6607dec7bbe9c985106770ecae8b5f01f3c0 100644 (file)
@@ -8,7 +8,7 @@ git-repack - Pack unpacked objects in a repository
 
 SYNOPSIS
 --------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
+'git-repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
 
 DESCRIPTION
 -----------
@@ -37,6 +37,18 @@ OPTIONS
        leaves behind, but `git fsck --full` shows as
        dangling.
 
+-A::
+       Same as `-a`, but any unreachable objects in a previous
+       pack become loose, unpacked objects, instead of being
+       left in the old pack.  Unreachable objects are never
+       intentionally added to a pack, even when repacking.
+       When used with '-d', this option
+       prevents unreachable objects from being immediately
+       deleted by way of being left in the old pack and then
+       removed.  Instead, the loose unreachable objects
+       will be pruned according to normal expiry rules
+       with the next linkgit:git-gc[1].
+
 -d::
        After packing, if the newly created packs make some
        existing packs redundant, remove the redundant packs.
@@ -55,8 +67,11 @@ OPTIONS
        linkgit:git-pack-objects[1].
 
 -n::
-        Do not update the server information with
-        `git update-server-info`.
+       Do not update the server information with
+       `git update-server-info`.  This option skips
+       updating local catalog files needed to publish
+       this repository (or a direct copy of it)
+       over HTTP or FTP.  See gitlink:git-update-server-info[1].
 
 --window=[N], --depth=[N]::
        These two options affect how the objects contained in the pack are
index 110e7ba71f48c54cb94f35e819583826e123a8b1..69599ffb679b17ebbe6ba85c772cd10a3511d89e 100644 (file)
@@ -347,7 +347,7 @@ Each line of options has this format:
 
        * Use `*` to mean that this option should not be listed in the usage
          generated for the `-h` argument. It's shown for `--help-all` as
-         documented in linkgit:gitcli[5].
+         documented in linkgit:gitcli[7].
 
        * Use `!` to not make the corresponding negated long option available.
 
@@ -378,6 +378,31 @@ C?        option C with an optional argument"
 eval `echo "$OPTS_SPEC" | git-rev-parse --parseopt -- "$@" || echo exit $?`
 ------------
 
+EXAMPLES
+--------
+
+* Print the object name of the current commit:
++
+------------
+$ git rev-parse --verify HEAD
+------------
+
+* Print the commit object name from the revision in the $REV shell variable:
++
+------------
+$ git rev-parse --verify $REV
+------------
++
+This will error out if $REV is empty or not a valid revision.
+
+* Same as above:
++
+------------
+$ git rev-parse --default master --verify $REV
+------------
++
+but if $REV is empty, the commit object name from master will be printed.
+
 
 Author
 ------
index 93e20f7752f0d48faca153eef144845b2db9ce1d..13ceabbcc8851344e54e13773a2a553360f931c4 100644 (file)
@@ -7,7 +7,7 @@ git-revert - Revert an existing commit
 
 SYNOPSIS
 --------
-'git-revert' [--edit | --no-edit] [-n] [-m parent-number] <commit>
+'git-revert' [--edit | --no-edit] [-n] [-m parent-number] [-s] <commit>
 
 DESCRIPTION
 -----------
@@ -51,6 +51,9 @@ OPTIONS
 This is useful when reverting more than one commits'
 effect to your working tree in a row.
 
+-s|--signoff::
+       Add Signed-off-by line at the end of the commit message.
+
 
 Author
 ------
index dccf0e20eccd20d45ac83828dbc7c67fa6a86e1a..29ed0acc62156a743db2741d1f576fca87eebff1 100644 (file)
@@ -79,8 +79,6 @@ Documentation
 -------------
 Documentation by David Greaves, Petr Baudis and the git-list <git@vger.kernel.org>.
 
-This manual page is a stub. You can help the git documentation by expanding it.
-
 GIT
 ---
 Part of the linkgit:git[7] suite
index 6ffd896fbc78b783a7e00ed462f879cbff29d758..0668f295eee14facf0f9c4dd6ed2830cb6a348f8 100644 (file)
@@ -11,7 +11,8 @@ SYNOPSIS
 [verse]
 'git-submodule' [--quiet] add [-b branch] [--] <repository> [<path>]
 'git-submodule' [--quiet] status [--cached] [--] [<path>...]
-'git-submodule' [--quiet] [init|update] [--] [<path>...]
+'git-submodule' [--quiet] init [--] [<path>...]
+'git-submodule' [--quiet] update [--init] [--] [<path>...]
 'git-submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
 
 
@@ -47,6 +48,10 @@ update::
        Update the registered submodules, i.e. clone missing submodules and
        checkout the commit specified in the index of the containing repository.
        This will make the submodules HEAD be detached.
++
+If the submodule is not yet initialized, and you just want to use the
+setting as stored in .gitmodules, you can automatically initialize the
+submodule with the --init option.
 
 summary::
        Show commit summary between the given commit (defaults to HEAD) and
index bec9accc8915c712bdea5e8975598e4f26db633f..3eae1ebb7dc104fd51c5e25d163ec33ab7b61ef1 100644 (file)
@@ -166,11 +166,18 @@ environment). This command has the same behaviour.
 Any other arguments are passed directly to `git log'
 
 'blame'::
-       Show what revision and author last modified each line of a file. This is
-       identical to `git blame', but SVN revision numbers are shown instead of git
-       commit hashes.
+       Show what revision and author last modified each line of a file. The
+       output of this mode is format-compatible with the output of
+       `svn blame' by default. Like the SVN blame command,
+       local uncommitted changes in the working copy are ignored;
+       the version of the file in the HEAD revision is annotated. Unknown
+       arguments are passed directly to git-blame.
 +
-All arguments are passed directly to `git blame'.
+--git-format;;
+       Produce output in the same format as `git blame', but with
+       SVN revision numbers instead of git commit hashes. In this mode,
+       changes that haven't been committed to SVN (including local
+       working-copy edits) are shown as revision 0.
 
 --
 'find-rev'::
@@ -188,6 +195,12 @@ All arguments are passed directly to `git blame'.
        commit.  All merging is assumed to have taken place
        independently of git-svn functions.
 
+'create-ignore'::
+       Recursively finds the svn:ignore property on directories and
+       creates matching .gitignore files. The resulting files are staged to
+       be committed, but are not committed. Use -r/--revision to refer to a
+       specfic revision.
+
 'show-ignore'::
        Recursively finds and lists the svn:ignore property on
        directories.  The output is suitable for appending to
@@ -210,6 +223,19 @@ All arguments are passed directly to `git blame'.
        argument.  Use the --url option to output only the value of the
        'URL:' field.
 
+'proplist'::
+       Lists the properties stored in the Subversion repository about a
+       given file or directory.  Use -r/--revision to refer to a specific
+       Subversion revision.
+
+'propget'::
+       Gets the Subversion property given as the first argument, for a
+       file.  A specific revision can be specified with -r/--revision.
+
+'show-externals'::
+       Shows the Subversion externals.  Use -r/--revision to specify a
+       specific revision.
+
 --
 
 OPTIONS
index ddbae5b194790d7f76b53885e3d4ea642fda034a..5ba0b9f1c9214b33948354f6b85c2b343674cbe5 100644 (file)
@@ -20,7 +20,7 @@ The following browsers (or commands) are currently supported:
 
 * firefox (this is the default under X Window when not using KDE)
 * iceweasel
-* konqueror (this is the default under KDE)
+* konqueror (this is the default under KDE, see 'Note about konqueror' below)
 * w3m (this is the default outside graphical environments)
 * links
 * lynx
@@ -71,6 +71,28 @@ variable exists then "git web--browse" will treat the specified tool
 as a custom command and will use a shell eval to run the command with
 the URLs passed as arguments.
 
+Note about konqueror
+--------------------
+
+When 'konqueror' is specified by the 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.
+
+For consistency, we also try such a trick if 'browser.konqueror.path' is
+set to something like 'A_PATH_TO/konqueror'. That means we will try to
+launch 'A_PATH_TO/kfmclient' instead.
+
+If you really want to use 'konqueror', then you can use something like
+the following:
+
+------------------------------------------------
+       [web]
+               browser = konq
+
+       [browser "konq"]
+               cmd = A_PATH_TO/konqueror
+------------------------------------------------
+
 Note about git config --global
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index 6f445b1e3bbefccdb865f8cb93fb470892b94e41..adcd3e00b201af6182bbc441aa0b288a5d2db8a5 100644 (file)
@@ -364,7 +364,7 @@ File/Directory Structure
 
 Please see the link:repository-layout.html[repository layout] document.
 
-Read link:hooks.html[hooks] for more details about each hook.
+Read linkgit:githooks[5][hooks] for more details about each hook.
 
 Higher level SCMs may provide and manage additional information in the
 `$GIT_DIR`.
index 7ee5ce386f851ee054991c5275d89bca4f226447..835cb05f9655de943b7b65178c933fc5d39c52f8 100644 (file)
@@ -1,4 +1,4 @@
-gitcli(5)
+gitcli(7)
 =========
 
 NAME
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
new file mode 100644 (file)
index 0000000..53747fe
--- /dev/null
@@ -0,0 +1,303 @@
+githooks(5)
+===========
+
+NAME
+----
+githooks - Hooks used by git
+
+SYNOPSIS
+--------
+$GIT_DIR/hooks/*
+
+
+DESCRIPTION
+-----------
+
+Hooks are little scripts you can place in `$GIT_DIR/hooks`
+directory to trigger action at certain points.  When
+`git-init` is run, a handful example hooks are copied in the
+`hooks` directory of the new repository, but by default they are
+all disabled.  To enable a hook, make it executable with `chmod +x`.
+
+This document describes the currently defined hooks.
+
+applypatch-msg
+--------------
+
+This hook is invoked by `git-am` script.  It takes a single
+parameter, the name of the file that holds the proposed commit
+log message.  Exiting with non-zero status causes
+`git-am` to abort before applying the patch.
+
+The hook is allowed to edit the message file in place, and can
+be used to normalize the message into some project standard
+format (if the project has one). It can also be used to refuse
+the commit after inspecting the message file.
+
+The default 'applypatch-msg' hook, when enabled, runs the
+'commit-msg' hook, if the latter is enabled.
+
+pre-applypatch
+--------------
+
+This hook is invoked by `git-am`.  It takes no parameter, and is
+invoked after the patch is applied, but before a commit is made.
+
+If it exits with non-zero status, then the working tree will not be
+committed after applying the patch.
+
+It can be used to inspect the current working tree and refuse to
+make a commit if it does not pass certain test.
+
+The default 'pre-applypatch' hook, when enabled, runs the
+'pre-commit' hook, if the latter is enabled.
+
+post-applypatch
+---------------
+
+This hook is invoked by `git-am`.  It takes no parameter,
+and is invoked after the patch is applied and a commit is made.
+
+This hook is meant primarily for notification, and cannot affect
+the outcome of `git-am`.
+
+pre-commit
+----------
+
+This hook is invoked by `git-commit`, and can be bypassed
+with `\--no-verify` option.  It takes no parameter, and is
+invoked before obtaining the proposed commit log message and
+making a commit.  Exiting with non-zero status from this script
+causes the `git-commit` to abort.
+
+The default 'pre-commit' hook, when enabled, catches introduction
+of lines with trailing whitespaces and aborts the commit when
+such a line is found.
+
+All the `git-commit` hooks are invoked with the environment
+variable `GIT_EDITOR=:` if the command will not bring up an editor
+to modify the commit message.
+
+prepare-commit-msg
+------------------
+
+This hook is invoked by `git-commit` right after preparing the
+default log message, and before the editor is started.
+
+It takes one to three parameters.  The first is the name of the file
+that the commit log message.  The second is the source of the commit
+message, and can be: `message` (if a `\-m` or `\-F` option was
+given); `template` (if a `\-t` option was given or the
+configuration option `commit.template` is set); `merge` (if the
+commit is a merge or a `.git/MERGE_MSG` file exists); `squash`
+(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by
+a commit SHA1 (if a `\-c`, `\-C` or `\--amend` option was given).
+
+If the exit status is non-zero, `git-commit` will abort.
+
+The purpose of the hook is to edit the message file in place, and
+it is not suppressed by the `\--no-verify` option.  A non-zero exit
+means a failure of the hook and aborts the commit.  It should not
+be used as replacement for pre-commit hook.
+
+The sample `prepare-commit-msg` hook that comes with git comments
+out the `Conflicts:` part of a merge's commit message.
+
+commit-msg
+----------
+
+This hook is invoked by `git-commit`, and can be bypassed
+with `\--no-verify` option.  It takes a single parameter, the
+name of the file that holds the proposed commit log message.
+Exiting with non-zero status causes the `git-commit` to
+abort.
+
+The hook is allowed to edit the message file in place, and can
+be used to normalize the message into some project standard
+format (if the project has one). It can also be used to refuse
+the commit after inspecting the message file.
+
+The default 'commit-msg' hook, when enabled, detects duplicate
+"Signed-off-by" lines, and aborts the commit if one is found.
+
+post-commit
+-----------
+
+This hook is invoked by `git-commit`.  It takes no
+parameter, and is invoked after a commit is made.
+
+This hook is meant primarily for notification, and cannot affect
+the outcome of `git-commit`.
+
+post-checkout
+-----------
+
+This hook is invoked when a `git-checkout` is run after having updated the
+worktree.  The hook is given three parameters: the ref of the previous HEAD,
+the ref of the new HEAD (which may or may not have changed), and a flag
+indicating whether the checkout was a branch checkout (changing branches,
+flag=1) or a file checkout (retrieving a file from the index, flag=0).
+This hook cannot affect the outcome of `git-checkout`.
+
+This hook can be used to perform repository validity checks, auto-display
+differences from the previous HEAD if different, or set working dir metadata
+properties.
+
+post-merge
+-----------
+
+This hook is invoked by `git-merge`, which happens when a `git pull`
+is done on a local repository.  The hook takes a single parameter, a status
+flag specifying whether or not the merge being done was a squash merge.
+This hook cannot affect the outcome of `git-merge` and is not executed,
+if the merge failed due to conflicts.
+
+This hook can be used in conjunction with a corresponding pre-commit hook to
+save and restore any form of metadata associated with the working tree
+(eg: permissions/ownership, ACLS, etc).  See contrib/hooks/setgitperms.perl
+for an example of how to do this.
+
+[[pre-receive]]
+pre-receive
+-----------
+
+This hook is invoked by `git-receive-pack` on the remote repository,
+which happens when a `git push` is done on a local repository.
+Just before starting to update refs on the remote repository, the
+pre-receive hook is invoked.  Its exit status determines the success
+or failure of the update.
+
+This hook executes once for the receive operation. It takes no
+arguments, but for each ref to be updated it receives on standard
+input a line of the format:
+
+  <old-value> SP <new-value> SP <ref-name> LF
+
+where `<old-value>` is the old object name stored in the ref,
+`<new-value>` is the new object name to be stored in the ref and
+`<ref-name>` is the full name of the ref.
+When creating a new ref, `<old-value>` is 40 `0`.
+
+If the hook exits with non-zero status, none of the refs will be
+updated. If the hook exits with zero, updating of individual refs can
+still be prevented by the <<update,'update'>> hook.
+
+Both standard output and standard error output are forwarded to
+`git-send-pack` on the other end, so you can simply `echo` messages
+for the user.
+
+[[update]]
+update
+------
+
+This hook is invoked by `git-receive-pack` on the remote repository,
+which happens when a `git push` is done on a local repository.
+Just before updating the ref on the remote repository, the update hook
+is invoked.  Its exit status determines the success or failure of
+the ref update.
+
+The hook executes once for each ref to be updated, and takes
+three parameters:
+
+ - the name of the ref being updated,
+ - the old object name stored in the ref,
+ - and the new objectname to be stored in the ref.
+
+A zero exit from the update hook allows the ref to be updated.
+Exiting with a non-zero status prevents `git-receive-pack`
+from updating that ref.
+
+This hook can be used to prevent 'forced' update on certain refs by
+making sure that the object name is a commit object that is a
+descendant of the commit object named by the old object name.
+That is, to enforce a "fast forward only" policy.
+
+It could also be used to log the old..new status.  However, it
+does not know the entire set of branches, so it would end up
+firing one e-mail per ref when used naively, though.  The
+<<post-receive,'post-receive'>> hook is more suited to that.
+
+Another use suggested on the mailing list is to use this hook to
+implement access control which is finer grained than the one
+based on filesystem group.
+
+Both standard output and standard error output are forwarded to
+`git-send-pack` on the other end, so you can simply `echo` messages
+for the user.
+
+The default 'update' hook, when enabled--and with
+`hooks.allowunannotated` config option turned on--prevents
+unannotated tags to be pushed.
+
+[[post-receive]]
+post-receive
+------------
+
+This hook is invoked by `git-receive-pack` on the remote repository,
+which happens when a `git push` is done on a local repository.
+It executes on the remote repository once after all the refs have
+been updated.
+
+This hook executes once for the receive operation.  It takes no
+arguments, but gets the same information as the
+<<pre-receive,'pre-receive'>>
+hook does on its standard input.
+
+This hook does not affect the outcome of `git-receive-pack`, as it
+is called after the real work is done.
+
+This supersedes the <<post-update,'post-update'>> hook in that it gets
+both old and new values of all the refs in addition to their
+names.
+
+Both standard output and standard error output are forwarded to
+`git-send-pack` on the other end, so you can simply `echo` messages
+for the user.
+
+The default 'post-receive' hook is empty, but there is
+a sample script `post-receive-email` provided in the `contrib/hooks`
+directory in git distribution, which implements sending commit
+emails.
+
+[[post-update]]
+post-update
+-----------
+
+This hook is invoked by `git-receive-pack` on the remote repository,
+which happens when a `git push` is done on a local repository.
+It executes on the remote repository once after all the refs have
+been updated.
+
+It takes a variable number of parameters, each of which is the
+name of ref that was actually updated.
+
+This hook is meant primarily for notification, and cannot affect
+the outcome of `git-receive-pack`.
+
+The 'post-update' hook can tell what are the heads that were pushed,
+but it does not know what their original and updated values are,
+so it is a poor place to do log old..new. The
+<<post-receive,'post-receive'>> hook does get both original and
+updated values of the refs. You might consider it instead if you need
+them.
+
+When enabled, the default 'post-update' hook runs
+`git-update-server-info` to keep the information used by dumb
+transports (e.g., HTTP) up-to-date.  If you are publishing
+a git repository that is accessible via HTTP, you should
+probably enable this hook.
+
+Both standard output and standard error output are forwarded to
+`git-send-pack` on the other end, so you can simply `echo` messages
+for the user.
+
+pre-auto-gc
+-----------
+
+This hook is invoked by `git-gc --auto`. It takes no parameter, and
+exiting with non-zero status from this script causes the `git-gc --auto`
+to abort.
+
+GIT
+---
+Part of the linkgit:git[7] suite
diff --git a/Documentation/hooks.txt b/Documentation/hooks.txt
deleted file mode 100644 (file)
index 1283ab4..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-Hooks used by git
-=================
-
-Hooks are little scripts you can place in `$GIT_DIR/hooks`
-directory to trigger action at certain points.  When
-`git-init` is run, a handful example hooks are copied in the
-`hooks` directory of the new repository, but by default they are
-all disabled.  To enable a hook, make it executable with `chmod +x`.
-
-This document describes the currently defined hooks.
-
-applypatch-msg
---------------
-
-This hook is invoked by `git-am` script.  It takes a single
-parameter, the name of the file that holds the proposed commit
-log message.  Exiting with non-zero status causes
-`git-am` to abort before applying the patch.
-
-The hook is allowed to edit the message file in place, and can
-be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
-
-The default 'applypatch-msg' hook, when enabled, runs the
-'commit-msg' hook, if the latter is enabled.
-
-pre-applypatch
---------------
-
-This hook is invoked by `git-am`.  It takes no parameter, and is
-invoked after the patch is applied, but before a commit is made.
-
-If it exits with non-zero status, then the working tree will not be
-committed after applying the patch.
-
-It can be used to inspect the current working tree and refuse to
-make a commit if it does not pass certain test.
-
-The default 'pre-applypatch' hook, when enabled, runs the
-'pre-commit' hook, if the latter is enabled.
-
-post-applypatch
----------------
-
-This hook is invoked by `git-am`.  It takes no parameter,
-and is invoked after the patch is applied and a commit is made.
-
-This hook is meant primarily for notification, and cannot affect
-the outcome of `git-am`.
-
-pre-commit
-----------
-
-This hook is invoked by `git-commit`, and can be bypassed
-with `\--no-verify` option.  It takes no parameter, and is
-invoked before obtaining the proposed commit log message and
-making a commit.  Exiting with non-zero status from this script
-causes the `git-commit` to abort.
-
-The default 'pre-commit' hook, when enabled, catches introduction
-of lines with trailing whitespaces and aborts the commit when
-such a line is found.
-
-All the `git-commit` hooks are invoked with the environment
-variable `GIT_EDITOR=:` if the command will not bring up an editor
-to modify the commit message.
-
-prepare-commit-msg
-------------------
-
-This hook is invoked by `git-commit` right after preparing the
-default log message, and before the editor is started.
-
-It takes one to three parameters.  The first is the name of the file
-that the commit log message.  The second is the source of the commit
-message, and can be: `message` (if a `\-m` or `\-F` option was
-given); `template` (if a `\-t` option was given or the
-configuration option `commit.template` is set); `merge` (if the
-commit is a merge or a `.git/MERGE_MSG` file exists); `squash`
-(if a `.git/SQUASH_MSG` file exists); or `commit`, followed by
-a commit SHA1 (if a `\-c`, `\-C` or `\--amend` option was given).
-
-If the exit status is non-zero, `git-commit` will abort.
-
-The purpose of the hook is to edit the message file in place, and
-it is not suppressed by the `\--no-verify` option.  A non-zero exit
-means a failure of the hook and aborts the commit.  It should not
-be used as replacement for pre-commit hook.
-
-The sample `prepare-commit-msg` hook that comes with git comments
-out the `Conflicts:` part of a merge's commit message.
-
-commit-msg
-----------
-
-This hook is invoked by `git-commit`, and can be bypassed
-with `\--no-verify` option.  It takes a single parameter, the
-name of the file that holds the proposed commit log message.
-Exiting with non-zero status causes the `git-commit` to
-abort.
-
-The hook is allowed to edit the message file in place, and can
-be used to normalize the message into some project standard
-format (if the project has one). It can also be used to refuse
-the commit after inspecting the message file.
-
-The default 'commit-msg' hook, when enabled, detects duplicate
-"Signed-off-by" lines, and aborts the commit if one is found.
-
-post-commit
------------
-
-This hook is invoked by `git-commit`.  It takes no
-parameter, and is invoked after a commit is made.
-
-This hook is meant primarily for notification, and cannot affect
-the outcome of `git-commit`.
-
-post-checkout
------------
-
-This hook is invoked when a `git-checkout` is run after having updated the
-worktree.  The hook is given three parameters: the ref of the previous HEAD,
-the ref of the new HEAD (which may or may not have changed), and a flag
-indicating whether the checkout was a branch checkout (changing branches,
-flag=1) or a file checkout (retrieving a file from the index, flag=0).
-This hook cannot affect the outcome of `git-checkout`.
-
-This hook can be used to perform repository validity checks, auto-display
-differences from the previous HEAD if different, or set working dir metadata
-properties.
-
-post-merge
------------
-
-This hook is invoked by `git-merge`, which happens when a `git pull`
-is done on a local repository.  The hook takes a single parameter, a status
-flag specifying whether or not the merge being done was a squash merge.
-This hook cannot affect the outcome of `git-merge`.
-
-This hook can be used in conjunction with a corresponding pre-commit hook to
-save and restore any form of metadata associated with the working tree
-(eg: permissions/ownership, ACLS, etc).  See contrib/hooks/setgitperms.perl
-for an example of how to do this.
-
-[[pre-receive]]
-pre-receive
------------
-
-This hook is invoked by `git-receive-pack` on the remote repository,
-which happens when a `git push` is done on a local repository.
-Just before starting to update refs on the remote repository, the
-pre-receive hook is invoked.  Its exit status determines the success
-or failure of the update.
-
-This hook executes once for the receive operation. It takes no
-arguments, but for each ref to be updated it receives on standard
-input a line of the format:
-
-  <old-value> SP <new-value> SP <ref-name> LF
-
-where `<old-value>` is the old object name stored in the ref,
-`<new-value>` is the new object name to be stored in the ref and
-`<ref-name>` is the full name of the ref.
-When creating a new ref, `<old-value>` is 40 `0`.
-
-If the hook exits with non-zero status, none of the refs will be
-updated. If the hook exits with zero, updating of individual refs can
-still be prevented by the <<update,'update'>> hook.
-
-Both standard output and standard error output are forwarded to
-`git-send-pack` on the other end, so you can simply `echo` messages
-for the user.
-
-[[update]]
-update
-------
-
-This hook is invoked by `git-receive-pack` on the remote repository,
-which happens when a `git push` is done on a local repository.
-Just before updating the ref on the remote repository, the update hook
-is invoked.  Its exit status determines the success or failure of
-the ref update.
-
-The hook executes once for each ref to be updated, and takes
-three parameters:
-
- - the name of the ref being updated,
- - the old object name stored in the ref,
- - and the new objectname to be stored in the ref.
-
-A zero exit from the update hook allows the ref to be updated.
-Exiting with a non-zero status prevents `git-receive-pack`
-from updating that ref.
-
-This hook can be used to prevent 'forced' update on certain refs by
-making sure that the object name is a commit object that is a
-descendant of the commit object named by the old object name.
-That is, to enforce a "fast forward only" policy.
-
-It could also be used to log the old..new status.  However, it
-does not know the entire set of branches, so it would end up
-firing one e-mail per ref when used naively, though.  The
-<<post-receive,'post-receive'>> hook is more suited to that.
-
-Another use suggested on the mailing list is to use this hook to
-implement access control which is finer grained than the one
-based on filesystem group.
-
-Both standard output and standard error output are forwarded to
-`git-send-pack` on the other end, so you can simply `echo` messages
-for the user.
-
-The default 'update' hook, when enabled--and with
-`hooks.allowunannotated` config option turned on--prevents
-unannotated tags to be pushed.
-
-[[post-receive]]
-post-receive
-------------
-
-This hook is invoked by `git-receive-pack` on the remote repository,
-which happens when a `git push` is done on a local repository.
-It executes on the remote repository once after all the refs have
-been updated.
-
-This hook executes once for the receive operation.  It takes no
-arguments, but gets the same information as the
-<<pre-receive,'pre-receive'>>
-hook does on its standard input.
-
-This hook does not affect the outcome of `git-receive-pack`, as it
-is called after the real work is done.
-
-This supersedes the <<post-update,'post-update'>> hook in that it gets
-both old and new values of all the refs in addition to their
-names.
-
-Both standard output and standard error output are forwarded to
-`git-send-pack` on the other end, so you can simply `echo` messages
-for the user.
-
-The default 'post-receive' hook is empty, but there is
-a sample script `post-receive-email` provided in the `contrib/hooks`
-directory in git distribution, which implements sending commit
-emails.
-
-[[post-update]]
-post-update
------------
-
-This hook is invoked by `git-receive-pack` on the remote repository,
-which happens when a `git push` is done on a local repository.
-It executes on the remote repository once after all the refs have
-been updated.
-
-It takes a variable number of parameters, each of which is the
-name of ref that was actually updated.
-
-This hook is meant primarily for notification, and cannot affect
-the outcome of `git-receive-pack`.
-
-The 'post-update' hook can tell what are the heads that were pushed,
-but it does not know what their original and updated values are,
-so it is a poor place to do log old..new. The
-<<post-receive,'post-receive'>> hook does get both original and
-updated values of the refs. You might consider it instead if you need
-them.
-
-When enabled, the default 'post-update' hook runs
-`git-update-server-info` to keep the information used by dumb
-transports (e.g., HTTP) up-to-date.  If you are publishing
-a git repository that is accessible via HTTP, you should
-probably enable this hook.
-
-Both standard output and standard error output are forwarded to
-`git-send-pack` on the other end, so you can simply `echo` messages
-for the user.
-
-pre-auto-gc
------------
-
-This hook is invoked by `git-gc --auto`. It takes no parameter, and
-exiting with non-zero status from this script causes the `git-gc --auto`
-to abort.
diff --git a/Documentation/merge-config.txt b/Documentation/merge-config.txt
new file mode 100644 (file)
index 0000000..48ce747
--- /dev/null
@@ -0,0 +1,40 @@
+merge.stat::
+       Whether to print the diffstat berween ORIG_HEAD and merge result
+       at the end of the merge.  True by default.
+
+merge.log::
+       Whether to include summaries of merged commits in newly created
+       merge commit messages. False by default.
+
+merge.renameLimit::
+       The number of files to consider when performing rename detection
+       during a merge; if not specified, defaults to the value of
+       diff.renameLimit.
+
+merge.tool::
+       Controls which merge resolution program is used by
+       linkgit:git-mergetool[1].  Valid built-in values are: "kdiff3",
+       "tkdiff", "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and
+       "opendiff".  Any other value is treated is custom merge tool
+       and there must be a corresponing mergetool.<tool>.cmd option.
+
+merge.verbosity::
+       Controls the amount of output shown by the recursive merge
+       strategy.  Level 0 outputs nothing except a final error
+       message if conflicts were detected. Level 1 outputs only
+       conflicts, 2 outputs conflicts and file changes.  Level 5 and
+       above outputs debugging information.  The default is level 2.
+       Can be overridden by 'GIT_MERGE_VERBOSITY' environment variable.
+
+merge.<driver>.name::
+       Defines a human readable name for a custom low-level
+       merge driver.  See linkgit:gitattributes[5] for details.
+
+merge.<driver>.driver::
+       Defines the command that implements a custom low-level
+       merge driver.  See linkgit:gitattributes[5] for details.
+
+merge.<driver>.recursive::
+       Names a low-level merge driver to be used when
+       performing an internal merge between common ancestors.
+       See linkgit:gitattributes[5] for details.
index 9f1fc825503a7c972fe162f4e2a87781e0f783f3..f37a77648934fcd8c707cfc1446ef5425cc7fa79 100644 (file)
@@ -1,10 +1,23 @@
---summary::
+--stat::
        Show a diffstat at the end of the merge. The diffstat is also
-       controlled by the configuration option merge.diffstat.
+       controlled by the configuration option merge.stat.
 
--n, \--no-summary::
+-n, \--no-stat::
        Do not show diffstat at the end of the merge.
 
+--summary, \--no-summary::
+       Synonyms to --stat and --no-stat; these are deprecated and will be
+       removed in the future.
+
+--log::
+       In addition to branch names, populate the log message with
+       one-line descriptions from the actual commits that are being
+       merged.
+
+--no-log::
+       Do not list one-line descriptions from the actual commits being
+       merged.
+
 --no-commit::
        Perform the merge but pretend the merge failed and do
        not autocommit, to give the user a chance to inspect and
index 69391300944438576e11cff19b4ce1aa2215b97b..7fd187be8a34a3ebc6d8d543c868f1ca932990af 100644 (file)
@@ -3,7 +3,10 @@ git repository layout
 
 You may find these things in your git repository (`.git`
 directory for a repository associated with your working tree, or
-`'project'.git` directory for a public 'bare' repository).
+`'project'.git` directory for a public 'bare' repository. It is
+also possible to have a working tree where `.git` is a plain
+ascii file containing `gitdir: <path>`, i.e. the path to the
+real git repository).
 
 objects::
        Object store associated with this repository.  Usually
@@ -121,7 +124,7 @@ hooks::
        commands.  A handful of sample hooks are installed when
        `git init` is run, but all of them are disabled by
        default.  To enable, they need to be made executable.
-       Read link:hooks.html[hooks] for more details about
+       Read linkgit:githooks[5][hooks] for more details about
        each hook.
 
 index::
index 2648a550850bf07128e4a0a14c82860d6bad17b8..dfcef79a6b772ae5fb11fc378772042ba465615f 100644 (file)
@@ -13,10 +13,11 @@ include::pretty-options.txt[]
 
        Synonym for `--date=relative`.
 
---date={relative,local,default,iso,rfc}::
+--date={relative,local,default,iso,rfc,short}::
 
        Only takes effect for dates shown in human-readable format, such
-       as when using "--pretty".
+       as when using "--pretty". `log.date` config variable sets a default
+       value for log command's --date option.
 +
 `--date=relative` shows dates relative to the current time,
 e.g. "2 hours ago".
@@ -75,6 +76,16 @@ you would get an output line this:
        -xxxxxxx... 1st on a
 -----------------------------------------------------------------------
 
+--graph::
+
+       Draw a text-based graphical representation of the commit history
+       on the left hand side of the output.  This may cause extra lines
+       to be printed in between commits, in order for the graph history
+       to be drawn properly.
++
+This implies the '--topo-order' option by default, but the
+'--date-order' option may also be specified.
+
 Diff Formatting
 ~~~~~~~~~~~~~~~
 
diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt
new file mode 100644 (file)
index 0000000..ce1c08e
--- /dev/null
@@ -0,0 +1,179 @@
+history graph API
+=================
+
+The graph API is used to draw a text-based representation of the commit
+history.  The API generates the graph in a line-by-line fashion.
+
+Functions
+---------
+
+Core functions:
+
+* `graph_init()` creates a new `struct git_graph`
+
+* `graph_release()` destroys a `struct git_graph`, and frees the memory
+  associated with it.
+
+* `graph_update()` moves the graph to a new commit.
+
+* `graph_next_line()` outputs the next line of the graph into a strbuf.  It
+  does not add a terminating newline.
+
+* `graph_padding_line()` outputs a line of vertical padding in the graph.  It
+  is similar to `graph_next_line()`, but is guaranteed to never print the line
+  containing the current commit.  Where `graph_next_line()` would print the
+  commit line next, `graph_padding_line()` prints a line that simply extends
+  all branch lines downwards one row, leaving their positions unchanged.
+
+* `graph_is_commit_finished()` determines if the graph has output all lines
+  necessary for the current commit.  If `graph_update()` is called before all
+  lines for the current commit have been printed, the next call to
+  `graph_next_line()` will output an ellipsis, to indicate that a portion of
+  the graph was omitted.
+
+The following utility functions are wrappers around `graph_next_line()` and
+`graph_is_commit_finished()`.  They always print the output to stdout.
+They can all be called with a NULL graph argument, in which case no graph
+output will be printed.
+
+* `graph_show_commit()` calls `graph_next_line()` until it returns non-zero.
+  This prints all graph lines up to, and including, the line containing this
+  commit.  Output is printed to stdout.  The last line printed does not contain
+  a terminating newline.  This should not be called if the commit line has
+  already been printed, or it will loop forever.
+
+* `graph_show_oneline()` calls `graph_next_line()` and prints the result to
+  stdout.  The line printed does not contain a terminating newline.
+
+* `graph_show_padding()` calls `graph_padding_line()` and prints the result to
+  stdout.  The line printed does not contain a terminating newline.
+
+* `graph_show_remainder()` calls `graph_next_line()` until
+  `graph_is_commit_finished()` returns non-zero.  Output is printed to stdout.
+  The last line printed does not contain a terminating newline.  Returns 1 if
+  output was printed, and 0 if no output was necessary.
+
+* `graph_show_strbuf()` prints the specified strbuf to stdout, prefixing all
+  lines but the first with a graph line.  The caller is responsible for
+  ensuring graph output for the first line has already been printed to stdout.
+  (This can be done with `graph_show_commit()` or `graph_show_oneline()`.)  If
+  a NULL graph is supplied, the strbuf is printed as-is.
+
+* `graph_show_commit_msg()` is similar to `graph_show_strbuf()`, but it also
+  prints the remainder of the graph, if more lines are needed after the strbuf
+  ends.  It is better than directly calling `graph_show_strbuf()` followed by
+  `graph_show_remainder()` since it properly handles buffers that do not end in
+  a terminating newline.  The output printed by `graph_show_commit_msg()` will
+  end in a newline if and only if the strbuf ends in a newline.
+
+Data structure
+--------------
+`struct git_graph` is an opaque data type used to store the current graph
+state.
+
+Calling sequence
+----------------
+
+* Create a `struct git_graph` by calling `graph_init()`.  When using the
+  revision walking API, this is done automatically by `setup_revisions()` if
+  the '--graph' option is supplied.
+
+* Use the revision walking API to walk through a group of contiguous commits.
+  The `get_revision()` function automatically calls `graph_update()` each time
+  it is invoked.
+
+* For each commit, call `graph_next_line()` repeatedly, until
+  `graph_is_commit_finished()` returns non-zero.  Each call go
+  `graph_next_line()` will output a single line of the graph.  The resulting
+  lines will not contain any newlines.  `graph_next_line()` returns 1 if the
+  resulting line contains the current commit, or 0 if this is merely a line
+  needed to adjust the graph before or after the current commit.  This return
+  value can be used to determine where to print the commit summary information
+  alongside the graph output.
+
+Limitations
+-----------
+
+* `graph_update()` must be called with commits in topological order.  It should
+  not be called on a commit if it has already been invoked with an ancestor of
+  that commit, or the graph output will be incorrect.
+
+* `graph_update()` must be called on a contiguous group of commits.  If
+  `graph_update()` is called on a particular commit, it should later be called
+  on all parents of that commit.  Parents must not be skipped, or the graph
+  output will appear incorrect.
++
+`graph_update()` may be used on a pruned set of commits only if the parent list
+has been rewritten so as to include only ancestors from the pruned set.
+
+* The graph API does not currently support reverse commit ordering.  In
+  order to implement reverse ordering, the graphing API needs an
+  (efficient) mechanism to find the children of a commit.
+
+Sample usage
+------------
+
+------------
+struct commit *commit;
+struct git_graph *graph = graph_init();
+
+while ((commit = get_revision(opts)) != NULL) {
+       graph_update(graph, commit);
+       while (!graph_is_commit_finished(graph))
+       {
+               struct strbuf sb;
+               int is_commit_line;
+
+               strbuf_init(&sb, 0);
+               is_commit_line = graph_next_line(graph, &sb);
+               fputs(sb.buf, stdout);
+
+               if (is_commit_line)
+                       log_tree_commit(opts, commit);
+               else
+                       putchar(opts->diffopt.line_termination);
+       }
+}
+
+graph_release(graph);
+------------
+
+Sample output
+-------------
+
+The following is an example of the output from the graph API.  This output does
+not include any commit summary information--callers are responsible for
+outputting that information, if desired.
+
+------------
+*
+*
+M
+|\
+* |
+| | *
+| \ \
+|  \ \
+M-. \ \
+|\ \ \ \
+| | * | |
+| | | | | *
+| | | | | *
+| | | | | M
+| | | | | |\
+| | | | | | *
+| * | | | | |
+| | | | | M  \
+| | | | | |\  |
+| | | | * | | |
+| | | | * | | |
+* | | | | | | |
+| |/ / / / / /
+|/| / / / / /
+* | | | | | |
+|/ / / / / /
+* | | | | |
+| | | | | *
+| | | | |/
+| | | | *
+------------
index 86b91a53e5c822782ed7682c83d435180d7b682f..e2db850150a683087491016159a0ff0a75eb879a 100644 (file)
@@ -1881,7 +1881,7 @@ $ chmod a+x hooks/post-update
 
 (For an explanation of the last two lines, see
 linkgit:git-update-server-info[1], and the documentation
-link:hooks.html[Hooks used by git].)
+linkgit:githooks[5][Hooks used by git].)
 
 Advertise the URL of proj.git.  Anybody else should then be able to
 clone or pull from that URL, for example with a command line like:
index f60bab896bd9a54714eff3596e38c9ffb9d38ac8..3cabc92e7a1f8ce30eb6e5151a4c81c82468bcee 100755 (executable)
@@ -11,7 +11,7 @@ LF='
 if test -f version
 then
        VN=$(cat version) || VN="$DEF_VER"
-elif test -d .git &&
+elif test -d .git -o -f .git &&
        VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
        case "$VN" in
        *$LF*) (exit 1) ;;
diff --git a/INSTALL b/INSTALL
index 6f3bcb45253367e0bf7fc8e43e45ccb404b23824..d9b425fa73e806751ff2636300ea24bd1e7f1e56 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -38,6 +38,10 @@ Issues of note:
    has been actively developed since 1997, and people have moved over to
    graphical file managers.
 
+   NOTE: As of gnuit-4.9.2, the GNU interactive tools package has been
+         renamed. You can compile gnuit with the --disable-transition
+         option and then it will not conflict with git.
+
  - You can use git after building but without installing if you
    wanted to.  Various git commands need to find other git
    commands and scripts to do their work, so you would need to
index 9d84c8d799fc1044177197fb804411b05569dc83..865e2bfcf1f3e640989efc5e80f1efb9403cc76e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -346,6 +346,7 @@ LIB_H += diff.h
 LIB_H += dir.h
 LIB_H += fsck.h
 LIB_H += git-compat-util.h
+LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
 LIB_H += list-objects.h
@@ -411,6 +412,7 @@ LIB_OBJS += entry.o
 LIB_OBJS += environment.o
 LIB_OBJS += exec_cmd.o
 LIB_OBJS += fsck.o
+LIB_OBJS += graph.o
 LIB_OBJS += grep.o
 LIB_OBJS += hash.o
 LIB_OBJS += help.o
@@ -423,6 +425,7 @@ LIB_OBJS += log-tree.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
 LIB_OBJS += merge-file.o
+LIB_OBJS += name-hash.o
 LIB_OBJS += object.o
 LIB_OBJS += pack-check.o
 LIB_OBJS += pack-revindex.o
@@ -635,8 +638,12 @@ endif
 ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
+       NO_MKDTEMP = YesPlease
        NO_STRLCPY = YesPlease
+       FREAD_READS_DIRECTORIES = UnfortunatelyYes
+       INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
+       BASIC_CFLAGS += -D_LARGE_FILES
 endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
index daf862e728622f9a59a2721e5cd297394be6c9da..56e949232cac1fe9dbe23aa93b50fe2f7c55c07d 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -32,6 +32,21 @@ static int find_tracked_branch(struct remote *remote, void *priv)
        return 0;
 }
 
+static int should_setup_rebase(const struct tracking *tracking)
+{
+       switch (autorebase) {
+       case AUTOREBASE_NEVER:
+               return 0;
+       case AUTOREBASE_LOCAL:
+               return tracking->remote == NULL;
+       case AUTOREBASE_REMOTE:
+               return tracking->remote != NULL;
+       case AUTOREBASE_ALWAYS:
+               return 1;
+       }
+       return 0;
+}
+
 /*
  * This is called when new_ref is branched off of orig_ref, and tries
  * to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -69,9 +84,14 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,
        git_config_set(key, tracking.remote ?  tracking.remote : ".");
        sprintf(key, "branch.%s.merge", new_ref);
        git_config_set(key, tracking.src ? tracking.src : orig_ref);
-       free(tracking.src);
        printf("Branch %s set up to track %s branch %s.\n", new_ref,
                tracking.remote ? "remote" : "local", orig_ref);
+       if (should_setup_rebase(&tracking)) {
+               sprintf(key, "branch.%s.rebase", new_ref);
+               git_config_set(key, "true");
+               printf("This branch will rebase on pull.\n");
+       }
+       free(tracking.src);
 
        return 0;
 }
index 4a91e3eb118850882fbc22e8d8f37e8bbfaa7617..73235ed08a9d60f8134235d9b68632303f359ca9 100644 (file)
@@ -79,12 +79,18 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec,
                prune_directory(dir, pathspec, baselen);
 }
 
+struct update_callback_data
+{
+       int flags;
+       int add_errors;
+};
+
 static void update_callback(struct diff_queue_struct *q,
                            struct diff_options *opt, void *cbdata)
 {
-       int i, verbose;
+       int i;
+       struct update_callback_data *data = cbdata;
 
-       verbose = *((int *)cbdata);
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
                const char *path = p->one->path;
@@ -94,27 +100,35 @@ static void update_callback(struct diff_queue_struct *q,
                case DIFF_STATUS_UNMERGED:
                case DIFF_STATUS_MODIFIED:
                case DIFF_STATUS_TYPE_CHANGED:
-                       add_file_to_cache(path, verbose);
+                       if (add_file_to_cache(path, data->flags & ADD_FILES_VERBOSE)) {
+                               if (!(data->flags & ADD_FILES_IGNORE_ERRORS))
+                                       die("updating files failed");
+                               data->add_errors++;
+                       }
                        break;
                case DIFF_STATUS_DELETED:
                        remove_file_from_cache(path);
-                       if (verbose)
+                       if (data->flags & ADD_FILES_VERBOSE)
                                printf("remove '%s'\n", path);
                        break;
                }
        }
 }
 
-void add_files_to_cache(int verbose, const char *prefix, const char **pathspec)
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
 {
+       struct update_callback_data data;
        struct rev_info rev;
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
        rev.prune_data = pathspec;
        rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = update_callback;
-       rev.diffopt.format_callback_data = &verbose;
+       data.flags = flags;
+       data.add_errors = 0;
+       rev.diffopt.format_callback_data = &data;
        run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+       return !!data.add_errors;
 }
 
 static void refresh(int verbose, const char **pathspec)
@@ -177,6 +191,7 @@ static const char ignore_error[] =
 "The following paths are ignored by one of your .gitignore files:\n";
 
 static int verbose = 0, show_only = 0, ignored_too = 0, refresh_only = 0;
+static int ignore_add_errors;
 
 static struct option builtin_add_options[] = {
        OPT__DRY_RUN(&show_only),
@@ -187,11 +202,22 @@ static struct option builtin_add_options[] = {
        OPT_BOOLEAN('f', NULL, &ignored_too, "allow adding otherwise ignored files"),
        OPT_BOOLEAN('u', NULL, &take_worktree_changes, "update tracked files"),
        OPT_BOOLEAN( 0 , "refresh", &refresh_only, "don't add, only refresh the index"),
+       OPT_BOOLEAN( 0 , "ignore-errors", &ignore_add_errors, "just skip files which cannot be added because of errors"),
        OPT_END(),
 };
 
+static int add_config(const char *var, const char *value)
+{
+       if (!strcasecmp(var, "add.ignore-errors")) {
+               ignore_add_errors = git_config_bool(var, value);
+               return 0;
+       }
+       return git_default_config(var, value);
+}
+
 int cmd_add(int argc, const char **argv, const char *prefix)
 {
+       int exit_status = 0;
        int i, newfd;
        const char **pathspec;
        struct dir_struct dir;
@@ -203,16 +229,23 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        if (add_interactive)
                exit(interactive_add(argc, argv, prefix));
 
-       git_config(git_default_config);
+       git_config(add_config);
 
        newfd = hold_locked_index(&lock_file, 1);
 
        if (take_worktree_changes) {
+               int flags = 0;
                const char **pathspec;
                if (read_cache() < 0)
                        die("index file corrupt");
                pathspec = get_pathspec(prefix, argv);
-               add_files_to_cache(verbose, prefix, pathspec);
+
+               if (verbose)
+                       flags |= ADD_FILES_VERBOSE;
+               if (ignore_add_errors)
+                       flags |= ADD_FILES_IGNORE_ERRORS;
+
+               exit_status = add_files_to_cache(prefix, pathspec, flags);
                goto finish;
        }
 
@@ -254,7 +287,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
        }
 
        for (i = 0; i < dir.nr; i++)
-               add_file_to_cache(dir.entries[i]->name, verbose);
+               if (add_file_to_cache(dir.entries[i]->name, verbose)) {
+                       if (!ignore_add_errors)
+                               die("adding files failed");
+                       exit_status = 1;
+               }
 
  finish:
        if (active_cache_changed) {
@@ -263,5 +300,5 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        die("Unable to write new index file");
        }
 
-       return 0;
+       return exit_status;
 }
index caa3f2aa0ca147091d28013c3d2c11346e350675..1103625a4a923d92ca8d335d1a872236630541e5 100644 (file)
@@ -2247,7 +2247,7 @@ static int check_to_create_blob(const char *new_name, int ok_if_exists)
                 * In such a case, path "new_name" does not exist as
                 * far as git is concerned.
                 */
-               if (has_symlink_leading_path(new_name, NULL))
+               if (has_symlink_leading_path(strlen(new_name), new_name))
                        return 0;
 
                return error("%s: already exists in working directory", new_name);
index 5bc4526f645d40a4a32c50240975ad26f49b8e3b..19c508a60827c9c7c744da995bf5737733864a76 100644 (file)
@@ -15,7 +15,7 @@
 #include "branch.h"
 
 static const char * const builtin_branch_usage[] = {
-       "git-branch [options] [-r | -a]",
+       "git-branch [options] [-r | -a] [--merged | --no-merged]",
        "git-branch [options] [-l] [-f] <branchname> [<start-point>]",
        "git-branch [options] [-r] (-d | -D) <branchname>",
        "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>",
@@ -46,6 +46,8 @@ enum color_branch {
        COLOR_BRANCH_CURRENT = 4,
 };
 
+static int mergefilter = -1;
+
 static int parse_branch_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -210,6 +212,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        struct ref_item *newitem;
        int kind = REF_UNKNOWN_TYPE;
        int len;
+       static struct commit_list branch;
 
        /* Detect kind */
        if (!prefixcmp(refname, "refs/heads/")) {
@@ -231,6 +234,16 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
+       if (mergefilter > -1) {
+               branch.item = lookup_commit_reference_gently(sha1, 1);
+               if (!branch.item)
+                       die("Unable to lookup tip of branch %s", refname);
+               if (mergefilter == 0 && has_commit(head_sha1, &branch))
+                       return 0;
+               if (mergefilter == 1 && !has_commit(head_sha1, &branch))
+                       return 0;
+       }
+
        /* Resize buffer */
        if (ref_list->index >= ref_list->alloc) {
                ref_list->alloc = alloc_nr(ref_list->alloc);
@@ -444,6 +457,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
                OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
                OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+               OPT_SET_INT(0, "merged", &mergefilter, "list only merged branches", 1),
                OPT_END(),
        };
 
index 10ec137ccec7d7f281562817d81a1035c4aa6124..05c06421b6c3a6d75b598e3c58a40c8461febd75 100644 (file)
@@ -282,7 +282,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                         * entries in the index.
                         */
 
-                       add_files_to_cache(0, NULL, NULL);
+                       add_files_to_cache(NULL, NULL, 0);
                        work = write_tree_from_memory();
 
                        ret = reset_to_new(new->commit->tree, opts->quiet);
index 256181a68bd991278fda4ecb48911d03b5f23141..d75224381b7ad88d6bbb67db3a85bbb881cea941 100644 (file)
@@ -47,6 +47,7 @@ static enum {
 
 static char *logfile, *force_author, *template_file;
 static char *edit_message, *use_message;
+static char *author_name, *author_email, *author_date;
 static int all, edit_flag, also, interactive, only, amend, signoff;
 static int quiet, verbose, untracked_files, no_verify, allow_empty;
 /*
@@ -101,7 +102,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
        OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
-       OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
+       OPT_BOOLEAN('u', "untracked-files", &untracked_files, "show all untracked files"),
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
        OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 
@@ -175,10 +176,13 @@ static void add_remove_files(struct path_list *list)
 {
        int i;
        for (i = 0; i < list->nr; i++) {
+               struct stat st;
                struct path_list_item *p = &(list->items[i]);
-               if (file_exists(p->path))
-                       add_file_to_cache(p->path, 0);
-               else
+
+               if (!lstat(p->path, &st)) {
+                       if (add_to_cache(p->path, &st, 0))
+                               die("updating files failed");
+               } else
                        remove_file_from_cache(p->path);
        }
 }
@@ -243,7 +247,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
         */
        if (all || (also && pathspec && *pathspec)) {
                int fd = hold_locked_index(&index_lock, 1);
-               add_files_to_cache(0, also ? prefix : NULL, pathspec);
+               add_files_to_cache(also ? prefix : NULL, pathspec, 0);
                refresh_cache(REFRESH_QUIET);
                if (write_cache(fd, active_cache, active_nr) ||
                    close_lock_file(&index_lock))
@@ -395,6 +399,47 @@ static int is_a_merge(const unsigned char *sha1)
 
 static const char sign_off_header[] = "Signed-off-by: ";
 
+static void determine_author_info(void)
+{
+       char *name, *email, *date;
+
+       name = getenv("GIT_AUTHOR_NAME");
+       email = getenv("GIT_AUTHOR_EMAIL");
+       date = getenv("GIT_AUTHOR_DATE");
+
+       if (use_message) {
+               const char *a, *lb, *rb, *eol;
+
+               a = strstr(use_message_buffer, "\nauthor ");
+               if (!a)
+                       die("invalid commit: %s", use_message);
+
+               lb = strstr(a + 8, " <");
+               rb = strstr(a + 8, "> ");
+               eol = strchr(a + 8, '\n');
+               if (!lb || !rb || !eol)
+                       die("invalid commit: %s", use_message);
+
+               name = xstrndup(a + 8, lb - (a + 8));
+               email = xstrndup(lb + 2, rb - (lb + 2));
+               date = xstrndup(rb + 2, eol - (rb + 2));
+       }
+
+       if (force_author) {
+               const char *lb = strstr(force_author, " <");
+               const char *rb = strchr(force_author, '>');
+
+               if (!lb || !rb)
+                       die("malformed --author parameter");
+               name = xstrndup(force_author, lb - force_author);
+               email = xstrndup(lb + 2, rb - (lb + 2));
+       }
+
+       author_name = name;
+       author_email = email;
+       author_date = date;
+}
+
 static int prepare_to_commit(const char *index_file, const char *prefix)
 {
        struct stat statbuf;
@@ -404,6 +449,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
        FILE *fp;
        const char *hook_arg1 = NULL;
        const char *hook_arg2 = NULL;
+       int ident_shown = 0;
 
        if (!no_verify && run_hook(index_file, "pre-commit", NULL))
                return 0;
@@ -483,7 +529,14 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
 
        strbuf_release(&sb);
 
+       determine_author_info();
+
+       /* This checks if committer ident is explicitly given */
+       git_committer_info(0);
        if (use_editor) {
+               char *author_ident;
+               const char *committer_ident;
+
                if (in_merge)
                        fprintf(fp,
                                "#\n"
@@ -506,6 +559,27 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                if (only_include_assumed)
                        fprintf(fp, "# %s\n", only_include_assumed);
 
+               author_ident = xstrdup(fmt_name(author_name, author_email));
+               committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"),
+                                          getenv("GIT_COMMITTER_EMAIL"));
+               if (strcmp(author_ident, committer_ident))
+                       fprintf(fp,
+                               "%s"
+                               "# Author:    %s\n",
+                               ident_shown++ ? "" : "#\n",
+                               author_ident);
+               free(author_ident);
+
+               if (!user_ident_explicitly_given)
+                       fprintf(fp,
+                               "%s"
+                               "# Committer: %s\n",
+                               ident_shown++ ? "" : "#\n",
+                               committer_ident);
+
+               if (ident_shown)
+                       fprintf(fp, "#\n");
+
                saved_color_setting = wt_status_use_color;
                wt_status_use_color = 0;
                commitable = run_status(fp, index_file, prefix, 1);
@@ -622,45 +696,6 @@ static int message_is_empty(struct strbuf *sb, int start)
        return 1;
 }
 
-static void determine_author_info(struct strbuf *sb)
-{
-       char *name, *email, *date;
-
-       name = getenv("GIT_AUTHOR_NAME");
-       email = getenv("GIT_AUTHOR_EMAIL");
-       date = getenv("GIT_AUTHOR_DATE");
-
-       if (use_message) {
-               const char *a, *lb, *rb, *eol;
-
-               a = strstr(use_message_buffer, "\nauthor ");
-               if (!a)
-                       die("invalid commit: %s", use_message);
-
-               lb = strstr(a + 8, " <");
-               rb = strstr(a + 8, "> ");
-               eol = strchr(a + 8, '\n');
-               if (!lb || !rb || !eol)
-                       die("invalid commit: %s", use_message);
-
-               name = xstrndup(a + 8, lb - (a + 8));
-               email = xstrndup(lb + 2, rb - (lb + 2));
-               date = xstrndup(rb + 2, eol - (rb + 2));
-       }
-
-       if (force_author) {
-               const char *lb = strstr(force_author, " <");
-               const char *rb = strchr(force_author, '>');
-
-               if (!lb || !rb)
-                       die("malformed --author parameter");
-               name = xstrndup(force_author, lb - force_author);
-               email = xstrndup(lb + 2, rb - (lb + 2));
-       }
-
-       strbuf_addf(sb, "author %s\n", fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME));
-}
-
 static int parse_and_validate_options(int argc, const char *argv[],
                                      const char * const usage[])
 {
@@ -920,7 +955,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1));
        }
 
-       determine_author_info(&sb);
+       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);
index 167f948036170fd2ec5a8e3bd3ca20359d8c9ee5..f6584ecea165704208059b632bb1a1ac2b6a6d1f 100644 (file)
@@ -360,12 +360,10 @@ static int store_updated_refs(const char *url, struct ref *ref_map)
 
                if (ref)
                        update_local_ref(ref, what, verbose, note);
-               else if (verbose)
+               else
                        sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
                                SUMMARY_WIDTH, *kind ? kind : "branch",
                                 REFCOL_WIDTH, *what ? what : "HEAD");
-               else
-                       *note = '\0';
                if (*note) {
                        if (!shown_url) {
                                fprintf(stderr, "From %.*s\n",
@@ -510,10 +508,8 @@ static void find_non_local_tags(struct transport *transport,
                     will_fetch(head, ref->old_sha1))) {
                        path_list_insert(ref_name, &new_refs);
 
-                       rm = alloc_ref(strlen(ref_name) + 1);
-                       strcpy(rm->name, ref_name);
-                       rm->peer_ref = alloc_ref(strlen(ref_name) + 1);
-                       strcpy(rm->peer_ref->name, ref_name);
+                       rm = alloc_ref_from_str(ref_name);
+                       rm->peer_ref = alloc_ref_from_str(ref_name);
                        hashcpy(rm->old_sha1, ref_sha1);
 
                        **tail = rm;
index 7077d524776e748e0add4ff85478cd62512add15..b72cb59e6a6aab33e7170826242d82785e4fa1e4 100644 (file)
@@ -6,13 +6,18 @@
 #include "tag.h"
 
 static const char *fmt_merge_msg_usage =
-       "git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
+       "git-fmt-merge-msg [--log] [--no-log] [--file <file>]";
 
 static int merge_summary;
 
 static int fmt_merge_msg_config(const char *key, const char *value)
 {
-       if (!strcmp("merge.summary", key))
+       static int found_merge_log = 0;
+       if (!strcmp("merge.log", key)) {
+               found_merge_log = 1;
+               merge_summary = git_config_bool(key, value);
+       }
+       if (!found_merge_log && !strcmp("merge.summary", key))
                merge_summary = git_config_bool(key, value);
        return 0;
 }
@@ -258,9 +263,10 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        git_config(fmt_merge_msg_config);
 
        while (argc > 1) {
-               if (!strcmp(argv[1], "--summary"))
+               if (!strcmp(argv[1], "--log") || !strcmp(argv[1], "--summary"))
                        merge_summary = 1;
-               else if (!strcmp(argv[1], "--no-summary"))
+               else if (!strcmp(argv[1], "--no-log")
+                               || !strcmp(argv[1], "--no-summary"))
                        merge_summary = 0;
                else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
                        if (argc < 3)
index f99ebc7926b95b04f1140d257da7a52bfc33ee50..48f7d95f9784d27f023b22343929f5ad188caa4c 100644 (file)
@@ -219,7 +219,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        char buf[80];
 
        struct option builtin_gc_options[] = {
-               OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
+               OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
                OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
                OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
                OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
@@ -249,24 +249,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                /*
                 * Auto-gc should be least intrusive as possible.
                 */
-               prune = 0;
                if (!need_to_gc())
                        return 0;
                fprintf(stderr, "Auto packing your repository for optimum "
                        "performance. You may also\n"
                        "run \"git gc\" manually. See "
                        "\"git help gc\" for more information.\n");
-       } else {
-               /*
-                * Use safer (for shared repos) "-A" option to
-                * repack when not pruning. Auto-gc makes its
-                * own decision.
-                */
-               if (prune)
-                       append_option(argv_repack, "-a", MAX_ADD);
-               else
-                       append_option(argv_repack, "-A", MAX_ADD);
-       }
+       } else
+               append_option(argv_repack, "-A", MAX_ADD);
 
        if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
                return error(FAILED_RUN, argv_pack_refs[0]);
index a76f5d3474f9d1e16bfc988ad6fd1d971876fe91..b061317275792ccb33eea80a5071b72ed6f8d6a7 100644 (file)
@@ -255,8 +255,8 @@ static int create_default_files(const char *git_dir, const char *template_path)
                        git_config_set("core.worktree", work_tree);
        }
 
-       /* Check if symlink is supported in the work tree */
        if (!reinit) {
+               /* Check if symlink is supported in the work tree */
                path[len] = 0;
                strcpy(path + len, "tXXXXXX");
                if (!close(xmkstemp(path)) &&
@@ -267,6 +267,12 @@ static int create_default_files(const char *git_dir, const char *template_path)
                        unlink(path); /* good */
                else
                        git_config_set("core.symlinks", "false");
+
+               /* Check if the filesystem is case-insensitive */
+               path[len] = 0;
+               strcpy(path + len, "CoNfIg");
+               if (!access(path, F_OK))
+                       git_config_set("core.ignorecase", "true");
        }
 
        return reinit;
index 256bbac93a3927adfbd12b124f9c838a4101120d..543855b7ad11bd0355d88bc1502030509875e0c7 100644 (file)
@@ -18,6 +18,9 @@
 #include "run-command.h"
 #include "shortlog.h"
 
+/* Set a default date-time format for git log ("log.date" config variable) */
+static const char *default_date_mode = NULL;
+
 static int default_show_root = 1;
 static const char *fmt_patch_subject_prefix = "PATCH";
 static const char *fmt_pretty;
@@ -61,7 +64,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
        DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
        rev->show_root_diff = default_show_root;
        rev->subject_prefix = fmt_patch_subject_prefix;
+
+       if (default_date_mode)
+               rev->date_mode = parse_date_format(default_date_mode);
+
        argc = setup_revisions(argc, argv, rev, "HEAD");
+
        if (rev->diffopt.pickaxe || rev->diffopt.filter)
                rev->always_show_header = 0;
        if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) {
@@ -232,6 +240,8 @@ static int git_log_config(const char *var, const char *value)
                fmt_patch_subject_prefix = xstrdup(value);
                return 0;
        }
+       if (!strcmp(var, "log.date"))
+               return git_config_string(&default_date_mode, var, value);
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
                return 0;
@@ -485,6 +495,13 @@ static int git_format_config(const char *var, const char *value)
                fmt_patch_suffix = xstrdup(value);
                return 0;
        }
+       if (!strcmp(var, "format.cc")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               ALLOC_GROW(extra_cc, extra_cc_nr + 1, extra_cc_alloc);
+               extra_cc[extra_cc_nr++] = xstrdup(value);
+               return 0;
+       }
        if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
                return 0;
        }
@@ -757,6 +774,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        int thread = 0;
        int cover_letter = 0;
        int boundary_count = 0;
+       int no_binary_diff = 0;
        struct commit *origin = NULL, *head = NULL;
        const char *in_reply_to = NULL;
        struct patch_ids ids;
@@ -862,6 +880,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                        fmt_patch_suffix = argv[i] + 9;
                else if (!strcmp(argv[i], "--cover-letter"))
                        cover_letter = 1;
+               else if (!strcmp(argv[i], "--no-binary"))
+                       no_binary_diff = 1;
                else
                        argv[j++] = argv[i];
        }
@@ -914,7 +934,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (!rev.diffopt.output_format)
                rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
 
-       if (!DIFF_OPT_TST(&rev.diffopt, TEXT))
+       if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
                DIFF_OPT_SET(&rev.diffopt, BINARY);
 
        if (!output_directory && !use_stdout)
index 910c0d20e7ba1128c705a49bfd9966212c5420b2..46e636fdcf1cb854a486e425df05a2485d18d5d7 100644 (file)
@@ -92,7 +92,8 @@ static struct path_list current_directory_set = {NULL, 0, 0, 1};
 
 static int call_depth = 0;
 static int verbosity = 2;
-static int rename_limit = -1;
+static int diff_rename_limit = -1;
+static int merge_rename_limit = -1;
 static int buffer_output = 1;
 static struct strbuf obuf = STRBUF_INIT;
 
@@ -361,7 +362,10 @@ static struct path_list *get_renames(struct tree *tree,
        diff_setup(&opts);
        DIFF_OPT_SET(&opts, RECURSIVE);
        opts.detect_rename = DIFF_DETECT_RENAME;
-       opts.rename_limit = rename_limit;
+       opts.rename_limit = merge_rename_limit >= 0 ? merge_rename_limit :
+                           diff_rename_limit >= 0 ? diff_rename_limit :
+                           500;
+       opts.warn_on_too_large_rename = 1;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        if (diff_setup_done(&opts) < 0)
                die("diff setup failed");
@@ -1343,7 +1347,11 @@ static int merge_config(const char *var, const char *value)
                return 0;
        }
        if (!strcasecmp(var, "diff.renamelimit")) {
-               rename_limit = git_config_int(var, value);
+               diff_rename_limit = git_config_int(var, value);
+               return 0;
+       }
+       if (!strcasecmp(var, "merge.renamelimit")) {
+               merge_rename_limit = git_config_int(var, value);
                return 0;
        }
        return git_default_config(var, value);
index 94f6dd2aad2da512bab0be2e84859eced6553cc9..fb8ffb41aaba5f6e2a8cc13207a2f2c407d6c91d 100644 (file)
@@ -256,7 +256,8 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 
                for (i = 0; i < added.nr; i++) {
                        const char *path = added.items[i].path;
-                       add_file_to_cache(path, verbose);
+                       if (add_file_to_cache(path, verbose))
+                               die("updating index entries failed");
                }
 
                for (i = 0; i < deleted.nr; i++)
index 777f272668c39931b330be555f41399adfbce397..f43eb67016ee719d34d2e2f4ae8d77e1ec74b396 100644 (file)
@@ -28,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
        [--window=N] [--window-memory=N] [--depth=N] \n\
        [--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
        [--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
-       [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
+       [--stdout | base-name] [--include-tag] \n\
+       [--keep-unreachable | --unpack-unreachable] \n\
        [<ref-list | <object-list]";
 
 struct object_entry {
@@ -43,6 +44,7 @@ struct object_entry {
                                             */
        void *delta_data;       /* cached delta (uncompressed) */
        unsigned long delta_size;       /* delta data size (uncompressed) */
+       unsigned long z_delta_size;     /* delta data size (compressed) */
        unsigned int hash;      /* name hint hash */
        enum object_type type;
        enum object_type in_pack_type;  /* could be delta */
@@ -65,7 +67,8 @@ static struct pack_idx_entry **written_list;
 static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
 
 static int non_empty;
-static int no_reuse_delta, no_reuse_object, keep_unreachable, include_tag;
+static int reuse_delta = 1, reuse_object = 1;
+static int keep_unreachable, unpack_unreachable, include_tag;
 static int local;
 static int incremental;
 static int allow_ofs_delta;
@@ -102,24 +105,53 @@ static uint32_t written, written_delta;
 static uint32_t reused, reused_delta;
 
 
-static void *delta_against(void *buf, unsigned long size, struct object_entry *entry)
+static void *get_delta(struct object_entry *entry)
 {
-       unsigned long othersize, delta_size;
+       unsigned long size, base_size, delta_size;
+       void *buf, *base_buf, *delta_buf;
        enum object_type type;
-       void *otherbuf = read_sha1_file(entry->delta->idx.sha1, &type, &othersize);
-       void *delta_buf;
 
-       if (!otherbuf)
+       buf = read_sha1_file(entry->idx.sha1, &type, &size);
+       if (!buf)
+               die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+       base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
+       if (!base_buf)
                die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
-        delta_buf = diff_delta(otherbuf, othersize,
+       delta_buf = diff_delta(base_buf, base_size,
                               buf, size, &delta_size, 0);
-        if (!delta_buf || delta_size != entry->delta_size)
+       if (!delta_buf || delta_size != entry->delta_size)
                die("delta size changed");
-        free(buf);
-        free(otherbuf);
+       free(buf);
+       free(base_buf);
        return delta_buf;
 }
 
+static unsigned long do_compress(void **pptr, unsigned long size)
+{
+       z_stream stream;
+       void *in, *out;
+       unsigned long maxsize;
+
+       memset(&stream, 0, sizeof(stream));
+       deflateInit(&stream, pack_compression_level);
+       maxsize = deflateBound(&stream, size);
+
+       in = *pptr;
+       out = xmalloc(maxsize);
+       *pptr = out;
+
+       stream.next_in = in;
+       stream.avail_in = size;
+       stream.next_out = out;
+       stream.avail_out = maxsize;
+       while (deflate(&stream, Z_FINISH) == Z_OK)
+               ; /* nothing */
+       deflateEnd(&stream);
+
+       free(in);
+       return stream.total_out;
+}
+
 /*
  * The per-object header is a pretty dense thing, which is
  *  - first byte: low four bits are "size", then three bits of "type",
@@ -222,42 +254,42 @@ static unsigned long write_object(struct sha1file *f,
                                  struct object_entry *entry,
                                  off_t write_offset)
 {
-       unsigned long size;
-       enum object_type type;
+       unsigned long size, limit, datalen;
        void *buf;
-       unsigned char header[10];
-       unsigned char dheader[10];
+       unsigned char header[10], dheader[10];
        unsigned hdrlen;
-       off_t datalen;
-       enum object_type obj_type;
-       int to_reuse = 0;
-       /* write limit if limited packsize and not first object */
-       unsigned long limit = pack_size_limit && nr_written ?
-                               pack_size_limit - write_offset : 0;
-                               /* no if no delta */
-       int usable_delta =      !entry->delta ? 0 :
-                               /* yes if unlimited packfile */
-                               !pack_size_limit ? 1 :
-                               /* no if base written to previous pack */
-                               entry->delta->idx.offset == (off_t)-1 ? 0 :
-                               /* otherwise double-check written to this
-                                * pack,  like we do below
-                                */
-                               entry->delta->idx.offset ? 1 : 0;
+       enum object_type type;
+       int usable_delta, to_reuse;
 
        if (!pack_to_stdout)
                crc32_begin(f);
 
-       obj_type = entry->type;
-       if (no_reuse_object)
+       type = entry->type;
+
+       /* write limit if limited packsize and not first object */
+       limit = pack_size_limit && nr_written ?
+                       pack_size_limit - write_offset : 0;
+
+       if (!entry->delta)
+               usable_delta = 0;       /* no delta */
+       else if (!pack_size_limit)
+              usable_delta = 1;        /* unlimited packfile */
+       else if (entry->delta->idx.offset == (off_t)-1)
+               usable_delta = 0;       /* base was written to another pack */
+       else if (entry->delta->idx.offset)
+               usable_delta = 1;       /* base already exists in this pack */
+       else
+               usable_delta = 0;       /* base could end up in another pack */
+
+       if (!reuse_object)
                to_reuse = 0;   /* explicit */
        else if (!entry->in_pack)
                to_reuse = 0;   /* can't reuse what we don't have */
-       else if (obj_type == OBJ_REF_DELTA || obj_type == OBJ_OFS_DELTA)
+       else if (type == OBJ_REF_DELTA || type == OBJ_OFS_DELTA)
                                /* check_object() decided it for us ... */
                to_reuse = usable_delta;
                                /* ... but pack split may override that */
-       else if (obj_type != entry->in_pack_type)
+       else if (type != entry->in_pack_type)
                to_reuse = 0;   /* pack has delta which is unusable */
        else if (entry->delta)
                to_reuse = 0;   /* we want to pack afresh */
@@ -267,50 +299,42 @@ static unsigned long write_object(struct sha1file *f,
                                 */
 
        if (!to_reuse) {
-               z_stream stream;
-               unsigned long maxsize;
-               void *out;
                if (!usable_delta) {
-                       buf = read_sha1_file(entry->idx.sha1, &obj_type, &size);
+                       buf = read_sha1_file(entry->idx.sha1, &type, &size);
                        if (!buf)
                                die("unable to read %s", sha1_to_hex(entry->idx.sha1));
+                       /*
+                        * make sure no cached delta data remains from a
+                        * previous attempt before a pack split occured.
+                        */
+                       free(entry->delta_data);
+                       entry->delta_data = NULL;
+                       entry->z_delta_size = 0;
                } else if (entry->delta_data) {
                        size = entry->delta_size;
                        buf = entry->delta_data;
                        entry->delta_data = NULL;
-                       obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+                       type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                } else {
-                       buf = read_sha1_file(entry->idx.sha1, &type, &size);
-                       if (!buf)
-                               die("unable to read %s", sha1_to_hex(entry->idx.sha1));
-                       buf = delta_against(buf, size, entry);
+                       buf = get_delta(entry);
                        size = entry->delta_size;
-                       obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+                       type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                }
-               /* compress the data to store and put compressed length in datalen */
-               memset(&stream, 0, sizeof(stream));
-               deflateInit(&stream, pack_compression_level);
-               maxsize = deflateBound(&stream, size);
-               out = xmalloc(maxsize);
-               /* Compress it */
-               stream.next_in = buf;
-               stream.avail_in = size;
-               stream.next_out = out;
-               stream.avail_out = maxsize;
-               while (deflate(&stream, Z_FINISH) == Z_OK)
-                       /* nothing */;
-               deflateEnd(&stream);
-               datalen = stream.total_out;
+
+               if (entry->z_delta_size)
+                       datalen = entry->z_delta_size;
+               else
+                       datalen = do_compress(&buf, size);
 
                /*
                 * The object header is a byte of 'type' followed by zero or
                 * more bytes of length.
                 */
-               hdrlen = encode_header(obj_type, size, header);
+               hdrlen = encode_header(type, size, header);
 
-               if (obj_type == OBJ_OFS_DELTA) {
+               if (type == OBJ_OFS_DELTA) {
                        /*
                         * Deltas with relative base contain an additional
                         * encoding of the relative offset for the delta
@@ -322,20 +346,18 @@ static unsigned long write_object(struct sha1file *f,
                        while (ofs >>= 7)
                                dheader[--pos] = 128 | (--ofs & 127);
                        if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
-                               free(out);
                                free(buf);
                                return 0;
                        }
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
-               } else if (obj_type == OBJ_REF_DELTA) {
+               } else if (type == OBJ_REF_DELTA) {
                        /*
                         * Deltas with a base reference contain
                         * an additional 20 bytes for the base sha1.
                         */
                        if (limit && hdrlen + 20 + datalen + 20 >= limit) {
-                               free(out);
                                free(buf);
                                return 0;
                        }
@@ -344,14 +366,12 @@ static unsigned long write_object(struct sha1file *f,
                        hdrlen += 20;
                } else {
                        if (limit && hdrlen + datalen + 20 >= limit) {
-                               free(out);
                                free(buf);
                                return 0;
                        }
                        sha1write(f, header, hdrlen);
                }
-               sha1write(f, out, datalen);
-               free(out);
+               sha1write(f, buf, datalen);
                free(buf);
        }
        else {
@@ -361,11 +381,11 @@ static unsigned long write_object(struct sha1file *f,
                off_t offset;
 
                if (entry->delta) {
-                       obj_type = (allow_ofs_delta && entry->delta->idx.offset) ?
+                       type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
                        reused_delta++;
                }
-               hdrlen = encode_header(obj_type, entry->size, header);
+               hdrlen = encode_header(type, entry->size, header);
                offset = entry->in_pack_offset;
                revidx = find_pack_revindex(p, offset);
                datalen = revidx[1].offset - offset;
@@ -374,7 +394,7 @@ static unsigned long write_object(struct sha1file *f,
                        die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
                offset += entry->in_pack_header_size;
                datalen -= entry->in_pack_header_size;
-               if (obj_type == OBJ_OFS_DELTA) {
+               if (type == OBJ_OFS_DELTA) {
                        off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
@@ -385,7 +405,7 @@ static unsigned long write_object(struct sha1file *f,
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
-               } else if (obj_type == OBJ_REF_DELTA) {
+               } else if (type == OBJ_REF_DELTA) {
                        if (limit && hdrlen + 20 + datalen + 20 >= limit)
                                return 0;
                        sha1write(f, header, hdrlen);
@@ -452,11 +472,10 @@ static void write_pack_file(void)
        struct sha1file *f;
        off_t offset, offset_one, last_obj_offset = 0;
        struct pack_header hdr;
-       int do_progress = progress >> pack_to_stdout;
        uint32_t nr_remaining = nr_result;
        time_t last_mtime = 0;
 
-       if (do_progress)
+       if (progress > pack_to_stdout)
                progress_state = start_progress("Writing objects", nr_result);
        written_list = xmalloc(nr_objects * sizeof(*written_list));
 
@@ -1022,7 +1041,7 @@ static void check_object(struct object_entry *entry)
                        unuse_pack(&w_curs);
                        return;
                case OBJ_REF_DELTA:
-                       if (!no_reuse_delta && !entry->preferred_base)
+                       if (reuse_delta && !entry->preferred_base)
                                base_ref = use_pack(p, &w_curs,
                                                entry->in_pack_offset + used, NULL);
                        entry->in_pack_header_size = used + 20;
@@ -1045,7 +1064,7 @@ static void check_object(struct object_entry *entry)
                                die("delta base offset out of bound for %s",
                                    sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
-                       if (!no_reuse_delta && !entry->preferred_base) {
+                       if (reuse_delta && !entry->preferred_base) {
                                struct revindex_entry *revidx;
                                revidx = find_pack_revindex(p, ofs);
                                base_ref = nth_packed_object_sha1(p, revidx->nr);
@@ -1233,7 +1252,7 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
         * We do not bother to try a delta that we discarded
         * on an earlier try, but only when reusing delta data.
         */
-       if (!no_reuse_delta && trg_entry->in_pack &&
+       if (reuse_delta && trg_entry->in_pack &&
            trg_entry->in_pack == src_entry->in_pack &&
            trg_entry->in_pack_type != OBJ_REF_DELTA &&
            trg_entry->in_pack_type != OBJ_OFS_DELTA)
@@ -1441,11 +1460,34 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
                                best_base = other_idx;
                }
 
+               /*
+                * If we decided to cache the delta data, then it is best
+                * to compress it right away.  First because we have to do
+                * it anyway, and doing it here while we're threaded will
+                * save a lot of time in the non threaded write phase,
+                * as well as allow for caching more deltas within
+                * the same cache size limit.
+                * ...
+                * But only if not writing to stdout, since in that case
+                * the network is most likely throttling writes anyway,
+                * and therefore it is best to go to the write phase ASAP
+                * instead, as we can afford spending more time compressing
+                * between writes at that moment.
+                */
+               if (entry->delta_data && !pack_to_stdout) {
+                       entry->z_delta_size = do_compress(&entry->delta_data,
+                                                         entry->delta_size);
+                       cache_lock();
+                       delta_cache_size -= entry->delta_size;
+                       delta_cache_size += entry->z_delta_size;
+                       cache_unlock();
+               }
+
                /* if we made n a delta, and if n is already at max
                 * depth, leaving it in the window is pointless.  we
                 * should evict it first.
                 */
-               if (entry->delta && depth <= n->depth)
+               if (entry->delta && max_depth <= n->depth)
                        continue;
 
                /*
@@ -1688,7 +1730,7 @@ static void prepare_pack(int window, int depth)
 
                if (entry->delta)
                        /* This happens if we decided to reuse existing
-                        * delta from a pack.  "!no_reuse_delta &&" is implied.
+                        * delta from a pack.  "reuse_delta &&" is implied.
                         */
                        continue;
 
@@ -1905,6 +1947,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        free(in_pack.array);
 }
 
+static void loosen_unused_packed_objects(struct rev_info *revs)
+{
+       struct packed_git *p;
+       uint32_t i;
+       const unsigned char *sha1;
+
+       for (p = packed_git; p; p = p->next) {
+               for (i = 0; i < revs->num_ignore_packed; i++) {
+                       if (matches_pack_name(p, revs->ignore_packed[i]))
+                               break;
+               }
+               if (revs->num_ignore_packed <= i)
+                       continue;
+
+               if (open_pack_index(p))
+                       die("cannot open pack index");
+
+               for (i = 0; i < p->num_objects; i++) {
+                       sha1 = nth_packed_object_sha1(p, i);
+                       if (!locate_object_entry(sha1))
+                               if (force_object_loose(sha1, p->mtime))
+                                       die("unable to force loose object");
+               }
+       }
+}
+
 static void get_object_list(int ac, const char **av)
 {
        struct rev_info revs;
@@ -1939,6 +2007,8 @@ static void get_object_list(int ac, const char **av)
 
        if (keep_unreachable)
                add_objects_in_unpacked_packs(&revs);
+       if (unpack_unreachable)
+               loosen_unused_packed_objects(&revs);
 }
 
 static int adjust_perm(const char *path, mode_t mode)
@@ -2050,11 +2120,11 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        continue;
                }
                if (!strcmp("--no-reuse-delta", arg)) {
-                       no_reuse_delta = 1;
+                       reuse_delta = 0;
                        continue;
                }
                if (!strcmp("--no-reuse-object", arg)) {
-                       no_reuse_object = no_reuse_delta = 1;
+                       reuse_object = reuse_delta = 0;
                        continue;
                }
                if (!strcmp("--delta-base-offset", arg)) {
@@ -2073,6 +2143,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        keep_unreachable = 1;
                        continue;
                }
+               if (!strcmp("--unpack-unreachable", arg)) {
+                       unpack_unreachable = 1;
+                       continue;
+               }
                if (!strcmp("--include-tag", arg)) {
                        include_tag = 1;
                        continue;
@@ -2138,6 +2212,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (!pack_to_stdout && thin)
                die("--thin cannot be used to build an indexable pack.");
 
+       if (keep_unreachable && unpack_unreachable)
+               die("--keep-unreachable and --unpack-unreachable are incompatible.");
+
 #ifdef THREADED_DELTA_SEARCH
        if (!delta_search_threads)      /* --threads=0 means autodetect */
                delta_search_threads = online_cpus();
index b68c6813b8c71e0e00790eb422e57f8e219095fb..b35aad68e9154bb755c51323106c738ab4fc61e9 100644 (file)
@@ -56,6 +56,17 @@ static int do_push(const char *repo, int flags)
        if (!remote)
                die("bad repository '%s'", repo);
 
+       if (remote->mirror)
+               flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
+
+       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
+               return -1;
+
+       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
+                               (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
+               return error("--all and --mirror are incompatible");
+       }
+
        if (!refspec
                && !(flags & TRANSPORT_PUSH_ALL)
                && remote->push_refspec_nr) {
@@ -95,6 +106,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int dry_run = 0;
        int force = 0;
        int tags = 0;
+       int rc;
        const char *repo = NULL;        /* default repository */
 
        struct option options[] = {
@@ -130,14 +142,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                repo = argv[0];
                set_refspecs(argv + 1, argc - 1);
        }
-       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
-               usage_with_options(push_usage, options);
 
-       if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
-                               (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
-               error("--all and --mirror are incompatible");
+       rc = do_push(repo, flags);
+       if (rc == -1)
                usage_with_options(push_usage, options);
-       }
-
-       return do_push(repo, flags);
+       else
+               return rc;
 }
index e9cfd2bbc5539ee0c9c048798383b837ff63991b..7ac30883bc72c51b0227828bef5758751e524f65 100644 (file)
@@ -40,7 +40,7 @@ static int read_cache_unmerged(void)
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
                if (ce_stage(ce)) {
-                       remove_index_entry(ce);
+                       remove_name_hash(ce);
                        if (last && !strcmp(ce->name, last->name))
                                continue;
                        cache_tree_invalidate_path(active_cache_tree, ce->name);
index 93bb84e1d4c762c6c049276202ebc2320447e9c8..8b63619ef08a2ac3d96000908fe4986396ddd6a2 100644 (file)
@@ -118,6 +118,13 @@ static int add(int argc, const char **argv)
                        return 1;
        }
 
+       if (mirror) {
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "remote.%s.mirror", name);
+               if (git_config_set(buf.buf, "yes"))
+                       return 1;
+       }
+
        if (fetch && fetch_remote(name))
                return 1;
 
index edc0bd35bb0e96a230284250dc4c40776cf7caac..54d55cc3a33e55d5c3021eae14b7a771b4e1c23f 100644 (file)
@@ -10,6 +10,7 @@
 #include "list-objects.h"
 #include "builtin.h"
 #include "log-tree.h"
+#include "graph.h"
 
 /* bits #0-15 in revision.h */
 
@@ -58,6 +59,8 @@ static const char *header_prefix;
 static void finish_commit(struct commit *commit);
 static void show_commit(struct commit *commit)
 {
+       graph_show_commit(revs.graph);
+
        if (show_timestamp)
                printf("%lu ", commit->date);
        if (header_prefix)
@@ -77,7 +80,7 @@ static void show_commit(struct commit *commit)
                      stdout);
        else
                fputs(sha1_to_hex(commit->object.sha1), stdout);
-       if (revs.parents) {
+       if (revs.print_parents) {
                struct commit_list *parents = commit->parents;
                while (parents) {
                        printf(" %s", sha1_to_hex(parents->item->object.sha1));
@@ -96,9 +99,48 @@ static void show_commit(struct commit *commit)
                pretty_print_commit(revs.commit_format, commit,
                                    &buf, revs.abbrev, NULL, NULL,
                                    revs.date_mode, 0);
-               if (buf.len)
-                       printf("%s%c", buf.buf, hdr_termination);
+               if (revs.graph) {
+                       if (buf.len) {
+                               if (revs.commit_format != CMIT_FMT_ONELINE)
+                                       graph_show_oneline(revs.graph);
+
+                               graph_show_commit_msg(revs.graph, &buf);
+
+                               /*
+                                * Add a newline after the commit message.
+                                *
+                                * Usually, this newline produces a blank
+                                * padding line between entries, in which case
+                                * we need to add graph padding on this line.
+                                *
+                                * However, the commit message may not end in a
+                                * newline.  In this case the newline simply
+                                * ends the last line of the commit message,
+                                * and we don't need any graph output.  (This
+                                * always happens with CMIT_FMT_ONELINE, and it
+                                * happens with CMIT_FMT_USERFORMAT when the
+                                * format doesn't explicitly end in a newline.)
+                                */
+                               if (buf.len && buf.buf[buf.len - 1] == '\n')
+                                       graph_show_padding(revs.graph);
+                               putchar('\n');
+                       } else {
+                               /*
+                                * If the message buffer is empty, just show
+                                * the rest of the graph output for this
+                                * commit.
+                                */
+                               if (graph_show_remainder(revs.graph))
+                                       putchar('\n');
+                       }
+               } else {
+                       if (buf.len)
+                               printf("%s%c", buf.buf, hdr_termination);
+               }
                strbuf_release(&buf);
+       } else {
+               if (graph_show_remainder(revs.graph))
+                       putchar('\n');
        }
        maybe_flush_or_die(stdout, "stdout");
        finish_commit(commit);
index 0e597073239c97160582fbb7047794796f67987f..ab3e85054e174d2d328c625f1db744854b62f5f7 100644 (file)
@@ -28,8 +28,6 @@ static int symbolic;
 static int abbrev;
 static int output_sq;
 
-static int revs_count;
-
 /*
  * Some arguments are relevant "revision" arguments,
  * others are about output format or other details.
@@ -96,16 +94,21 @@ static void show(const char *arg)
                puts(arg);
 }
 
+/* Like show(), but with a negation prefix according to type */
+static void show_with_type(int type, const char *arg)
+{
+       if (type != show_type)
+               putchar('^');
+       show(arg);
+}
+
 /* Output a revision, only if filter allows it */
 static void show_rev(int type, const unsigned char *sha1, const char *name)
 {
        if (!(filter & DO_REVS))
                return;
        def = NULL;
-       revs_count++;
 
-       if (type != show_type)
-               putchar('^');
        if (symbolic && name) {
                if (symbolic == SHOW_SYMBOLIC_FULL) {
                        unsigned char discard[20];
@@ -122,20 +125,20 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
                                 */
                                break;
                        case 1: /* happy */
-                               show(full);
+                               show_with_type(type, full);
                                break;
                        default: /* ambiguous */
                                error("refname '%s' is ambiguous", name);
                                break;
                        }
                } else {
-                       show(name);
+                       show_with_type(type, name);
                }
        }
        else if (abbrev)
-               show(find_unique_abbrev(sha1, abbrev));
+               show_with_type(type, find_unique_abbrev(sha1, abbrev));
        else
-               show(sha1_to_hex(sha1));
+               show_with_type(type, sha1_to_hex(sha1));
 }
 
 /* Output a flag, only if filter allows it. */
@@ -150,7 +153,7 @@ static int show_flag(const char *arg)
        return 0;
 }
 
-static void show_default(void)
+static int show_default(void)
 {
        const char *s = def;
 
@@ -160,9 +163,10 @@ static void show_default(void)
                def = NULL;
                if (!get_sha1(s, sha1)) {
                        show_rev(NORMAL, sha1, s);
-                       return;
+                       return 1;
                }
        }
+       return 0;
 }
 
 static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
@@ -375,8 +379,9 @@ static void die_no_single_rev(int quiet)
 
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
-       int i, as_is = 0, verify = 0, quiet = 0;
+       int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
        unsigned char sha1[20];
+       const char *name = NULL;
 
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
                return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -568,12 +573,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                /* Not a flag argument */
                if (try_difference(arg))
                        continue;
-               if (!get_sha1(arg, sha1)) {
-                       show_rev(NORMAL, sha1, arg);
-                       continue;
+               name = arg;
+               type = NORMAL;
+               if (*arg == '^') {
+                       name++;
+                       type = REVERSED;
                }
-               if (*arg == '^' && !get_sha1(arg+1, sha1)) {
-                       show_rev(REVERSED, sha1, arg+1);
+               if (!get_sha1(name, sha1)) {
+                       if (verify)
+                               revs_count++;
+                       else
+                               show_rev(type, sha1, name);
                        continue;
                }
                if (verify)
@@ -583,8 +593,14 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        continue;
                verify_filename(prefix, arg);
        }
-       show_default();
-       if (verify && revs_count != 1)
+       if (verify) {
+               if (revs_count == 1) {
+                       show_rev(type, sha1, name);
+                       return 0;
+               } else if (revs_count == 0 && show_default())
+                       return 0;
                die_no_single_rev(quiet);
+       } else
+               show_default();
        return 0;
 }
index 607a2f0337c3d3f1fb8bdac7443e3a7f56e92305..2b57525d7234086bdf2022d13fa405e5007627fb 100644 (file)
@@ -33,7 +33,7 @@ static const char * const cherry_pick_usage[] = {
        NULL
 };
 
-static int edit, no_replay, no_commit, mainline;
+static int edit, no_replay, no_commit, mainline, signoff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
 
@@ -53,6 +53,7 @@ static void parse_args(int argc, const char **argv)
                OPT_BOOLEAN('e', "edit", &edit, "edit the commit message"),
                OPT_BOOLEAN('x', NULL, &no_replay, "append commit name when cherry-picking"),
                OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
+               OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
                OPT_END(),
        };
@@ -404,10 +405,19 @@ static int revert_or_cherry_pick(int argc, const char **argv)
         */
 
        if (!no_commit) {
-               if (edit)
-                       return execl_git_cmd("commit", "-n", NULL);
-               else
-                       return execl_git_cmd("commit", "-n", "-F", defmsg, NULL);
+               /* 6 is max possible length of our args array including NULL */
+               const char *args[6];
+               int i = 0;
+               args[i++] = "commit";
+               args[i++] = "-n";
+               if (signoff)
+                       args[i++] = "-s";
+               if (!edit) {
+                       args[i++] = "-F";
+                       args[i++] = defmsg;
+               }
+               args[i] = NULL;
+               return execv_git_cmd(args);
        }
        free(reencoded_message);
 
diff --git a/cache.h b/cache.h
index 5a28dddec931f983a4aeff2ee7129444d07e07aa..8db19cc253dda9b63401f7b55ffbc0d333e216a8 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -133,6 +133,7 @@ struct cache_entry {
 #define CE_UPDATE    (0x10000)
 #define CE_REMOVE    (0x20000)
 #define CE_UPTODATE  (0x40000)
+#define CE_ADDED     (0x80000)
 
 #define CE_HASHED    (0x100000)
 #define CE_UNHASHED  (0x200000)
@@ -153,20 +154,6 @@ static inline void copy_cache_entry(struct cache_entry *dst, struct cache_entry
        dst->ce_flags = (dst->ce_flags & ~CE_STATE_MASK) | state;
 }
 
-/*
- * We don't actually *remove* it, we can just mark it invalid so that
- * we won't find it in lookups.
- *
- * Not only would we have to search the lists (simple enough), but
- * we'd also have to rehash other hash buckets in case this makes the
- * hash bucket empty (common). So it's much better to just mark
- * it.
- */
-static inline void remove_index_entry(struct cache_entry *ce)
-{
-       ce->ce_flags |= CE_UNHASHED;
-}
-
 static inline unsigned create_ce_flags(size_t len, unsigned stage)
 {
        if (len >= CE_NAMEMASK)
@@ -241,6 +228,23 @@ struct index_state {
 
 extern struct index_state the_index;
 
+/* Name hashing */
+extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
+/*
+ * We don't actually *remove* it, we can just mark it invalid so that
+ * we won't find it in lookups.
+ *
+ * Not only would we have to search the lists (simple enough), but
+ * we'd also have to rehash other hash buckets in case this makes the
+ * hash bucket empty (common). So it's much better to just mark
+ * it.
+ */
+static inline void remove_name_hash(struct cache_entry *ce)
+{
+       ce->ce_flags |= CE_UNHASHED;
+}
+
+
 #ifndef NO_THE_INDEX_COMPATIBILITY_MACROS
 #define active_cache (the_index.cache)
 #define active_nr (the_index.cache_nr)
@@ -257,11 +261,12 @@ extern struct index_state the_index;
 #define add_cache_entry(ce, option) add_index_entry(&the_index, (ce), (option))
 #define remove_cache_entry_at(pos) remove_index_entry_at(&the_index, (pos))
 #define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
+#define add_to_cache(path, st, verbose) add_to_index(&the_index, (path), (st), (verbose))
 #define add_file_to_cache(path, verbose) add_file_to_index(&the_index, (path), (verbose))
 #define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
 #define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
 #define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
-#define cache_name_exists(name, namelen) index_name_exists(&the_index, (name), (namelen))
+#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
 #endif
 
 enum object_type {
@@ -311,6 +316,7 @@ extern char *get_index_file(void);
 extern char *get_graft_file(void);
 extern int set_git_dir(const char *path);
 extern const char *get_git_work_tree(void);
+extern const char *read_gitfile_gently(const char *path);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
@@ -350,7 +356,7 @@ extern int write_index(const struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
-extern int index_name_exists(struct index_state *istate, const char *name, int namelen);
+extern struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int igncase);
 extern int index_name_pos(const struct index_state *, const char *name, int namelen);
 #define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
 #define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
@@ -360,6 +366,7 @@ extern int add_index_entry(struct index_state *, struct cache_entry *ce, int opt
 extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern int remove_file_from_index(struct index_state *, const char *path);
+extern int add_to_index(struct index_state *, const char *path, struct stat *, int verbose);
 extern int add_file_to_index(struct index_state *, const char *path, int verbose);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
@@ -404,6 +411,7 @@ extern int delete_ref(const char *, const unsigned char *sha1);
 extern int trust_executable_bit;
 extern int quote_path_fully;
 extern int has_symlinks;
+extern int ignore_case;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
 extern int log_all_ref_updates;
@@ -433,7 +441,15 @@ enum branch_track {
        BRANCH_TRACK_EXPLICIT,
 };
 
+enum rebase_setup_type {
+       AUTOREBASE_NEVER = 0,
+       AUTOREBASE_LOCAL,
+       AUTOREBASE_REMOTE,
+       AUTOREBASE_ALWAYS,
+};
+
 extern enum branch_track git_branch_track;
+extern enum rebase_setup_type autorebase;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -505,6 +521,7 @@ extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type,
 extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
+extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
@@ -590,7 +607,7 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
-extern int has_symlink_leading_path(const char *name, char *last_symlink);
+extern int has_symlink_leading_path(int len, const char *name);
 
 extern struct alternate_object_database {
        struct alternate_object_database *next;
@@ -634,6 +651,7 @@ struct ref {
        struct ref *next;
        unsigned char old_sha1[20];
        unsigned char new_sha1[20];
+       char *symref;
        unsigned int force:1,
                merge:1,
                nonfastforward:1,
@@ -718,6 +736,7 @@ extern int config_error_nonbool(const char *);
 #define MAX_GITNAME (1000)
 extern char git_default_email[MAX_GITNAME];
 extern char git_default_name[MAX_GITNAME];
+extern int user_ident_explicitly_given;
 
 extern const char *git_commit_encoding;
 extern const char *git_log_output_encoding;
@@ -764,7 +783,13 @@ extern int convert_to_git(const char *path, const char *src, size_t len,
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
 
 /* add */
-void add_files_to_cache(int verbose, const char *prefix, const char **pathspec);
+#define ADD_FILES_VERBOSE      01
+#define ADD_FILES_IGNORE_ERRORS        02
+/*
+ * return 0 if success, 1 - if addition of a file failed and
+ * ADD_FILES_IGNORE_ERRORS was specified in flags
+ */
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
index ccb9e89fa46144744fa530930d8db7644ab2da74..b5ca142fedf2ac0e0cedde1011ab385f65010fdf 100644 (file)
@@ -1,5 +1,16 @@
+/*
+ *  The order of the following two lines is important.
+ *
+ *  FREAD_READS_DIRECTORIES is undefined before including git-compat-util.h
+ *  to avoid the redefinition of fopen within git-compat-util.h. This is
+ *  necessary since fopen is a macro on some platforms which may be set
+ *  based on compiler options. For example, on AIX fopen is set to fopen64
+ *  when _LARGE_FILES is defined. The previous technique of merely undefining
+ *  fopen after including git-compat-util.h is inadequate in this case.
+ */
+#undef FREAD_READS_DIRECTORIES
 #include "../git-compat-util.h"
-#undef fopen
+
 FILE *git_fopen(const char *path, const char *mode)
 {
        FILE *fp;
index b0ada515b9d839fc8691bc9af320353ff323b251..5195de10c9289a25053756ce799e654d5e01483e 100644 (file)
--- a/config.c
+++ b/config.c
@@ -350,6 +350,11 @@ int git_default_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.ignorecase")) {
+               ignore_case = git_config_bool(var, value);
+               return 0;
+       }
+
        if (!strcmp(var, "core.bare")) {
                is_bare_repository_cfg = git_config_bool(var, value);
                return 0;
@@ -443,6 +448,8 @@ int git_default_config(const char *var, const char *value)
                if (!value)
                        return config_error_nonbool(var);
                strlcpy(git_default_name, value, sizeof(git_default_name));
+               if (git_default_email[0])
+                       user_ident_explicitly_given = 1;
                return 0;
        }
 
@@ -450,6 +457,8 @@ int git_default_config(const char *var, const char *value)
                if (!value)
                        return config_error_nonbool(var);
                strlcpy(git_default_email, value, sizeof(git_default_email));
+               if (git_default_name[0])
+                       user_ident_explicitly_given = 1;
                return 0;
        }
 
@@ -487,6 +496,21 @@ int git_default_config(const char *var, const char *value)
                git_branch_track = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "branch.autosetuprebase")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               else if (!strcmp(value, "never"))
+                       autorebase = AUTOREBASE_NEVER;
+               else if (!strcmp(value, "local"))
+                       autorebase = AUTOREBASE_LOCAL;
+               else if (!strcmp(value, "remote"))
+                       autorebase = AUTOREBASE_REMOTE;
+               else if (!strcmp(value, "always"))
+                       autorebase = AUTOREBASE_ALWAYS;
+               else
+                       return error("Malformed value for %s", var);
+               return 0;
+       }
 
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
@@ -607,11 +631,9 @@ static int store_aux(const char* key, const char* value)
        case KEY_SEEN:
                if (matches(key, value)) {
                        if (store.seen == 1 && store.multi_replace == 0) {
-                               fprintf(stderr,
-                                       "Warning: %s has multiple values\n",
-                                       key);
+                               warning("%s has multiple values", key);
                        } else if (store.seen >= MAX_MATCHES) {
-                               fprintf(stderr, "Too many matches\n");
+                               error("too many matches for %s", key);
                                return 1;
                        }
 
@@ -661,9 +683,9 @@ static int store_aux(const char* key, const char* value)
        return 0;
 }
 
-static int write_error(void)
+static int write_error(const char *filename)
 {
-       fprintf(stderr, "Failed to write new configuration file\n");
+       error("failed to write new configuration file %s", filename);
 
        /* Same error code as "failed to rename". */
        return 4;
@@ -680,7 +702,7 @@ static int store_write_section(int fd, const char* key)
        if (dot) {
                strbuf_addf(&sb, "[%.*s \"", (int)(dot - key), key);
                for (i = dot - key + 1; i < store.baselen; i++) {
-                       if (key[i] == '"')
+                       if (key[i] == '"' || key[i] == '\\')
                                strbuf_addch(&sb, '\\');
                        strbuf_addch(&sb, key[i]);
                }
@@ -822,7 +844,7 @@ int git_config_set_multivar(const char* key, const char* value,
         */
 
        if (last_dot == NULL) {
-               fprintf(stderr, "key does not contain a section: %s\n", key);
+               error("key does not contain a section: %s", key);
                ret = 2;
                goto out_free;
        }
@@ -842,14 +864,14 @@ int git_config_set_multivar(const char* key, const char* value,
                /* Leave the extended basename untouched.. */
                if (!dot || i > store.baselen) {
                        if (!iskeychar(c) || (i == store.baselen+1 && !isalpha(c))) {
-                               fprintf(stderr, "invalid key: %s\n", key);
+                               error("invalid key: %s", key);
                                free(store.key);
                                ret = 1;
                                goto out_free;
                        }
                        c = tolower(c);
                } else if (c == '\n') {
-                       fprintf(stderr, "invalid key (newline): %s\n", key);
+                       error("invalid key (newline): %s", key);
                        free(store.key);
                        ret = 1;
                        goto out_free;
@@ -865,7 +887,7 @@ int git_config_set_multivar(const char* key, const char* value,
        lock = xcalloc(sizeof(struct lock_file), 1);
        fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (fd < 0) {
-               fprintf(stderr, "could not lock config file\n");
+               error("could not lock config file %s", config_filename);
                free(store.key);
                ret = -1;
                goto out_free;
@@ -912,8 +934,7 @@ int git_config_set_multivar(const char* key, const char* value,
                        store.value_regex = (regex_t*)xmalloc(sizeof(regex_t));
                        if (regcomp(store.value_regex, value_regex,
                                        REG_EXTENDED)) {
-                               fprintf(stderr, "Invalid pattern: %s\n",
-                                       value_regex);
+                               error("invalid pattern: %s", value_regex);
                                free(store.value_regex);
                                ret = 6;
                                goto out_free;
@@ -931,7 +952,7 @@ int git_config_set_multivar(const char* key, const char* value,
                 * existing config file.
                 */
                if (git_config_from_file(store_aux, config_filename)) {
-                       fprintf(stderr, "invalid config file\n");
+                       error("invalid config file %s", config_filename);
                        free(store.key);
                        if (store.value_regex != NULL) {
                                regfree(store.value_regex);
@@ -1010,7 +1031,7 @@ int git_config_set_multivar(const char* key, const char* value,
        }
 
        if (commit_lock_file(lock) < 0) {
-               fprintf(stderr, "Cannot commit config file!\n");
+               error("could not commit config file %s", config_filename);
                ret = 4;
                goto out_free;
        }
@@ -1031,7 +1052,7 @@ int git_config_set_multivar(const char* key, const char* value,
        return ret;
 
 write_err_out:
-       ret = write_error();
+       ret = write_error(lock->filename);
        goto out_free;
 
 }
@@ -1081,7 +1102,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        config_filename = xstrdup(config_filename);
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
-               ret = error("Could not lock config file!");
+               ret = error("could not lock config file %s", config_filename);
                goto out;
        }
 
@@ -1105,7 +1126,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
-                                       ret = write_error();
+                                       ret = write_error(lock->filename);
                                        goto out;
                                }
                                continue;
@@ -1116,14 +1137,14 @@ int git_config_rename_section(const char *old_name, const char *new_name)
                        continue;
                length = strlen(buf);
                if (write_in_full(out_fd, buf, length) != length) {
-                       ret = write_error();
+                       ret = write_error(lock->filename);
                        goto out;
                }
        }
        fclose(config_file);
  unlock_and_out:
        if (commit_lock_file(lock) < 0)
-                       ret = error("Cannot commit config file!");
+               ret = error("could not commit config file %s", config_filename);
  out:
        free(config_filename);
        return ret;
index 23db664f48057b7fa778f0b265510d5b95457c16..16984632d984006e2f2867a26714086bf3045ca3 100755 (executable)
@@ -780,7 +780,7 @@ _git_merge ()
                ;;
        --*)
                __gitcomp "
-                       --no-commit --no-summary --squash --strategy
+                       --no-commit --no-stat --log --no-log --squash --strategy
                        "
                return
        esac
index cfd629da48526f5949124b5d9baeb52dfadd5f64..fe2ccec7e6b3230715cbdf04591d1c0efb6a6389 100644 (file)
@@ -337,22 +337,41 @@ int run_diff_files_cmd(struct rev_info *revs, int argc, const char **argv)
        }
        return run_diff_files(revs, options);
 }
+
 /*
- * See if work tree has an entity that can be staged.  Return 0 if so,
- * return 1 if not and return -1 if error.
+ * Has the work tree entity been removed?
+ *
+ * Return 1 if it was removed from the work tree, 0 if an entity to be
+ * compared with the cache entry ce still exists (the latter includes
+ * the case where a directory that is not a submodule repository
+ * exists for ce that is a submodule -- it is a submodule that is not
+ * checked out).  Return negative for an error.
  */
-static int check_work_tree_entity(const struct cache_entry *ce, struct stat *st, char *symcache)
+static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
        if (lstat(ce->name, st) < 0) {
                if (errno != ENOENT && errno != ENOTDIR)
                        return -1;
                return 1;
        }
-       if (has_symlink_leading_path(ce->name, symcache))
+       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
                return 1;
        if (S_ISDIR(st->st_mode)) {
                unsigned char sub[20];
-               if (resolve_gitlink_ref(ce->name, "HEAD", sub))
+
+               /*
+                * If ce is already a gitlink, we can have a plain
+                * directory (i.e. the submodule is not checked out),
+                * or a checked out submodule.  Either case this is not
+                * a case where something was removed from the work tree,
+                * so we will return 0.
+                *
+                * Otherwise, if the directory is not a submodule
+                * repository, that means ce which was a blob turned into
+                * a directory --- the blob was removed!
+                */
+               if (!S_ISGITLINK(ce->ce_mode) &&
+                   resolve_gitlink_ref(ce->name, "HEAD", sub))
                        return 1;
        }
        return 0;
@@ -402,7 +421,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        memset(&(dpath->parent[0]), 0,
                               sizeof(struct combine_diff_parent)*5);
 
-                       changed = check_work_tree_entity(ce, &st, symcache);
+                       changed = check_removed(ce, &st);
                        if (!changed)
                                dpath->mode = ce_mode_from_stat(ce, st.st_mode);
                        else {
@@ -466,7 +485,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                if (ce_uptodate(ce))
                        continue;
 
-               changed = check_work_tree_entity(ce, &st, symcache);
+               changed = check_removed(ce, &st);
                if (changed) {
                        if (changed < 0) {
                                perror(ce->name);
@@ -479,8 +498,11 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                        continue;
                }
                changed = ce_match_stat(ce, &st, ce_option);
-               if (!changed && !DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
-                       continue;
+               if (!changed) {
+                       ce_mark_uptodate(ce);
+                       if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
+                               continue;
+               }
                oldmode = ce->ce_mode;
                newmode = ce_mode_from_stat(ce, st.st_mode);
                diff_change(&revs->diffopt, oldmode, newmode,
@@ -524,7 +546,7 @@ static int get_stat_data(struct cache_entry *ce,
        if (!cached) {
                int changed;
                struct stat st;
-               changed = check_work_tree_entity(ce, &st, cbdata->symcache);
+               changed = check_removed(ce, &st);
                if (changed < 0)
                        return -1;
                else if (changed) {
diff --git a/diff.c b/diff.c
index e35384b4442fbaedbf460ddf61c32b85dde2a4d7..439d4746cae343f08b298473d35d9792048abfa0 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -19,7 +19,7 @@
 #endif
 
 static int diff_detect_rename_default;
-static int diff_rename_limit_default = 100;
+static int diff_rename_limit_default = 200;
 int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
diff --git a/diff.h b/diff.h
index 1bd94a4807c741e7cb2e4d8eb9f6476eba691388..3a02d38d12d94c39ff9ef3ab744ea7d157fb52b4 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -83,6 +83,7 @@ struct diff_options {
        int pickaxe_opts;
        int rename_score;
        int rename_limit;
+       int warn_on_too_large_rename;
        int dirstat_percent;
        int setup;
        int abbrev;
index 1369a5ec45349c1ef04e4df2d7d7d4dd28cd3596..1b2ebb40014d820fe4fb679509ab694d453be7b4 100644 (file)
@@ -492,7 +492,8 @@ void diffcore_rename(struct diff_options *options)
                rename_limit = 32767;
        if ((num_create > rename_limit && num_src > rename_limit) ||
            (num_create * num_src > rename_limit * rename_limit)) {
-               warning("too many files, skipping inexact rename detection");
+               if (options->warn_on_too_large_rename)
+                       warning("too many files, skipping inexact rename detection");
                goto cleanup;
        }
 
diff --git a/dir.c b/dir.c
index d79762c7c0bc9e762ed8dc5f00fb2fd3ce01ad57..29d1d5ba31def46ba8b55905dc60773cc6cc167e 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -52,6 +52,11 @@ int common_prefix(const char **pathspec)
        return prefix;
 }
 
+static inline int special_char(unsigned char c1)
+{
+       return !c1 || c1 == '*' || c1 == '[' || c1 == '?';
+}
+
 /*
  * Does 'match' matches the given name?
  * A match is found if
@@ -69,14 +74,27 @@ static int match_one(const char *match, const char *name, int namelen)
        int matchlen;
 
        /* If the match was just the prefix, we matched */
-       matchlen = strlen(match);
-       if (!matchlen)
+       if (!*match)
                return MATCHED_RECURSIVELY;
 
+       for (;;) {
+               unsigned char c1 = *match;
+               unsigned char c2 = *name;
+               if (special_char(c1))
+                       break;
+               if (c1 != c2)
+                       return 0;
+               match++;
+               name++;
+               namelen--;
+       }
+
+
        /*
         * If we don't match the matchstring exactly,
         * we need to match by fnmatch
         */
+       matchlen = strlen(match);
        if (strncmp(match, name, matchlen))
                return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
 
@@ -371,7 +389,7 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
 
 struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
 {
-       if (cache_name_exists(pathname, len))
+       if (cache_name_exists(pathname, len, ignore_case))
                return NULL;
 
        ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
index 6739a3f41745fe1c01dc767e09af8746d6f2815f..4fcb471248fb5276a68b9f86a77a2f5cde7dd884 100644 (file)
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
+int user_ident_explicitly_given;
 int trust_executable_bit = 1;
 int quote_path_fully = 1;
 int has_symlinks = 1;
+int ignore_case;
 int assume_unchanged;
 int prefer_symlink_refs;
 int is_bare_repository_cfg = -1; /* unspecified */
@@ -38,6 +40,7 @@ int auto_crlf = 0;    /* 1: both ways, -1: only when adding git objects */
 enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
+enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
 
 /* This is set by setup_git_dir_gently() and/or git_default_config() */
 char *git_work_tree_cfg;
@@ -49,6 +52,8 @@ static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file;
 static void setup_git_env(void)
 {
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
+       if (!git_dir)
+               git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
        if (!git_dir)
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
        git_object_dir = getenv(DB_ENVIRONMENT);
index 73e5439d97481a0e4dcaa17d025b27ec3fdcf41b..caea684338031431dc3177716654a1a289faa9a3 100644 (file)
@@ -1690,7 +1690,7 @@ static void skip_optional_lf(void)
                ungetc(term_char, stdin);
 }
 
-static void cmd_mark(void)
+static void parse_mark(void)
 {
        if (!prefixcmp(command_buf.buf, "mark :")) {
                next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
@@ -1700,7 +1700,7 @@ static void cmd_mark(void)
                next_mark = 0;
 }
 
-static void cmd_data(struct strbuf *sb)
+static void parse_data(struct strbuf *sb)
 {
        strbuf_reset(sb);
 
@@ -1798,13 +1798,13 @@ static char *parse_ident(const char *buf)
        return ident;
 }
 
-static void cmd_new_blob(void)
+static void parse_new_blob(void)
 {
        static struct strbuf buf = STRBUF_INIT;
 
        read_next_command();
-       cmd_mark();
-       cmd_data(&buf);
+       parse_mark();
+       parse_data(&buf);
        store_object(OBJ_BLOB, &buf, &last_blob, NULL, next_mark);
 }
 
@@ -1908,7 +1908,7 @@ static void file_change_m(struct branch *b)
                        p = uq.buf;
                }
                read_next_command();
-               cmd_data(&buf);
+               parse_data(&buf);
                store_object(OBJ_BLOB, &buf, &last_blob, sha1, 0);
        } else if (oe) {
                if (oe->type != OBJ_BLOB)
@@ -1995,7 +1995,7 @@ static void file_change_deleteall(struct branch *b)
        load_tree(&b->branch_tree);
 }
 
-static void cmd_from_commit(struct branch *b, char *buf, unsigned long size)
+static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
 {
        if (!buf || size < 46)
                die("Not a valid commit: %s", sha1_to_hex(b->sha1));
@@ -2006,7 +2006,7 @@ static void cmd_from_commit(struct branch *b, char *buf, unsigned long size)
                b->branch_tree.versions[1].sha1);
 }
 
-static void cmd_from_existing(struct branch *b)
+static void parse_from_existing(struct branch *b)
 {
        if (is_null_sha1(b->sha1)) {
                hashclr(b->branch_tree.versions[0].sha1);
@@ -2017,12 +2017,12 @@ static void cmd_from_existing(struct branch *b)
 
                buf = read_object_with_reference(b->sha1,
                        commit_type, &size, b->sha1);
-               cmd_from_commit(b, buf, size);
+               parse_from_commit(b, buf, size);
                free(buf);
        }
 }
 
-static int cmd_from(struct branch *b)
+static int parse_from(struct branch *b)
 {
        const char *from;
        struct branch *s;
@@ -2053,12 +2053,12 @@ static int cmd_from(struct branch *b)
                if (oe->pack_id != MAX_PACK_ID) {
                        unsigned long size;
                        char *buf = gfi_unpack_entry(oe, &size);
-                       cmd_from_commit(b, buf, size);
+                       parse_from_commit(b, buf, size);
                        free(buf);
                } else
-                       cmd_from_existing(b);
+                       parse_from_existing(b);
        } else if (!get_sha1(from, b->sha1))
-               cmd_from_existing(b);
+               parse_from_existing(b);
        else
                die("Invalid ref name or SHA1 expression: %s", from);
 
@@ -2066,7 +2066,7 @@ static int cmd_from(struct branch *b)
        return 1;
 }
 
-static struct hash_list *cmd_merge(unsigned int *count)
+static struct hash_list *parse_merge(unsigned int *count)
 {
        struct hash_list *list = NULL, *n, *e = e;
        const char *from;
@@ -2107,7 +2107,7 @@ static struct hash_list *cmd_merge(unsigned int *count)
        return list;
 }
 
-static void cmd_new_commit(void)
+static void parse_new_commit(void)
 {
        static struct strbuf msg = STRBUF_INIT;
        struct branch *b;
@@ -2124,7 +2124,7 @@ static void cmd_new_commit(void)
                b = new_branch(sp);
 
        read_next_command();
-       cmd_mark();
+       parse_mark();
        if (!prefixcmp(command_buf.buf, "author ")) {
                author = parse_ident(command_buf.buf + 7);
                read_next_command();
@@ -2135,10 +2135,10 @@ static void cmd_new_commit(void)
        }
        if (!committer)
                die("Expected committer but didn't get one");
-       cmd_data(&msg);
+       parse_data(&msg);
        read_next_command();
-       cmd_from(b);
-       merge_list = cmd_merge(&merge_count);
+       parse_from(b);
+       merge_list = parse_merge(&merge_count);
 
        /* ensure the branch is active/loaded */
        if (!b->branch_tree.tree || !max_active_branches) {
@@ -2196,7 +2196,7 @@ static void cmd_new_commit(void)
        b->last_commit = object_count_by_type[OBJ_COMMIT];
 }
 
-static void cmd_new_tag(void)
+static void parse_new_tag(void)
 {
        static struct strbuf msg = STRBUF_INIT;
        char *sp;
@@ -2253,7 +2253,7 @@ static void cmd_new_tag(void)
 
        /* tag payload/message */
        read_next_command();
-       cmd_data(&msg);
+       parse_data(&msg);
 
        /* build the tag object */
        strbuf_reset(&new_data);
@@ -2273,7 +2273,7 @@ static void cmd_new_tag(void)
                t->pack_id = pack_id;
 }
 
-static void cmd_reset_branch(void)
+static void parse_reset_branch(void)
 {
        struct branch *b;
        char *sp;
@@ -2293,12 +2293,12 @@ static void cmd_reset_branch(void)
        else
                b = new_branch(sp);
        read_next_command();
-       cmd_from(b);
+       parse_from(b);
        if (command_buf.len > 0)
                unread_command_buf = 1;
 }
 
-static void cmd_checkpoint(void)
+static void parse_checkpoint(void)
 {
        if (object_count) {
                cycle_packfile();
@@ -2309,7 +2309,7 @@ static void cmd_checkpoint(void)
        skip_optional_lf();
 }
 
-static void cmd_progress(void)
+static void parse_progress(void)
 {
        fwrite(command_buf.buf, 1, command_buf.len, stdout);
        fputc('\n', stdout);
@@ -2449,17 +2449,17 @@ int main(int argc, const char **argv)
        set_die_routine(die_nicely);
        while (read_next_command() != EOF) {
                if (!strcmp("blob", command_buf.buf))
-                       cmd_new_blob();
+                       parse_new_blob();
                else if (!prefixcmp(command_buf.buf, "commit "))
-                       cmd_new_commit();
+                       parse_new_commit();
                else if (!prefixcmp(command_buf.buf, "tag "))
-                       cmd_new_tag();
+                       parse_new_tag();
                else if (!prefixcmp(command_buf.buf, "reset "))
-                       cmd_reset_branch();
+                       parse_reset_branch();
                else if (!strcmp("checkpoint", command_buf.buf))
-                       cmd_checkpoint();
+                       parse_checkpoint();
                else if (!prefixcmp(command_buf.buf, "progress "))
-                       cmd_progress();
+                       parse_progress();
                else
                        die("Unsupported command: %s", command_buf.buf);
        }
index 75886a8f2fb0c2244ba921398ae2339dc9ffea26..b48096ec2037e3f14d4bdf89a0afe4623aff090e 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -11,7 +11,7 @@ git-am [options] --skip
 --
 d,dotest=       (removed -- do not use)
 i,interactive   run interactively
-b,binary        pass --allo-binary-replacement to git-apply
+b,binary        pass --allow-binary-replacement to git-apply
 3,3way          allow fall back on 3way merging if needed
 s,signoff       add a Signed-off-by line to the commit message
 u,utf8          recode into utf8 (default)
index d8d9bfde4cdd4b558992c68cb7cdaaa1b8a1212d..164e8ed81fc3ecb5d33dafb9af0992761bfe513d 100755 (executable)
@@ -69,14 +69,19 @@ bisect_start() {
        head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
        head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
        die "Bad HEAD - I need a HEAD"
+       #
+       # Check that we either already have BISECT_START, or that the
+       # branches bisect, new-bisect don't exist, to not override them.
+       #
+       test -s "$GIT_DIR/BISECT_START" ||
+               if git show-ref --verify -q refs/heads/bisect ||
+                   git show-ref --verify -q refs/heads/new-bisect; then
+                       die 'The branches "bisect" and "new-bisect" must not exist.'
+               fi
        start_head=''
        case "$head" in
        refs/heads/bisect)
-               if [ -s "$GIT_DIR/BISECT_START" ]; then
-                   branch=`cat "$GIT_DIR/BISECT_START"`
-               else
-                   branch=master
-               fi
+               branch=`cat "$GIT_DIR/BISECT_START"`
                git checkout $branch || exit
                ;;
        refs/heads/*|$_x40)
@@ -219,18 +224,33 @@ 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 $_eval
+               eval_rev_list "$_eval"
                return
        fi
 
        # Let's parse the output of:
        # "git rev-list --bisect-vars --bisect-all ..."
-       eval $_eval | while read hash line
+       eval_rev_list "$_eval" | while read hash line
        do
                case "$VARS,$FOUND,$TRIED,$hash" in
                        # We display some vars.
@@ -328,8 +348,8 @@ bisect_next() {
        exit_if_skipped_commits "$bisect_rev"
 
        echo "Bisecting: $bisect_nr revisions left to test after this"
-       git branch -f new-bisect "$bisect_rev"
-       git checkout -q new-bisect || exit
+       git branch -D new-bisect 2> /dev/null
+       git checkout -q -b new-bisect "$bisect_rev" || exit
        git branch -M new-bisect bisect
        git show-branch "$bisect_rev"
 }
index 8c7fc7f6317113fcef923dcc625fb94e13fb77a0..547228e13ce60e575d0b4a10a322edfff6c0622c 100755 (executable)
@@ -240,7 +240,6 @@ die "working tree '$GIT_WORK_TREE' already exists."
 D=
 W=
 cleanup() {
-       err=$?
        test -z "$D" && rm -rf "$dir"
        test -z "$W" && test -n "$GIT_WORK_TREE" && rm -rf "$GIT_WORK_TREE"
        cd ..
@@ -248,7 +247,7 @@ cleanup() {
        test -n "$W" && rm -rf "$W"
        exit $err
 }
-trap cleanup 0
+trap 'err=$?; cleanup' 0
 mkdir -p "$dir" && D=$(cd "$dir" && pwd) || usage
 test -n "$GIT_WORK_TREE" && mkdir -p "$GIT_WORK_TREE" &&
 W=$(cd "$GIT_WORK_TREE" && pwd) && GIT_WORK_TREE="$W" && export GIT_WORK_TREE
@@ -334,7 +333,10 @@ yes)
                        fi
                fi &&
                cd "$repo" &&
-               find objects -depth -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \
+               # Create dirs using umask and permissions and destination
+               find objects -type d -print | (cd "$GIT_DIR" && xargs mkdir -p) &&
+               # Copy existing 0444 permissions on content
+               find objects ! -type d -print | cpio $cpio_quiet_flag -pumd$l "$GIT_DIR/" || \
                        exit 1
        fi
        git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
index 167c3fe63a4fd9ca4abf5c16694e22a9ae22717b..01c4045e89a2e156062255193ed5d865fdf8a922 100644 (file)
@@ -206,6 +206,9 @@ void *gitmemmem(const void *haystack, size_t haystacklen,
 #endif
 
 #ifdef FREAD_READS_DIRECTORIES
+#ifdef fopen
+#undef fopen
+#endif
 #define fopen(a,b) git_fopen(a,b)
 extern FILE *git_fopen(const char*, const char*);
 #endif
index b6036bd4d305215b4a70b6fd0fe54d7607dbe068..c93bd9c9b553f18b29e72a08176588d889e7acde 100755 (executable)
@@ -6,6 +6,7 @@
 use Data::Dumper;
 use File::Basename qw(basename dirname);
 use File::Spec;
+use Git;
 
 our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w);
 
 
 die "Need at least one commit identifier!" unless @ARGV;
 
+# Get git-config settings
+my $repo = Git->repository();
+$opt_w = $repo->config('cvsexportcommit.cvsdir') unless defined $opt_w;
+
 if ($opt_w) {
        # Remember where GIT_DIR is before changing to CVS checkout
        unless ($ENV{GIT_DIR}) {
index ea59015baa2507fdc8fe77d1c77ebdb2d5db2fa7..80e99e539477cbd7266b19c1bd90d424cf1341ee 100755 (executable)
@@ -406,8 +406,22 @@ if [ "$filter_tag_name" ]; then
                echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
 
                if [ "$type" = "tag" ]; then
-                       # Warn that we are not rewriting the tag object itself.
-                       warn "unreferencing tag object $sha1t"
+                       new_sha1=$(git cat-file tag "$ref" |
+                               sed -n \
+                                   -e "1,/^$/{
+                                         s/^object .*/object $new_sha1/
+                                         s/^type .*/type commit/
+                                         s/^tag .*/tag $new_ref/
+                                       }" \
+                                   -e '/^-----BEGIN PGP SIGNATURE-----/q' \
+                                   -e 'p' |
+                               git mktag) ||
+                               die "Could not create new tag object for $ref"
+                       if git cat-file tag "$ref" | \
+                          grep '^-----BEGIN PGP SIGNATURE-----' >/dev/null 2>&1
+                       then
+                               warn "gpg signature stripped from tag object $sha1t"
+                       fi
                fi
 
                git update-ref "refs/tags/$new_ref" "$new_sha1" ||
@@ -421,11 +435,17 @@ rm -rf "$tempdir"
 trap - 0
 
 unset GIT_DIR GIT_WORK_TREE GIT_INDEX_FILE
-test -z "$ORIG_GIT_DIR" || GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
-test -z "$ORIG_GIT_WORK_TREE" || GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
+test -z "$ORIG_GIT_DIR" || {
+       GIT_DIR="$ORIG_GIT_DIR" && export GIT_DIR
+}
+test -z "$ORIG_GIT_WORK_TREE" || {
+       GIT_WORK_TREE="$ORIG_GIT_WORK_TREE" &&
        export GIT_WORK_TREE
-test -z "$ORIG_GIT_INDEX_FILE" || GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
+}
+test -z "$ORIG_GIT_INDEX_FILE" || {
+       GIT_INDEX_FILE="$ORIG_GIT_INDEX_FILE" &&
        export GIT_INDEX_FILE
+}
 git read-tree -u -m HEAD
 
 exit $ret
index 7c25bb98089ec4c0a57993020a30d2d1afad8dae..9df49710e1b378af1f49c5ce3c8c8c48731d7d38 100755 (executable)
@@ -601,6 +601,7 @@ proc apply_config {} {
        }
 }
 
+set default_config(branch.autosetupmerge) true
 set default_config(merge.diffstat) true
 set default_config(merge.summary) false
 set default_config(merge.verbosity) 2
index 53dfb4ce6bb053349fbed39af8ffaf5a143b6567..3817771b944cc363205b86c91f7b4801c1d568f9 100644 (file)
@@ -183,6 +183,9 @@ method _create {} {
        if {$spec ne {} && $opt_fetch} {
                $co enable_fetch $spec
        }
+       if {$spec ne {}} {
+               $co remote_source $spec
+       }
 
        if {[$co run]} {
                destroy $w
index 86c4f73370a76ffa0196be0c58f11092b101cf0b..ef1930b4911591566be4561b8c17c24e1cfbfaad 100644 (file)
@@ -127,7 +127,7 @@ method _delete {} {
        foreach i $to_delete {
                set b [lindex $i 0]
                set o [lindex $i 1]
-               if {[catch {git update-ref -d "refs/heads/$b" $o} err]} {
+               if {[catch {git branch -D $b} err]} {
                        append failed " - $b: $err\n"
                }
        }
index 6e1411711bd46df08c09fd2bc371b017050a8368..caca88831b7066c573917542d24a52e832dcff34 100644 (file)
@@ -16,6 +16,7 @@ field merge_base   {}; # merge base if we have another ref involved
 field fetch_spec   {}; # refetch tracking branch if used?
 field checkout      1; # actually checkout the branch?
 field create        0; # create the branch if it doesn't exist?
+field remote_source {}; # same as fetch_spec, to setup tracking
 
 field reset_ok      0; # did the user agree to reset?
 field fetch_ok      0; # did the fetch succeed?
@@ -44,6 +45,10 @@ method enable_fetch {spec} {
        set fetch_spec $spec
 }
 
+method remote_source {spec} {
+       set remote_source $spec
+}
+
 method enable_checkout {co} {
        set checkout $co
 }
@@ -145,7 +150,7 @@ method _finish_fetch {ok} {
 }
 
 method _update_ref {} {
-       global null_sha1 current_branch
+       global null_sha1 current_branch repo_config
 
        set ref $new_ref
        set new $new_hash
@@ -172,6 +177,23 @@ method _update_ref {} {
 
                set reflog_msg "branch: Created from $new_expr"
                set cur $null_sha1
+
+               if {($repo_config(branch.autosetupmerge) eq {true}
+                       || $repo_config(branch.autosetupmerge) eq {always})
+                       && $remote_source ne {}
+                       && "refs/heads/$newbranch" eq $ref} {
+
+                       set c_remote [lindex $remote_source 1]
+                       set c_merge [lindex $remote_source 2]
+                       if {[catch {
+                                       git config branch.$newbranch.remote $c_remote
+                                       git config branch.$newbranch.merge  $c_merge
+                               } err]} {
+                               _error $this [strcat \
+                               [mc "Failed to configure simplified git-pull for '%s'." $newbranch] \
+                               "\n\n$err"]
+                       }
+               }
        } elseif {$create && $merge_type eq {none}} {
                # We were told to create it, but not do a merge.
                # Bad.  Name shouldn't have existed.
index d66aa3fe3367e0a8db0ee5c90925352a3a143198..a18ac8b4308d8263a0688058524282b72bafe77a 100644 (file)
@@ -102,8 +102,8 @@ proc hint_gc {} {
                *]]
 
        if {$objects_current >= $object_limit} {
-               set objects_current [expr {$objects_current * 256}]
-               set object_limit    [expr {$object_limit    * 256}]
+               set objects_current [expr {$objects_current * 250}]
+               set object_limit    [expr {$object_limit    * 250}]
                if {[ask_popup \
                        [mc "This repository currently has approximately %i loose objects.
 
index 9be748683ce101821a91bf9461ccadfd58c8adf1..78f344f08f34842c134b6b0e22b6eb8c49b1dbbd 100644 (file)
@@ -84,13 +84,19 @@ method _connect {pipe_fd} {
        regexp \
                {International Ispell Version .* \(but really (Aspell .*?)\)$} \
                $s_version _junk s_version
+       regexp {^Aspell (\d)+\.(\d+)} $s_version _junk major minor
 
        puts $pipe_fd !             ; # enable terse mode
-       puts $pipe_fd {$$cr master} ; # fetch the language
-       flush $pipe_fd
 
-       gets $pipe_fd s_lang
-       regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang
+       # fetch the language
+       if {$major > 0 || ($major == 0 && $minor >= 60)} {
+               puts $pipe_fd {$$cr master}
+               flush $pipe_fd
+               gets $pipe_fd s_lang
+               regexp {[/\\]([^/\\]+)\.[^\.]+$} $s_lang _ s_lang
+       } else {
+               set s_lang {}
+       }
 
        if {$::default_config(gui.spellingdictionary) eq {}
         && [get_config gui.spellingdictionary] eq {}} {
index 022b816ae1bf5591467218a310ed2a42aa634809..f20955c18520bf2b0cc8826da9e1dc93a5624ac3 100644 (file)
@@ -8,7 +8,7 @@ msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
 "POT-Creation-Date: 2008-03-14 07:18+0100\n"
-"PO-Revision-Date: 2008-02-16 21:52+0100\n"
+"PO-Revision-Date: 2008-05-01 11:51+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -1754,9 +1754,8 @@ msgid "Number of Diff Context Lines"
 msgstr "Anzahl der Kontextzeilen beim Vergleich"
 
 #: lib/option.tcl:127
-#, fuzzy
 msgid "Commit Message Text Width"
-msgstr "Versionsbeschreibung:"
+msgstr "Textbreite der Versionsbeschreibung"
 
 #: lib/option.tcl:128
 msgid "New Branch Name Template"
@@ -1895,40 +1894,36 @@ msgstr "Fehler beim Erstellen des Icons:"
 
 #: lib/spellcheck.tcl:57
 msgid "Unsupported spell checker"
-msgstr ""
+msgstr "Rechtschreibprüfungsprogramm nicht unterstützt"
 
 #: lib/spellcheck.tcl:65
-#, fuzzy
 msgid "Spell checking is unavailable"
-msgstr "Rechtschreibprüfung fehlgeschlagen"
+msgstr "Rechtschreibprüfung nicht verfügbar"
 
 #: lib/spellcheck.tcl:68
 msgid "Invalid spell checking configuration"
-msgstr ""
+msgstr "Unbenutzbare Konfiguration der Rechtschreibprüfung"
 
 #: lib/spellcheck.tcl:70
 #, tcl-format
 msgid "Reverting dictionary to %s."
-msgstr ""
+msgstr "Wörterbuch auf %s zurückgesetzt."
 
 #: lib/spellcheck.tcl:73
-#, fuzzy
 msgid "Spell checker silently failed on startup"
-msgstr "Rechtschreibprüfung fehlgeschlagen"
+msgstr "Rechtschreibprüfungsprogramm mit Fehler abgebrochen"
 
 #: lib/spellcheck.tcl:80
-#, fuzzy
 msgid "Unrecognized spell checker"
-msgstr "Unbekannte Version von »aspell«"
+msgstr "Unbekanntes Rechtschreibprüfungsprogramm"
 
 #: lib/spellcheck.tcl:180
 msgid "No Suggestions"
 msgstr "Keine Vorschläge"
 
 #: lib/spellcheck.tcl:381
-#, fuzzy
 msgid "Unexpected EOF from spell checker"
-msgstr "Unerwartetes EOF von »aspell«"
+msgstr "Unerwartetes EOF vom Rechtschreibprüfungsprogramm"
 
 #: lib/spellcheck.tcl:385
 msgid "Spell Checker Failed"
@@ -2002,6 +1997,3 @@ msgstr "Kompaktes Datenformat benutzen (für langsame Netzverbindungen)"
 #: lib/transport.tcl:168
 msgid "Include tags"
 msgstr "Mit Markierungen übertragen"
-
-#~ msgid "Not connected to aspell"
-#~ msgstr "Keine Verbindung zu »aspell«"
index 7dbbb1d79dc1d66db3a662045a13554f8d574004..5fc5f5201f8ea8e155a25795b93bfea86fc12d12 100755 (executable)
@@ -8,8 +8,10 @@ OPTIONS_SPEC="\
 git-merge [options] <remote>...
 git-merge [options] <msg> HEAD <remote>
 --
-summary              show a diffstat at the end of the merge
-n,no-summary         don't show a diffstat at the end of the merge
+stat                 show a diffstat at the end of the merge
+n                    don't show a diffstat at the end of the merge
+summary              (synonym to --stat)
+log                  add list of one-line log to merge commit message
 squash               create a single commit instead of doing a merge
 commit               perform a commit if the merge sucesses (default)
 ff                   allow fast forward (default)
@@ -37,7 +39,7 @@ use_strategies=
 
 allow_fast_forward=t
 allow_trivial_merge=t
-squash= no_commit=
+squash= no_commit= log_arg=
 
 dropsave() {
        rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
@@ -148,10 +150,12 @@ merge_name () {
 parse_config () {
        while test $# != 0; do
                case "$1" in
-               -n|--no-summary)
+               -n|--no-stat|--no-summary)
                        show_diffstat=false ;;
-               --summary)
+               --stat|--summary)
                        show_diffstat=t ;;
+               --log|--no-log)
+                       log_arg=$1 ;;
                --squash)
                        test "$allow_fast_forward" = t ||
                                die "You cannot combine --squash with --no-ff."
@@ -210,6 +214,7 @@ while test $args_left -lt $#; do shift; done
 
 if test -z "$show_diffstat"; then
     test "$(git config --bool merge.diffstat)" = false && show_diffstat=false
+    test "$(git config --bool merge.stat)" = false && show_diffstat=false
     test -z "$show_diffstat" && show_diffstat=t
 fi
 
@@ -258,7 +263,7 @@ else
        merge_name=$(for remote
                do
                        merge_name "$remote"
-               done | git fmt-merge-msg
+               done | git fmt-merge-msg $log_arg
        )
        merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
 fi
index 5c86f69229042c16704d11ce404e43297cc7b72c..fcdec4a504acd5681e0695fb9657d6a1cd513ab0 100755 (executable)
@@ -237,9 +237,9 @@ merge_file () {
        ecmerge)
            touch "$BACKUP"
            if base_present; then
-               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --mode=merge3 --to="$MERGED"
+               "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"
            else
-               "$merge_tool_path" "$LOCAL" "$REMOTE" --mode=merge2 --to="$MERGED"
+               "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"
            fi
            check_unchanged
            ;;
index 3ce32b5f211bd20f1d154c860eb5bf7f9005ca3c..809e537a4d81233966aad23df11c13931d6250c1 100755 (executable)
@@ -4,7 +4,7 @@
 #
 # Fetch one or more remote refs and merge it/them into the current HEAD.
 
-USAGE='[-n | --no-summary] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
+USAGE='[-n | --no-stat] [--[no-]commit] [--[no-]squash] [--[no-]ff] [-s strategy]... [<fetch-options>] <repo> <head>...'
 LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
@@ -16,19 +16,19 @@ cd_to_toplevel
 test -z "$(git ls-files -u)" ||
        die "You are in the middle of a conflicted merge."
 
-strategy_args= no_summary= no_commit= squash= no_ff=
+strategy_args= no_stat= no_commit= squash= no_ff= log_arg=
 curr_branch=$(git symbolic-ref -q HEAD)
 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
 while :
 do
        case "$1" in
-       -n|--n|--no|--no-|--no-s|--no-su|--no-sum|--no-summ|\
-               --no-summa|--no-summar|--no-summary)
-               no_summary=-n ;;
-       --summary)
-               no_summary=$1
-               ;;
+       -n|--no-stat|--no-summary)
+               no_stat=-n ;;
+       --stat|--summary)
+               no_stat=$1 ;;
+       --log|--no-log)
+               log_arg=$1 ;;
        --no-c|--no-co|--no-com|--no-comm|--no-commi|--no-commit)
                no_commit=--no-commit ;;
        --c|--co|--com|--comm|--commi|--commit)
@@ -107,6 +107,11 @@ error_on_no_merge_candidates () {
 }
 
 test true = "$rebase" && {
+       git update-index --refresh &&
+       git diff-files --quiet &&
+       git diff-index --cached --quiet HEAD -- ||
+       die "refusing to pull with rebase: your working tree is not up-to-date"
+
        . git-parse-remote &&
        origin="$1"
        test -z "$origin" && origin=$(get_default_remote)
@@ -172,9 +177,9 @@ then
        exit
 fi
 
-merge_name=$(git fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
+merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 test true = "$rebase" &&
        exec git-rebase $strategy_args --onto $merge_head \
        ${oldremoteref:-$merge_head}
-exec git-merge $no_summary $no_commit $squash $no_ff $strategy_args \
+exec git-merge $no_stat $no_commit $squash $no_ff $log_arg $strategy_args \
        "$merge_name" HEAD $merge_head
index 9b13b833cb5762542848ee3e85e23d3ca0f76fa6..68855c18ae62ef6f4a3fdacb6f8de9c57511aa79 100755 (executable)
@@ -214,7 +214,7 @@ do
                else
                        die "No rebase in progress?"
                fi
-               git reset --hard $(cat $dotest/orig-head)
+               git reset --hard $(cat "$dotest/orig-head")
                rm -r "$dotest"
                exit
                ;;
@@ -353,7 +353,7 @@ orig_head=$branch
 mb=$(git merge-base "$onto" "$branch")
 if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
        # linear history?
-       ! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null
+       ! (git rev-list --parents "$onto".."$branch" | grep " .* ") > /dev/null
 then
        # Lazily switch to the target branch if needed...
        test -z "$switch_to" || git checkout "$switch_to"
index e18eb3f5dcf42abfbd125594877ececf92c3d9b6..10f735cff561c6606f5c2bd3f9feec1d6f6ae52e 100755 (executable)
@@ -8,9 +8,10 @@ OPTIONS_SPEC="\
 git-repack [options]
 --
 a               pack everything in a single pack
-A               same as -a, and keep unreachable objects too
+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
+n               do not run git-update-server-info
 q,quiet         be quiet
 l               pass --local to git-pack-objects
  Packing constraints
@@ -22,7 +23,7 @@ max-pack-size=  maximum size of each packfile
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
-no_update_info= all_into_one= remove_redundant= keep_unreachable=
+no_update_info= all_into_one= remove_redundant= unpack_unreachable=
 local= quiet= no_reuse= extra=
 while test $# != 0
 do
@@ -30,7 +31,7 @@ do
        -n)     no_update_info=t ;;
        -a)     all_into_one=t ;;
        -A)     all_into_one=t
-               keep_unreachable=--keep-unreachable ;;
+               unpack_unreachable=--unpack-unreachable ;;
        -d)     remove_redundant=t ;;
        -q)     quiet=-q ;;
        -f)     no_reuse=--no-reuse-object ;;
@@ -78,9 +79,9 @@ case ",$all_into_one," in
        if test -z "$args"
        then
                args='--unpacked --incremental'
-       elif test -n "$keep_unreachable"
+       elif test -n "$unpack_unreachable"
        then
-               args="$args $keep_unreachable"
+               args="$args $unpack_unreachable"
        fi
        ;;
 esac
index 9e568bf9c012b610fd8db7e4444cb7ff8c6e0a0f..a598fdc890c817875766c29aa714d622af770f4b 100755 (executable)
@@ -203,6 +203,7 @@ sub format_2822_time {
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
     "to" => \@to,
+    "cc" => \@initial_cc,
     "cccmd" => \$cc_cmd,
     "aliasfiletype" => \$aliasfiletype,
     "bcc" => \@bcclist,
@@ -512,7 +513,7 @@ sub expand_aliases {
        close(C);
 
        my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
-       system('sh', '-c', '$0 $@', $editor, $compose_filename);
+       system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
 
        open(C2,">",$compose_filename . ".final")
                or die "Failed to open $compose_filename.final : " . $!;
@@ -520,8 +521,30 @@ sub expand_aliases {
        open(C,"<",$compose_filename)
                or die "Failed to open $compose_filename : " . $!;
 
+       my $need_8bit_cte = file_has_nonascii($compose_filename);
+       my $in_body = 0;
        while(<C>) {
                next if m/^GIT: /;
+               if (!$in_body && /^\n$/) {
+                       $in_body = 1;
+                       if ($need_8bit_cte) {
+                               print C2 "MIME-Version: 1.0\n",
+                                        "Content-Type: text/plain; ",
+                                          "charset=utf-8\n",
+                                        "Content-Transfer-Encoding: 8bit\n";
+                       }
+               }
+               if (!$in_body && /^MIME-Version:/i) {
+                       $need_8bit_cte = 0;
+               }
+               if (!$in_body && /^Subject: ?(.*)/i) {
+                       my $subject = $1;
+                       $_ = "Subject: " .
+                               ($subject =~ /[^[:ascii:]]/ ?
+                                quote_rfc2047($subject) :
+                                $subject) .
+                               "\n";
+               }
                print C2 $_;
        }
        close(C);
@@ -612,6 +635,14 @@ sub unquote_rfc2047 {
        return wantarray ? ($_, $encoding) : $_;
 }
 
+sub quote_rfc2047 {
+       local $_ = shift;
+       my $encoding = shift || 'utf-8';
+       s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
+       s/(.*)/=\?$encoding\?q\?$1\?=/;
+       return $_;
+}
+
 # use the simplest quoting being able to handle the recipient
 sub sanitize_address
 {
@@ -629,13 +660,12 @@ sub sanitize_address
 
        # rfc2047 is needed if a non-ascii char is included
        if ($recipient_name =~ /[^[:ascii:]]/) {
-               $recipient_name =~ s/([^-a-zA-Z0-9!*+\/])/sprintf("=%02X", ord($1))/eg;
-               $recipient_name =~ s/(.*)/=\?utf-8\?q\?$1\?=/;
+               $recipient_name = quote_rfc2047($recipient_name);
        }
 
        # double quotes are needed if specials or CTLs are included
        elsif ($recipient_name =~ /[][()<>@,;:\\".\000-\037\177]/) {
-               $recipient_name =~ s/(["\\\r])/\\$1/;
+               $recipient_name =~ s/(["\\\r])/\\$1/g;
                $recipient_name = "\"$recipient_name\"";
        }
 
@@ -958,3 +988,13 @@ sub validate_patch {
        }
        return undef;
 }
+
+sub file_has_nonascii {
+       my $fn = shift;
+       open(my $fh, '<', $fn)
+               or die "unable to open $fn: $!\n";
+       while (my $line = <$fh>) {
+               return 1 if $line =~ /[^[:ascii:]]/;
+       }
+       return 0;
+}
index ce0f00c8a4f6d5f9a2c1c3ebdbadff52106b60d0..100737210de3c76682ab3803626a36328fb27ca1 100755 (executable)
@@ -5,7 +5,7 @@
 # Copyright (c) 2007 Lars Hjemli
 
 USAGE="[--quiet] [--cached] \
-[add <repo> [-b branch]|status|init|update|summary [-n|--summary-limit <n>] [<commit>]] \
+[add <repo> [-b branch]|status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
 [--] [<path>...]"
 OPTIONS_SPEC=
 . git-sh-setup
@@ -74,8 +74,7 @@ module_name()
 {
        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
        re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
-       name=$( GIT_CONFIG=.gitmodules \
-               git config --get-regexp '^submodule\..*\.path$' |
+       name=$( git config -f .gitmodules --get-regexp '^submodule\..*\.path$' |
                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
        test -z "$name" &&
        die "No submodule mapping found in .gitmodules for path '$path'"
@@ -198,8 +197,8 @@ cmd_add()
        git add "$path" ||
        die "Failed to add submodule '$path'"
 
-       GIT_CONFIG=.gitmodules git config submodule."$path".path "$path" &&
-       GIT_CONFIG=.gitmodules git config submodule."$path".url "$repo" &&
+       git config -f .gitmodules submodule."$path".path "$path" &&
+       git config -f .gitmodules submodule."$path".url "$repo" &&
        git add .gitmodules ||
        die "Failed to register submodule '$path'"
 }
@@ -240,7 +239,7 @@ cmd_init()
                url=$(git config submodule."$name".url)
                test -z "$url" || continue
 
-               url=$(GIT_CONFIG=.gitmodules git config submodule."$name".url)
+               url=$(git config -f .gitmodules submodule."$name".url)
                test -z "$url" &&
                die "No url found for submodule path '$path' in .gitmodules"
 
@@ -272,6 +271,10 @@ cmd_update()
                -q|--quiet)
                        quiet=1
                        ;;
+               -i|--init)
+                       shift
+                       cmd_init "$@" || return
+                       ;;
                --)
                        shift
                        break
@@ -297,10 +300,11 @@ cmd_update()
                        # path have been specified
                        test "$#" != "0" &&
                        say "Submodule path '$path' not initialized"
+                       say "Maybe you want to use 'update --init'?"
                        continue
                fi
 
-               if ! test -d "$path"/.git
+               if ! test -d "$path"/.git -o -f "$path"/.git
                then
                        module_clone "$path" "$url" || exit
                        subsha1=
@@ -555,7 +559,7 @@ cmd_status()
        do
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
-               if test -z "$url" || ! test -d "$path"/.git
+               if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
                then
                        say "-$sha1 $path"
                        continue;
index b70f8efaaa404d63c6a45486f8513348cbb368f5..2c53f39aefa0131cb15ab679e6b2633622c000a1 100755 (executable)
@@ -65,7 +65,8 @@ BEGIN
        $_template, $_shared,
        $_version, $_fetch_all, $_no_rebase,
        $_merge, $_strategy, $_dry_run, $_local,
-       $_prefix, $_no_checkout, $_url, $_verbose);
+       $_prefix, $_no_checkout, $_url, $_verbose,
+       $_git_format);
 $Git::SVN::_follow_parent = 1;
 my %remote_opts = ( 'username=s' => \$Git::SVN::Prompt::_username,
                     'config-dir=s' => \$Git::SVN::Ra::config_dir,
@@ -188,7 +189,7 @@ BEGIN
                    { 'url' => \$_url, } ],
        'blame' => [ \&Git::SVN::Log::cmd_blame,
                    "Show what revision and author last modified each line of a file",
-                   {} ],
+                   { 'git-format' => \$_git_format } ],
 );
 
 my $cmd;
@@ -225,7 +226,7 @@ BEGIN
 my %opts = %{$cmd{$cmd}->[2]} if (defined $cmd);
 
 read_repo_config(\%opts);
-Getopt::Long::Configure('pass_through') if ($cmd && $cmd eq 'log');
+Getopt::Long::Configure('pass_through') if ($cmd && ($cmd eq 'log' || $cmd eq 'blame'));
 my $rv = GetOptions(%opts, 'help|H|h' => \$_help, 'version|V' => \$_version,
                     'minimize-connections' => \$Git::SVN::Migration::_minimize,
                     'id|i=s' => \$Git::SVN::default_ref_id,
@@ -614,7 +615,7 @@ sub cmd_create_ignore {
                print GITIGNORE "$s\n";
                close(GITIGNORE)
                  or fatal("Failed to close `$ignore': $!");
-               command_noisy('add', $ignore);
+               command_noisy('add', '-f', $ignore);
        });
 }
 
@@ -3673,7 +3674,7 @@ sub escape_uri_only {
        my ($uri) = @_;
        my @tmp;
        foreach (split m{/}, $uri) {
-               s/([^\w.%-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
+               s/([^\w.%+-]|%(?![a-fA-F0-9]{2}))/sprintf("%%%02X",ord($1))/eg;
                push @tmp, $_;
        }
        join('/', @tmp);
@@ -4468,19 +4469,51 @@ sub cmd_show_log {
 }
 
 sub cmd_blame {
-       my $path = shift;
+       my $path = pop;
 
        config_pager();
        run_pager();
 
-       my ($fh, $ctx) = command_output_pipe('blame', @_, $path);
-       while (my $line = <$fh>) {
-               if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
-                       my (undef, $rev, undef) = ::cmt_metadata($1);
-                       $rev = sprintf('%-10s', $rev);
-                       $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+       my ($fh, $ctx, $rev);
+
+       if ($_git_format) {
+               ($fh, $ctx) = command_output_pipe('blame', @_, $path);
+               while (my $line = <$fh>) {
+                       if ($line =~ /^\^?([[:xdigit:]]+)\s/) {
+                               # Uncommitted edits show up as a rev ID of
+                               # all zeros, which we can't look up with
+                               # cmt_metadata
+                               if ($1 !~ /^0+$/) {
+                                       (undef, $rev, undef) =
+                                               ::cmt_metadata($1);
+                                       $rev = '0' if (!$rev);
+                               } else {
+                                       $rev = '0';
+                               }
+                               $rev = sprintf('%-10s', $rev);
+                               $line =~ s/^\^?[[:xdigit:]]+(\s)/$rev$1/;
+                       }
+                       print $line;
+               }
+       } else {
+               ($fh, $ctx) = command_output_pipe('blame', '-p', @_, 'HEAD',
+                                                 '--', $path);
+               my ($sha1);
+               my %authors;
+               while (my $line = <$fh>) {
+                       if ($line =~ /^([[:xdigit:]]{40})\s\d+\s\d+/) {
+                               $sha1 = $1;
+                               (undef, $rev, undef) = ::cmt_metadata($1);
+                               $rev = '0' if (!$rev);
+                       }
+                       elsif ($line =~ /^author (.*)/) {
+                               $authors{$rev} = $1;
+                               $authors{$rev} =~ s/\s/_/g;
+                       }
+                       elsif ($line =~ /^\t(.*)$/) {
+                               printf("%6s %10s %s\n", $rev, $authors{$rev}, $1);
+                       }
                }
-               print $line;
        }
        command_close_pipe($fh, $ctx);
 }
index f90dfabffa894741f500e513d5380c7752d0195c..e1b6045605865cbfc4ae0d57039111d5df825649 100644 (file)
@@ -40,9 +40,9 @@ endif
 all:: gitk-wish $(ALL_MSGFILES)
 
 install:: all
-       $(INSTALL) gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
-       $(INSTALL) -d '$(DESTDIR_SQ)$(msgsdir_SQ)'
-       $(foreach p,$(ALL_MSGFILES), $(INSTALL) $p '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true
+       $(INSTALL) -m 755 gitk-wish '$(DESTDIR_SQ)$(bindir_SQ)'/gitk
+       $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(msgsdir_SQ)'
+       $(foreach p,$(ALL_MSGFILES), $(INSTALL) -m 644 $p '$(DESTDIR_SQ)$(msgsdir_SQ)' &&) true
 
 uninstall::
        $(foreach p,$(ALL_MSGFILES), $(RM) '$(DESTDIR_SQ)$(msgsdir_SQ)'/$(notdir $p) &&) true
index 9a4d9c40cf6ec1bed01a2c2b950efb2d9887a8a5..9ab6dbaa460cc4c63398e836506c204d382419b4 100644 (file)
@@ -2,7 +2,7 @@
 # Tcl ignores the next line -*- tcl -*- \
 exec wish "$0" -- "$@"
 
-# Copyright (C) 2005-2006 Paul Mackerras.  All rights reserved.
+# Copyright © 2005-2008 Paul Mackerras.  All rights reserved.
 # This program is free software; it may be used, copied, modified
 # and distributed under the terms of the GNU General Public Licence,
 # either version 2, or (at your option) any later version.
@@ -47,12 +47,24 @@ proc filereadable {fd script} {
     lappend runq [list $fd $script]
 }
 
+proc nukefile {fd} {
+    global runq
+
+    for {set i 0} {$i < [llength $runq]} {} {
+       if {[lindex $runq $i 0] eq $fd} {
+           set runq [lreplace $runq $i $i]
+       } else {
+           incr i
+       }
+    }
+}
+
 proc dorunq {} {
     global isonrunq runq
 
     set tstart [clock clicks -milliseconds]
     set t0 $tstart
-    while {$runq ne {}} {
+    while {[llength $runq] > 0} {
        set fd [lindex $runq 0 0]
        set script [lindex $runq 0 1]
        set repeat [eval $script]
@@ -78,41 +90,274 @@ proc dorunq {} {
     }
 }
 
-# Start off a git rev-list process and arrange to read its output
+proc unmerged_files {files} {
+    global nr_unmerged
+
+    # find the list of unmerged files
+    set mlist {}
+    set nr_unmerged 0
+    if {[catch {
+       set fd [open "| git ls-files -u" r]
+    } err]} {
+       show_error {} . "[mc "Couldn't get list of unmerged files:"] $err"
+       exit 1
+    }
+    while {[gets $fd line] >= 0} {
+       set i [string first "\t" $line]
+       if {$i < 0} continue
+       set fname [string range $line [expr {$i+1}] end]
+       if {[lsearch -exact $mlist $fname] >= 0} continue
+       incr nr_unmerged
+       if {$files eq {} || [path_filter $files $fname]} {
+           lappend mlist $fname
+       }
+    }
+    catch {close $fd}
+    return $mlist
+}
+
+proc parseviewargs {n arglist} {
+    global vdatemode vmergeonly vflags vdflags vrevs vfiltered vorigargs
+
+    set vdatemode($n) 0
+    set vmergeonly($n) 0
+    set glflags {}
+    set diffargs {}
+    set nextisval 0
+    set revargs {}
+    set origargs $arglist
+    set allknown 1
+    set filtered 0
+    set i -1
+    foreach arg $arglist {
+       incr i
+       if {$nextisval} {
+           lappend glflags $arg
+           set nextisval 0
+           continue
+       }
+       switch -glob -- $arg {
+           "-d" -
+           "--date-order" {
+               set vdatemode($n) 1
+               # remove from origargs in case we hit an unknown option
+               set origargs [lreplace $origargs $i $i]
+               incr i -1
+           }
+           # These request or affect diff output, which we don't want.
+           # Some could be used to set our defaults for diff display.
+           "-[puabwcrRBMC]" -
+           "--no-renames" - "--full-index" - "--binary" - "--abbrev=*" -
+           "--find-copies-harder" - "-l*" - "--ext-diff" - "--no-ext-diff" -
+           "--src-prefix=*" - "--dst-prefix=*" - "--no-prefix" -
+           "-O*" - "--text" - "--full-diff" - "--ignore-space-at-eol" -
+           "--ignore-space-change" - "-U*" - "--unified=*" {
+               lappend diffargs $arg
+           }
+           # These cause our parsing of git log's output to fail, or else
+           # they're options we want to set ourselves, so ignore them.
+           "--raw" - "--patch-with-raw" - "--patch-with-stat" -
+           "--name-only" - "--name-status" - "--color" - "--color-words" -
+           "--log-size" - "--pretty=*" - "--decorate" - "--abbrev-commit" -
+           "--cc" - "-z" - "--header" - "--parents" - "--boundary" -
+           "--no-color" - "-g" - "--walk-reflogs" - "--no-walk" -
+           "--timestamp" - "relative-date" - "--date=*" - "--stdin" -
+           "--objects" - "--objects-edge" - "--reverse" {
+           }
+           # These are harmless, and some are even useful
+           "--stat=*" - "--numstat" - "--shortstat" - "--summary" -
+           "--check" - "--exit-code" - "--quiet" - "--topo-order" -
+           "--full-history" - "--dense" - "--sparse" -
+           "--follow" - "--left-right" - "--encoding=*" {
+               lappend glflags $arg
+           }
+           # These mean that we get a subset of the commits
+           "--diff-filter=*" - "--no-merges" - "--unpacked" -
+           "--max-count=*" - "--skip=*" - "--since=*" - "--after=*" -
+           "--until=*" - "--before=*" - "--max-age=*" - "--min-age=*" -
+           "--author=*" - "--committer=*" - "--grep=*" - "-[iE]" -
+           "--remove-empty" - "--first-parent" - "--cherry-pick" -
+           "-S*" - "--pickaxe-all" - "--pickaxe-regex" - {
+               set filtered 1
+               lappend glflags $arg
+           }
+           # This appears to be the only one that has a value as a
+           # separate word following it
+           "-n" {
+               set filtered 1
+               set nextisval 1
+               lappend glflags $arg
+           }
+           "--not" {
+               set notflag [expr {!$notflag}]
+               lappend revargs $arg
+           }
+           "--all" {
+               lappend revargs $arg
+           }
+           "--merge" {
+               set vmergeonly($n) 1
+               # git rev-parse doesn't understand --merge
+               lappend revargs --gitk-symmetric-diff-marker MERGE_HEAD...HEAD
+           }
+           # Other flag arguments including -<n>
+           "-*" {
+               if {[string is digit -strict [string range $arg 1 end]]} {
+                   set filtered 1
+               } else {
+                   # a flag argument that we don't recognize;
+                   # that means we can't optimize
+                   set allknown 0
+               }
+               lappend glflags $arg
+           }
+           # Non-flag arguments specify commits or ranges of commits
+           default {
+               if {[string match "*...*" $arg]} {
+                   lappend revargs --gitk-symmetric-diff-marker
+               }
+               lappend revargs $arg
+           }
+       }
+    }
+    set vdflags($n) $diffargs
+    set vflags($n) $glflags
+    set vrevs($n) $revargs
+    set vfiltered($n) $filtered
+    set vorigargs($n) $origargs
+    return $allknown
+}
+
+proc parseviewrevs {view revs} {
+    global vposids vnegids
+
+    if {$revs eq {}} {
+       set revs HEAD
+    }
+    if {[catch {set ids [eval exec git rev-parse $revs]} err]} {
+       # we get stdout followed by stderr in $err
+       # for an unknown rev, git rev-parse echoes it and then errors out
+       set errlines [split $err "\n"]
+       set badrev {}
+       for {set l 0} {$l < [llength $errlines]} {incr l} {
+           set line [lindex $errlines $l]
+           if {!([string length $line] == 40 && [string is xdigit $line])} {
+               if {[string match "fatal:*" $line]} {
+                   if {[string match "fatal: ambiguous argument*" $line]
+                       && $badrev ne {}} {
+                       if {[llength $badrev] == 1} {
+                           set err "unknown revision $badrev"
+                       } else {
+                           set err "unknown revisions: [join $badrev ", "]"
+                       }
+                   } else {
+                       set err [join [lrange $errlines $l end] "\n"]
+                   }
+                   break
+               }
+               lappend badrev $line
+           }
+       }                   
+       error_popup "Error parsing revisions: $err"
+       return {}
+    }
+    set ret {}
+    set pos {}
+    set neg {}
+    set sdm 0
+    foreach id [split $ids "\n"] {
+       if {$id eq "--gitk-symmetric-diff-marker"} {
+           set sdm 4
+       } elseif {[string match "^*" $id]} {
+           if {$sdm != 1} {
+               lappend ret $id
+               if {$sdm == 3} {
+                   set sdm 0
+               }
+           }
+           lappend neg [string range $id 1 end]
+       } else {
+           if {$sdm != 2} {
+               lappend ret $id
+           } else {
+               lset ret end [lindex $ret end]...$id
+           }
+           lappend pos $id
+       }
+       incr sdm -1
+    }
+    set vposids($view) $pos
+    set vnegids($view) $neg
+    return $ret
+}
+
+# Start off a git log process and arrange to read its output
 proc start_rev_list {view} {
-    global startmsecs
-    global commfd leftover tclencoding datemode
-    global viewargs viewargscmd viewfiles commitidx viewcomplete vnextroot
+    global startmsecs commitidx viewcomplete curview
+    global commfd leftover tclencoding
+    global viewargs viewargscmd viewfiles vfilelimit
     global showlocalchanges commitinterest mainheadid
-    global progressdirn progresscoords proglastnc curview
+    global viewactive loginstance viewinstances vmergeonly
+    global pending_select mainheadid
+    global vcanopt vflags vrevs vorigargs
 
     set startmsecs [clock clicks -milliseconds]
     set commitidx($view) 0
-    set viewcomplete($view) 0
-    set vnextroot($view) 0
+    # these are set this way for the error exits
+    set viewcomplete($view) 1
+    set viewactive($view) 0
+    varcinit $view
+
     set args $viewargs($view)
     if {$viewargscmd($view) ne {}} {
        if {[catch {
            set str [exec sh -c $viewargscmd($view)]
        } err]} {
            error_popup "Error executing --argscmd command: $err"
-           exit 1
+           return 0
        }
        set args [concat $args [split $str "\n"]]
     }
-    set order "--topo-order"
-    if {$datemode} {
-       set order "--date-order"
+    set vcanopt($view) [parseviewargs $view $args]
+
+    set files $viewfiles($view)
+    if {$vmergeonly($view)} {
+       set files [unmerged_files $files]
+       if {$files eq {}} {
+           global nr_unmerged
+           if {$nr_unmerged == 0} {
+               error_popup [mc "No files selected: --merge specified but\
+                            no files are unmerged."]
+           } else {
+               error_popup [mc "No files selected: --merge specified but\
+                            no unmerged files are within file limit."]
+           }
+           return 0
+       }
     }
+    set vfilelimit($view) $files
+
+    if {$vcanopt($view)} {
+       set revs [parseviewrevs $view $vrevs($view)]
+       if {$revs eq {}} {
+           return 0
+       }
+       set args [concat $vflags($view) $revs]
+    } else {
+       set args $vorigargs($view)
+    }
+
     if {[catch {
-       set fd [open [concat | git log --no-color -z --pretty=raw $order --parents \
-                        --boundary $args "--" $viewfiles($view)] r]
+       set fd [open [concat | git log --no-color -z --pretty=raw --parents \
+                        --boundary $args "--" $files] r]
     } err]} {
-       error_popup "[mc "Error executing git rev-list:"] $err"
-       exit 1
+       error_popup "[mc "Error executing git log:"] $err"
+       return 0
     }
-    set commfd($view) $fd
-    set leftover($view) {}
+    set i [incr loginstance]
+    set viewinstances($view) [list $i]
+    set commfd($i) $fd
+    set leftover($i) {}
     if {$showlocalchanges} {
        lappend commitinterest($mainheadid) {dodiffindex}
     }
@@ -120,35 +365,152 @@ proc start_rev_list {view} {
     if {$tclencoding != {}} {
        fconfigure $fd -encoding $tclencoding
     }
-    filerun $fd [list getcommitlines $fd $view]
+    filerun $fd [list getcommitlines $fd $i $view 0]
     nowbusy $view [mc "Reading"]
     if {$view == $curview} {
-       set progressdirn 1
-       set progresscoords {0 0}
-       set proglastnc 0
+       set pending_select $mainheadid
     }
+    set viewcomplete($view) 0
+    set viewactive($view) 1
+    return 1
 }
 
-proc stop_rev_list {} {
-    global commfd curview
+proc stop_rev_list {view} {
+    global commfd viewinstances leftover
 
-    if {![info exists commfd($curview)]} return
-    set fd $commfd($curview)
-    catch {
-       set pid [pid $fd]
-       exec kill $pid
+    foreach inst $viewinstances($view) {
+       set fd $commfd($inst)
+       catch {
+           set pid [pid $fd]
+           exec kill $pid
+       }
+       catch {close $fd}
+       nukefile $fd
+       unset commfd($inst)
+       unset leftover($inst)
     }
-    catch {close $fd}
-    unset commfd($curview)
+    set viewinstances($view) {}
 }
 
 proc getcommits {} {
-    global phase canv curview
+    global canv curview need_redisplay viewactive
 
-    set phase getcommits
     initlayout
-    start_rev_list $curview
-    show_status [mc "Reading commits..."]
+    if {[start_rev_list $curview]} {
+       show_status [mc "Reading commits..."]
+       set need_redisplay 1
+    } else {
+       show_status [mc "No commits selected"]
+    }
+}
+
+proc updatecommits {} {
+    global curview vcanopt vorigargs vfilelimit viewinstances
+    global viewactive viewcomplete loginstance tclencoding mainheadid
+    global startmsecs commfd showneartags showlocalchanges leftover
+    global mainheadid pending_select
+    global isworktree
+    global varcid vposids vnegids vflags vrevs
+
+    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
+    set oldmainid $mainheadid
+    rereadrefs
+    if {$showlocalchanges} {
+       if {$mainheadid ne $oldmainid} {
+           dohidelocalchanges
+       }
+       if {[commitinview $mainheadid $curview]} {
+           dodiffindex
+       }
+    }
+    set view $curview
+    if {$vcanopt($view)} {
+       set oldpos $vposids($view)
+       set oldneg $vnegids($view)
+       set revs [parseviewrevs $view $vrevs($view)]
+       if {$revs eq {}} {
+           return
+       }
+       # note: getting the delta when negative refs change is hard,
+       # and could require multiple git log invocations, so in that
+       # case we ask git log for all the commits (not just the delta)
+       if {$oldneg eq $vnegids($view)} {
+           set newrevs {}
+           set npos 0
+           # take out positive refs that we asked for before or
+           # that we have already seen
+           foreach rev $revs {
+               if {[string length $rev] == 40} {
+                   if {[lsearch -exact $oldpos $rev] < 0
+                       && ![info exists varcid($view,$rev)]} {
+                       lappend newrevs $rev
+                       incr npos
+                   }
+               } else {
+                   lappend $newrevs $rev
+               }
+           }
+           if {$npos == 0} return
+           set revs $newrevs
+           set vposids($view) [lsort -unique [concat $oldpos $vposids($view)]]
+       }
+       set args [concat $vflags($view) $revs --not $oldpos]
+    } else {
+       set args $vorigargs($view)
+    }
+    if {[catch {
+       set fd [open [concat | git log --no-color -z --pretty=raw --parents \
+                         --boundary $args "--" $vfilelimit($view)] r]
+    } err]} {
+       error_popup "Error executing git log: $err"
+       return
+    }
+    if {$viewactive($view) == 0} {
+       set startmsecs [clock clicks -milliseconds]
+    }
+    set i [incr loginstance]
+    lappend viewinstances($view) $i
+    set commfd($i) $fd
+    set leftover($i) {}
+    fconfigure $fd -blocking 0 -translation lf -eofchar {}
+    if {$tclencoding != {}} {
+       fconfigure $fd -encoding $tclencoding
+    }
+    filerun $fd [list getcommitlines $fd $i $view 1]
+    incr viewactive($view)
+    set viewcomplete($view) 0
+    set pending_select $mainheadid
+    nowbusy $view "Reading"
+    if {$showneartags} {
+       getallcommits
+    }
+}
+
+proc reloadcommits {} {
+    global curview viewcomplete selectedline currentid thickerline
+    global showneartags treediffs commitinterest cached_commitrow
+    global targetid
+
+    if {!$viewcomplete($curview)} {
+       stop_rev_list $curview
+    }
+    resetvarcs $curview
+    catch {unset selectedline}
+    catch {unset currentid}
+    catch {unset thickerline}
+    catch {unset treediffs}
+    readrefs
+    changedrefs
+    if {$showneartags} {
+       getallcommits
+    }
+    clear_display
+    catch {unset commitinterest}
+    catch {unset cached_commitrow}
+    catch {unset targetid}
+    setcanvscroll
+    getcommits
+    return 0
 }
 
 # This makes a string representation of a positive integer which
@@ -164,46 +526,759 @@ proc strrep {n} {
     return [format "z%.8x" $n]
 }
 
-proc getcommitlines {fd view}  {
-    global commitlisted commitinterest
-    global leftover commfd
-    global displayorder commitidx viewcomplete commitrow commitdata
-    global parentlist children curview hlview
-    global vparentlist vdisporder vcmitlisted
-    global ordertok vnextroot idpending
+# Procedures used in reordering commits from git log (without
+# --topo-order) into the order for display.
+
+proc varcinit {view} {
+    global varcstart vupptr vdownptr vleftptr vbackptr varctok varcrow
+    global vtokmod varcmod vrowmod varcix vlastins
+
+    set varcstart($view) {{}}
+    set vupptr($view) {0}
+    set vdownptr($view) {0}
+    set vleftptr($view) {0}
+    set vbackptr($view) {0}
+    set varctok($view) {{}}
+    set varcrow($view) {{}}
+    set vtokmod($view) {}
+    set varcmod($view) 0
+    set vrowmod($view) 0
+    set varcix($view) {{}}
+    set vlastins($view) {0}
+}
+
+proc resetvarcs {view} {
+    global varcid varccommits parents children vseedcount ordertok
+
+    foreach vid [array names varcid $view,*] {
+       unset varcid($vid)
+       unset children($vid)
+       unset parents($vid)
+    }
+    # some commits might have children but haven't been seen yet
+    foreach vid [array names children $view,*] {
+       unset children($vid)
+    }
+    foreach va [array names varccommits $view,*] {
+       unset varccommits($va)
+    }
+    foreach vd [array names vseedcount $view,*] {
+       unset vseedcount($vd)
+    }
+    catch {unset ordertok}
+}
+
+# returns a list of the commits with no children
+proc seeds {v} {
+    global vdownptr vleftptr varcstart
+
+    set ret {}
+    set a [lindex $vdownptr($v) 0]
+    while {$a != 0} {
+       lappend ret [lindex $varcstart($v) $a]
+       set a [lindex $vleftptr($v) $a]
+    }
+    return $ret
+}
+
+proc newvarc {view id} {
+    global varcid varctok parents children vdatemode
+    global vupptr vdownptr vleftptr vbackptr varcrow varcix varcstart
+    global commitdata commitinfo vseedcount varccommits vlastins
+
+    set a [llength $varctok($view)]
+    set vid $view,$id
+    if {[llength $children($vid)] == 0 || $vdatemode($view)} {
+       if {![info exists commitinfo($id)]} {
+           parsecommit $id $commitdata($id) 1
+       }
+       set cdate [lindex $commitinfo($id) 4]
+       if {![string is integer -strict $cdate]} {
+           set cdate 0
+       }
+       if {![info exists vseedcount($view,$cdate)]} {
+           set vseedcount($view,$cdate) -1
+       }
+       set c [incr vseedcount($view,$cdate)]
+       set cdate [expr {$cdate ^ 0xffffffff}]
+       set tok "s[strrep $cdate][strrep $c]"
+    } else {
+       set tok {}
+    }
+    set ka 0
+    if {[llength $children($vid)] > 0} {
+       set kid [lindex $children($vid) end]
+       set k $varcid($view,$kid)
+       if {[string compare [lindex $varctok($view) $k] $tok] > 0} {
+           set ki $kid
+           set ka $k
+           set tok [lindex $varctok($view) $k]
+       }
+    }
+    if {$ka != 0} {
+       set i [lsearch -exact $parents($view,$ki) $id]
+       set j [expr {[llength $parents($view,$ki)] - 1 - $i}]
+       append tok [strrep $j]
+    }
+    set c [lindex $vlastins($view) $ka]
+    if {$c == 0 || [string compare $tok [lindex $varctok($view) $c]] < 0} {
+       set c $ka
+       set b [lindex $vdownptr($view) $ka]
+    } else {
+       set b [lindex $vleftptr($view) $c]
+    }
+    while {$b != 0 && [string compare $tok [lindex $varctok($view) $b]] >= 0} {
+       set c $b
+       set b [lindex $vleftptr($view) $c]
+    }
+    if {$c == $ka} {
+       lset vdownptr($view) $ka $a
+       lappend vbackptr($view) 0
+    } else {
+       lset vleftptr($view) $c $a
+       lappend vbackptr($view) $c
+    }
+    lset vlastins($view) $ka $a
+    lappend vupptr($view) $ka
+    lappend vleftptr($view) $b
+    if {$b != 0} {
+       lset vbackptr($view) $b $a
+    }
+    lappend varctok($view) $tok
+    lappend varcstart($view) $id
+    lappend vdownptr($view) 0
+    lappend varcrow($view) {}
+    lappend varcix($view) {}
+    set varccommits($view,$a) {}
+    lappend vlastins($view) 0
+    return $a
+}
+
+proc splitvarc {p v} {
+    global varcid varcstart varccommits varctok
+    global vupptr vdownptr vleftptr vbackptr varcix varcrow vlastins
+
+    set oa $varcid($v,$p)
+    set ac $varccommits($v,$oa)
+    set i [lsearch -exact $varccommits($v,$oa) $p]
+    if {$i <= 0} return
+    set na [llength $varctok($v)]
+    # "%" sorts before "0"...
+    set tok "[lindex $varctok($v) $oa]%[strrep $i]"
+    lappend varctok($v) $tok
+    lappend varcrow($v) {}
+    lappend varcix($v) {}
+    set varccommits($v,$oa) [lrange $ac 0 [expr {$i - 1}]]
+    set varccommits($v,$na) [lrange $ac $i end]
+    lappend varcstart($v) $p
+    foreach id $varccommits($v,$na) {
+       set varcid($v,$id) $na
+    }
+    lappend vdownptr($v) [lindex $vdownptr($v) $oa]
+    lappend vlastins($v) [lindex $vlastins($v) $oa]
+    lset vdownptr($v) $oa $na
+    lset vlastins($v) $oa 0
+    lappend vupptr($v) $oa
+    lappend vleftptr($v) 0
+    lappend vbackptr($v) 0
+    for {set b [lindex $vdownptr($v) $na]} {$b != 0} {set b [lindex $vleftptr($v) $b]} {
+       lset vupptr($v) $b $na
+    }
+}
+
+proc renumbervarc {a v} {
+    global parents children varctok varcstart varccommits
+    global vupptr vdownptr vleftptr vbackptr vlastins varcid vtokmod vdatemode
+
+    set t1 [clock clicks -milliseconds]
+    set todo {}
+    set isrelated($a) 1
+    set kidchanged($a) 1
+    set ntot 0
+    while {$a != 0} {
+       if {[info exists isrelated($a)]} {
+           lappend todo $a
+           set id [lindex $varccommits($v,$a) end]
+           foreach p $parents($v,$id) {
+               if {[info exists varcid($v,$p)]} {
+                   set isrelated($varcid($v,$p)) 1
+               }
+           }
+       }
+       incr ntot
+       set b [lindex $vdownptr($v) $a]
+       if {$b == 0} {
+           while {$a != 0} {
+               set b [lindex $vleftptr($v) $a]
+               if {$b != 0} break
+               set a [lindex $vupptr($v) $a]
+           }
+       }
+       set a $b
+    }
+    foreach a $todo {
+       if {![info exists kidchanged($a)]} continue
+       set id [lindex $varcstart($v) $a]
+       if {[llength $children($v,$id)] > 1} {
+           set children($v,$id) [lsort -command [list vtokcmp $v] \
+                                     $children($v,$id)]
+       }
+       set oldtok [lindex $varctok($v) $a]
+       if {!$vdatemode($v)} {
+           set tok {}
+       } else {
+           set tok $oldtok
+       }
+       set ka 0
+       set kid [last_real_child $v,$id]
+       if {$kid ne {}} {
+           set k $varcid($v,$kid)
+           if {[string compare [lindex $varctok($v) $k] $tok] > 0} {
+               set ki $kid
+               set ka $k
+               set tok [lindex $varctok($v) $k]
+           }
+       }
+       if {$ka != 0} {
+           set i [lsearch -exact $parents($v,$ki) $id]
+           set j [expr {[llength $parents($v,$ki)] - 1 - $i}]
+           append tok [strrep $j]
+       }
+       if {$tok eq $oldtok} {
+           continue
+       }
+       set id [lindex $varccommits($v,$a) end]
+       foreach p $parents($v,$id) {
+           if {[info exists varcid($v,$p)]} {
+               set kidchanged($varcid($v,$p)) 1
+           } else {
+               set sortkids($p) 1
+           }
+       }
+       lset varctok($v) $a $tok
+       set b [lindex $vupptr($v) $a]
+       if {$b != $ka} {
+           if {[string compare [lindex $varctok($v) $ka] $vtokmod($v)] < 0} {
+               modify_arc $v $ka
+           }
+           if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
+               modify_arc $v $b
+           }
+           set c [lindex $vbackptr($v) $a]
+           set d [lindex $vleftptr($v) $a]
+           if {$c == 0} {
+               lset vdownptr($v) $b $d
+           } else {
+               lset vleftptr($v) $c $d
+           }
+           if {$d != 0} {
+               lset vbackptr($v) $d $c
+           }
+           if {[lindex $vlastins($v) $b] == $a} {
+               lset vlastins($v) $b $c
+           }
+           lset vupptr($v) $a $ka
+           set c [lindex $vlastins($v) $ka]
+           if {$c == 0 || \
+                   [string compare $tok [lindex $varctok($v) $c]] < 0} {
+               set c $ka
+               set b [lindex $vdownptr($v) $ka]
+           } else {
+               set b [lindex $vleftptr($v) $c]
+           }
+           while {$b != 0 && \
+                     [string compare $tok [lindex $varctok($v) $b]] >= 0} {
+               set c $b
+               set b [lindex $vleftptr($v) $c]
+           }
+           if {$c == $ka} {
+               lset vdownptr($v) $ka $a
+               lset vbackptr($v) $a 0
+           } else {
+               lset vleftptr($v) $c $a
+               lset vbackptr($v) $a $c
+           }
+           lset vleftptr($v) $a $b
+           if {$b != 0} {
+               lset vbackptr($v) $b $a
+           }
+           lset vlastins($v) $ka $a
+       }
+    }
+    foreach id [array names sortkids] {
+       if {[llength $children($v,$id)] > 1} {
+           set children($v,$id) [lsort -command [list vtokcmp $v] \
+                                     $children($v,$id)]
+       }
+    }
+    set t2 [clock clicks -milliseconds]
+    #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
+}
+
+# Fix up the graph after we have found out that in view $v,
+# $p (a commit that we have already seen) is actually the parent
+# of the last commit in arc $a.
+proc fix_reversal {p a v} {
+    global varcid varcstart varctok vupptr
+
+    set pa $varcid($v,$p)
+    if {$p ne [lindex $varcstart($v) $pa]} {
+       splitvarc $p $v
+       set pa $varcid($v,$p)
+    }
+    # seeds always need to be renumbered
+    if {[lindex $vupptr($v) $pa] == 0 ||
+       [string compare [lindex $varctok($v) $a] \
+            [lindex $varctok($v) $pa]] > 0} {
+       renumbervarc $pa $v
+    }
+}
+
+proc insertrow {id p v} {
+    global cmitlisted children parents varcid varctok vtokmod
+    global varccommits ordertok commitidx numcommits curview
+    global targetid targetrow
+
+    readcommit $id
+    set vid $v,$id
+    set cmitlisted($vid) 1
+    set children($vid) {}
+    set parents($vid) [list $p]
+    set a [newvarc $v $id]
+    set varcid($vid) $a
+    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
+       modify_arc $v $a
+    }
+    lappend varccommits($v,$a) $id
+    set vp $v,$p
+    if {[llength [lappend children($vp) $id]] > 1} {
+       set children($vp) [lsort -command [list vtokcmp $v] $children($vp)]
+       catch {unset ordertok}
+    }
+    fix_reversal $p $a $v
+    incr commitidx($v)
+    if {$v == $curview} {
+       set numcommits $commitidx($v)
+       setcanvscroll
+       if {[info exists targetid]} {
+           if {![comes_before $targetid $p]} {
+               incr targetrow
+           }
+       }
+    }
+}
+
+proc insertfakerow {id p} {
+    global varcid varccommits parents children cmitlisted
+    global commitidx varctok vtokmod targetid targetrow curview numcommits
+
+    set v $curview
+    set a $varcid($v,$p)
+    set i [lsearch -exact $varccommits($v,$a) $p]
+    if {$i < 0} {
+       puts "oops: insertfakerow can't find [shortids $p] on arc $a"
+       return
+    }
+    set children($v,$id) {}
+    set parents($v,$id) [list $p]
+    set varcid($v,$id) $a
+    lappend children($v,$p) $id
+    set cmitlisted($v,$id) 1
+    set numcommits [incr commitidx($v)]
+    # note we deliberately don't update varcstart($v) even if $i == 0
+    set varccommits($v,$a) [linsert $varccommits($v,$a) $i $id]
+    modify_arc $v $a $i
+    if {[info exists targetid]} {
+       if {![comes_before $targetid $p]} {
+           incr targetrow
+       }
+    }
+    setcanvscroll
+    drawvisible
+}
+
+proc removefakerow {id} {
+    global varcid varccommits parents children commitidx
+    global varctok vtokmod cmitlisted currentid selectedline
+    global targetid curview numcommits
+
+    set v $curview
+    if {[llength $parents($v,$id)] != 1} {
+       puts "oops: removefakerow [shortids $id] has [llength $parents($v,$id)] parents"
+       return
+    }
+    set p [lindex $parents($v,$id) 0]
+    set a $varcid($v,$id)
+    set i [lsearch -exact $varccommits($v,$a) $id]
+    if {$i < 0} {
+       puts "oops: removefakerow can't find [shortids $id] on arc $a"
+       return
+    }
+    unset varcid($v,$id)
+    set varccommits($v,$a) [lreplace $varccommits($v,$a) $i $i]
+    unset parents($v,$id)
+    unset children($v,$id)
+    unset cmitlisted($v,$id)
+    set numcommits [incr commitidx($v) -1]
+    set j [lsearch -exact $children($v,$p) $id]
+    if {$j >= 0} {
+       set children($v,$p) [lreplace $children($v,$p) $j $j]
+    }
+    modify_arc $v $a $i
+    if {[info exist currentid] && $id eq $currentid} {
+       unset currentid
+       unset selectedline
+    }
+    if {[info exists targetid] && $targetid eq $id} {
+       set targetid $p
+    }
+    setcanvscroll
+    drawvisible
+}
+
+proc first_real_child {vp} {
+    global children nullid nullid2
+
+    foreach id $children($vp) {
+       if {$id ne $nullid && $id ne $nullid2} {
+           return $id
+       }
+    }
+    return {}
+}
+
+proc last_real_child {vp} {
+    global children nullid nullid2
+
+    set kids $children($vp)
+    for {set i [llength $kids]} {[incr i -1] >= 0} {} {
+       set id [lindex $kids $i]
+       if {$id ne $nullid && $id ne $nullid2} {
+           return $id
+       }
+    }
+    return {}
+}
+
+proc vtokcmp {v a b} {
+    global varctok varcid
+
+    return [string compare [lindex $varctok($v) $varcid($v,$a)] \
+               [lindex $varctok($v) $varcid($v,$b)]]
+}
+
+# This assumes that if lim is not given, the caller has checked that
+# arc a's token is less than $vtokmod($v)
+proc modify_arc {v a {lim {}}} {
+    global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
+
+    if {$lim ne {}} {
+       set c [string compare [lindex $varctok($v) $a] $vtokmod($v)]
+       if {$c > 0} return
+       if {$c == 0} {
+           set r [lindex $varcrow($v) $a]
+           if {$r ne {} && $vrowmod($v) <= $r + $lim} return
+       }
+    }
+    set vtokmod($v) [lindex $varctok($v) $a]
+    set varcmod($v) $a
+    if {$v == $curview} {
+       while {$a != 0 && [lindex $varcrow($v) $a] eq {}} {
+           set a [lindex $vupptr($v) $a]
+           set lim {}
+       }
+       set r 0
+       if {$a != 0} {
+           if {$lim eq {}} {
+               set lim [llength $varccommits($v,$a)]
+           }
+           set r [expr {[lindex $varcrow($v) $a] + $lim}]
+       }
+       set vrowmod($v) $r
+       undolayout $r
+    }
+}
+
+proc update_arcrows {v} {
+    global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
+    global varcid vrownum varcorder varcix varccommits
+    global vupptr vdownptr vleftptr varctok
+    global displayorder parentlist curview cached_commitrow
+
+    if {$vrowmod($v) == $commitidx($v)} return
+    if {$v == $curview} {
+       if {[llength $displayorder] > $vrowmod($v)} {
+           set displayorder [lrange $displayorder 0 [expr {$vrowmod($v) - 1}]]
+           set parentlist [lrange $parentlist 0 [expr {$vrowmod($v) - 1}]]
+       }
+       catch {unset cached_commitrow}
+    }
+    set narctot [expr {[llength $varctok($v)] - 1}]
+    set a $varcmod($v)
+    while {$a != 0 && [lindex $varcix($v) $a] eq {}} {
+       # go up the tree until we find something that has a row number,
+       # or we get to a seed
+       set a [lindex $vupptr($v) $a]
+    }
+    if {$a == 0} {
+       set a [lindex $vdownptr($v) 0]
+       if {$a == 0} return
+       set vrownum($v) {0}
+       set varcorder($v) [list $a]
+       lset varcix($v) $a 0
+       lset varcrow($v) $a 0
+       set arcn 0
+       set row 0
+    } else {
+       set arcn [lindex $varcix($v) $a]
+       if {[llength $vrownum($v)] > $arcn + 1} {
+           set vrownum($v) [lrange $vrownum($v) 0 $arcn]
+           set varcorder($v) [lrange $varcorder($v) 0 $arcn]
+       }
+       set row [lindex $varcrow($v) $a]
+    }
+    while {1} {
+       set p $a
+       incr row [llength $varccommits($v,$a)]
+       # go down if possible
+       set b [lindex $vdownptr($v) $a]
+       if {$b == 0} {
+           # if not, go left, or go up until we can go left
+           while {$a != 0} {
+               set b [lindex $vleftptr($v) $a]
+               if {$b != 0} break
+               set a [lindex $vupptr($v) $a]
+           }
+           if {$a == 0} break
+       }
+       set a $b
+       incr arcn
+       lappend vrownum($v) $row
+       lappend varcorder($v) $a
+       lset varcix($v) $a $arcn
+       lset varcrow($v) $a $row
+    }
+    set vtokmod($v) [lindex $varctok($v) $p]
+    set varcmod($v) $p
+    set vrowmod($v) $row
+    if {[info exists currentid]} {
+       set selectedline [rowofcommit $currentid]
+    }
+}
+
+# Test whether view $v contains commit $id
+proc commitinview {id v} {
+    global varcid
+
+    return [info exists varcid($v,$id)]
+}
+
+# Return the row number for commit $id in the current view
+proc rowofcommit {id} {
+    global varcid varccommits varcrow curview cached_commitrow
+    global varctok vtokmod
+
+    set v $curview
+    if {![info exists varcid($v,$id)]} {
+       puts "oops rowofcommit no arc for [shortids $id]"
+       return {}
+    }
+    set a $varcid($v,$id)
+    if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] >= 0} {
+       update_arcrows $v
+    }
+    if {[info exists cached_commitrow($id)]} {
+       return $cached_commitrow($id)
+    }
+    set i [lsearch -exact $varccommits($v,$a) $id]
+    if {$i < 0} {
+       puts "oops didn't find commit [shortids $id] in arc $a"
+       return {}
+    }
+    incr i [lindex $varcrow($v) $a]
+    set cached_commitrow($id) $i
+    return $i
+}
+
+# Returns 1 if a is on an earlier row than b, otherwise 0
+proc comes_before {a b} {
+    global varcid varctok curview
+
+    set v $curview
+    if {$a eq $b || ![info exists varcid($v,$a)] || \
+           ![info exists varcid($v,$b)]} {
+       return 0
+    }
+    if {$varcid($v,$a) != $varcid($v,$b)} {
+       return [expr {[string compare [lindex $varctok($v) $varcid($v,$a)] \
+                          [lindex $varctok($v) $varcid($v,$b)]] < 0}]
+    }
+    return [expr {[rowofcommit $a] < [rowofcommit $b]}]
+}
+
+proc bsearch {l elt} {
+    if {[llength $l] == 0 || $elt <= [lindex $l 0]} {
+       return 0
+    }
+    set lo 0
+    set hi [llength $l]
+    while {$hi - $lo > 1} {
+       set mid [expr {int(($lo + $hi) / 2)}]
+       set t [lindex $l $mid]
+       if {$elt < $t} {
+           set hi $mid
+       } elseif {$elt > $t} {
+           set lo $mid
+       } else {
+           return $mid
+       }
+    }
+    return $lo
+}
+
+# Make sure rows $start..$end-1 are valid in displayorder and parentlist
+proc make_disporder {start end} {
+    global vrownum curview commitidx displayorder parentlist
+    global varccommits varcorder parents vrowmod varcrow
+    global d_valid_start d_valid_end
+
+    if {$end > $vrowmod($curview)} {
+       update_arcrows $curview
+    }
+    set ai [bsearch $vrownum($curview) $start]
+    set start [lindex $vrownum($curview) $ai]
+    set narc [llength $vrownum($curview)]
+    for {set r $start} {$ai < $narc && $r < $end} {incr ai} {
+       set a [lindex $varcorder($curview) $ai]
+       set l [llength $displayorder]
+       set al [llength $varccommits($curview,$a)]
+       if {$l < $r + $al} {
+           if {$l < $r} {
+               set pad [ntimes [expr {$r - $l}] {}]
+               set displayorder [concat $displayorder $pad]
+               set parentlist [concat $parentlist $pad]
+           } elseif {$l > $r} {
+               set displayorder [lrange $displayorder 0 [expr {$r - 1}]]
+               set parentlist [lrange $parentlist 0 [expr {$r - 1}]]
+           }
+           foreach id $varccommits($curview,$a) {
+               lappend displayorder $id
+               lappend parentlist $parents($curview,$id)
+           }
+       } elseif {[lindex $displayorder [expr {$r + $al - 1}]] eq {}} {
+           set i $r
+           foreach id $varccommits($curview,$a) {
+               lset displayorder $i $id
+               lset parentlist $i $parents($curview,$id)
+               incr i
+           }
+       }
+       incr r $al
+    }
+}
+
+proc commitonrow {row} {
+    global displayorder
+
+    set id [lindex $displayorder $row]
+    if {$id eq {}} {
+       make_disporder $row [expr {$row + 1}]
+       set id [lindex $displayorder $row]
+    }
+    return $id
+}
+
+proc closevarcs {v} {
+    global varctok varccommits varcid parents children
+    global cmitlisted commitidx commitinterest vtokmod
+
+    set missing_parents 0
+    set scripts {}
+    set narcs [llength $varctok($v)]
+    for {set a 1} {$a < $narcs} {incr a} {
+       set id [lindex $varccommits($v,$a) end]
+       foreach p $parents($v,$id) {
+           if {[info exists varcid($v,$p)]} continue
+           # add p as a new commit
+           incr missing_parents
+           set cmitlisted($v,$p) 0
+           set parents($v,$p) {}
+           if {[llength $children($v,$p)] == 1 &&
+               [llength $parents($v,$id)] == 1} {
+               set b $a
+           } else {
+               set b [newvarc $v $p]
+           }
+           set varcid($v,$p) $b
+           if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
+               modify_arc $v $b
+           }
+           lappend varccommits($v,$b) $p
+           incr commitidx($v)
+           if {[info exists commitinterest($p)]} {
+               foreach script $commitinterest($p) {
+                   lappend scripts [string map [list "%I" $p] $script]
+               }
+               unset commitinterest($id)
+           }
+       }
+    }
+    if {$missing_parents > 0} {
+       foreach s $scripts {
+           eval $s
+       }
+    }
+}
+
+# Use $rwid as a substitute for $id, i.e. reparent $id's children to $rwid
+# Assumes we already have an arc for $rwid.
+proc rewrite_commit {v id rwid} {
+    global children parents varcid varctok vtokmod varccommits
+
+    foreach ch $children($v,$id) {
+       # make $rwid be $ch's parent in place of $id
+       set i [lsearch -exact $parents($v,$ch) $id]
+       if {$i < 0} {
+           puts "oops rewrite_commit didn't find $id in parent list for $ch"
+       }
+       set parents($v,$ch) [lreplace $parents($v,$ch) $i $i $rwid]
+       # add $ch to $rwid's children and sort the list if necessary
+       if {[llength [lappend children($v,$rwid) $ch]] > 1} {
+           set children($v,$rwid) [lsort -command [list vtokcmp $v] \
+                                       $children($v,$rwid)]
+       }
+       # fix the graph after joining $id to $rwid
+       set a $varcid($v,$ch)
+       fix_reversal $rwid $a $v
+       # parentlist is wrong for the last element of arc $a
+       # even if displayorder is right, hence the 3rd arg here
+       modify_arc $v $a [expr {[llength $varccommits($v,$a)] - 1}]
+    }
+}
+
+proc getcommitlines {fd inst view updating}  {
+    global cmitlisted commitinterest leftover
+    global commitidx commitdata vdatemode
+    global parents children curview hlview
+    global idpending ordertok
+    global varccommits varcid varctok vtokmod vfilelimit
 
     set stuff [read $fd 500000]
     # git log doesn't terminate the last commit with a null...
-    if {$stuff == {} && $leftover($view) ne {} && [eof $fd]} {
+    if {$stuff == {} && $leftover($inst) ne {} && [eof $fd]} {
        set stuff "\0"
     }
     if {$stuff == {}} {
        if {![eof $fd]} {
            return 1
        }
-       # Check if we have seen any ids listed as parents that haven't
-       # appeared in the list
-       foreach vid [array names idpending "$view,*"] {
-           # should only get here if git log is buggy
-           set id [lindex [split $vid ","] 1]
-           set commitrow($vid) $commitidx($view)
-           incr commitidx($view)
-           if {$view == $curview} {
-               lappend parentlist {}
-               lappend displayorder $id
-               lappend commitlisted 0
-           } else {
-               lappend vparentlist($view) {}
-               lappend vdisporder($view) $id
-               lappend vcmitlisted($view) 0
-           }
+       global commfd viewcomplete viewactive viewname
+       global viewinstances
+       unset commfd($inst)
+       set i [lsearch -exact $viewinstances($view) $inst]
+       if {$i >= 0} {
+           set viewinstances($view) [lreplace $viewinstances($view) $i $i]
        }
-       set viewcomplete($view) 1
-       global viewname progresscoords
-       unset commfd($view)
-       notbusy $view
-       set progresscoords {0 0}
-       adjustprogress
        # set it blocking so we wait for the process to terminate
        fconfigure $fd -blocking 1
        if {[catch {close $fd} err]} {
@@ -213,10 +1288,10 @@ proc getcommitlines {fd view}  {
            }
            if {[string range $err 0 4] == "usage"} {
                set err "Gitk: error reading commits$fv:\
-                       bad arguments to git rev-list."
+                       bad arguments to git log."
                if {$viewname($view) eq "Command line"} {
                    append err \
-                       "  (Note: arguments to gitk are passed to git rev-list\
+                       "  (Note: arguments to gitk are passed to git log\
                         to allow selection of commits to be displayed.)"
                }
            } else {
@@ -224,23 +1299,31 @@ proc getcommitlines {fd view}  {
            }
            error_popup $err
        }
+       if {[incr viewactive($view) -1] <= 0} {
+           set viewcomplete($view) 1
+           # Check if we have seen any ids listed as parents that haven't
+           # appeared in the list
+           closevarcs $view
+           notbusy $view
+       }
        if {$view == $curview} {
-           run chewcommits $view
+           run chewcommits
        }
        return 0
     }
     set start 0
     set gotsome 0
+    set scripts {}
     while 1 {
        set i [string first "\0" $stuff $start]
        if {$i < 0} {
-           append leftover($view) [string range $stuff $start end]
+           append leftover($inst) [string range $stuff $start end]
            break
        }
        if {$start == 0} {
-           set cmit $leftover($view)
+           set cmit $leftover($inst)
            append cmit [string range $stuff 0 [expr {$i - 1}]]
-           set leftover($view) {}
+           set leftover($inst) {}
        } else {
            set cmit [string range $stuff $start [expr {$i - 1}]]
        }
@@ -276,121 +1359,128 @@ proc getcommitlines {fd view}  {
            exit 1
        }
        set id [lindex $ids 0]
-       if {![info exists ordertok($view,$id)]} {
-           set otok "o[strrep $vnextroot($view)]"
-           incr vnextroot($view)
-           set ordertok($view,$id) $otok
-       } else {
-           set otok $ordertok($view,$id)
-           unset idpending($view,$id)
+       set vid $view,$id
+
+       if {!$listed && $updating && ![info exists varcid($vid)] &&
+           $vfilelimit($view) ne {}} {
+           # git log doesn't rewrite parents for unlisted commits
+           # when doing path limiting, so work around that here
+           # by working out the rewritten parent with git rev-list
+           # and if we already know about it, using the rewritten
+           # parent as a substitute parent for $id's children.
+           if {![catch {
+               set rwid [exec git rev-list --first-parent --max-count=1 \
+                             $id -- $vfilelimit($view)]
+           }]} {
+               if {$rwid ne {} && [info exists varcid($view,$rwid)]} {
+                   # use $rwid in place of $id
+                   rewrite_commit $view $id $rwid
+                   continue
+               }
+           }
+       }
+
+       set a 0
+       if {[info exists varcid($vid)]} {
+           if {$cmitlisted($vid) || !$listed} continue
+           set a $varcid($vid)
        }
        if {$listed} {
            set olds [lrange $ids 1 end]
-           if {[llength $olds] == 1} {
-               set p [lindex $olds 0]
-               lappend children($view,$p) $id
-               if {![info exists ordertok($view,$p)]} {
-                   set ordertok($view,$p) $ordertok($view,$id)
-                   set idpending($view,$p) 1
-               }
-           } else {
-               set i 0
-               foreach p $olds {
-                   if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
-                       lappend children($view,$p) $id
-                   }
-                   if {![info exists ordertok($view,$p)]} {
-                       set ordertok($view,$p) "$otok[strrep $i]]"
-                       set idpending($view,$p) 1
-                   }
-                   incr i
-               }
-           }
        } else {
            set olds {}
        }
-       if {![info exists children($view,$id)]} {
-           set children($view,$id) {}
-       }
        set commitdata($id) [string range $cmit [expr {$j + 1}] end]
-       set commitrow($view,$id) $commitidx($view)
-       incr commitidx($view)
-       if {$view == $curview} {
-           lappend parentlist $olds
-           lappend displayorder $id
-           lappend commitlisted $listed
-       } else {
-           lappend vparentlist($view) $olds
-           lappend vdisporder($view) $id
-           lappend vcmitlisted($view) $listed
+       set cmitlisted($vid) $listed
+       set parents($vid) $olds
+       if {![info exists children($vid)]} {
+           set children($vid) {}
+       } elseif {$a == 0 && [llength $children($vid)] == 1} {
+           set k [lindex $children($vid) 0]
+           if {[llength $parents($view,$k)] == 1 &&
+               (!$vdatemode($view) ||
+                $varcid($view,$k) == [llength $varctok($view)] - 1)} {
+               set a $varcid($view,$k)
+           }
+       }
+       if {$a == 0} {
+           # new arc
+           set a [newvarc $view $id]
+       }
+       if {[string compare [lindex $varctok($view) $a] $vtokmod($view)] < 0} {
+           modify_arc $view $a
+       }
+       if {![info exists varcid($vid)]} {
+           set varcid($vid) $a
+           lappend varccommits($view,$a) $id
+           incr commitidx($view)
+       }
+
+       set i 0
+       foreach p $olds {
+           if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
+               set vp $view,$p
+               if {[llength [lappend children($vp) $id]] > 1 &&
+                   [vtokcmp $view [lindex $children($vp) end-1] $id] > 0} {
+                   set children($vp) [lsort -command [list vtokcmp $view] \
+                                          $children($vp)]
+                   catch {unset ordertok}
+               }
+               if {[info exists varcid($view,$p)]} {
+                   fix_reversal $p $a $view
+               }
+           }
+           incr i
        }
+
        if {[info exists commitinterest($id)]} {
            foreach script $commitinterest($id) {
-               eval [string map [list "%I" $id] $script]
+               lappend scripts [string map [list "%I" $id] $script]
            }
            unset commitinterest($id)
        }
        set gotsome 1
     }
     if {$gotsome} {
-       run chewcommits $view
+       global numcommits hlview
+
        if {$view == $curview} {
-           # update progress bar
-           global progressdirn progresscoords proglastnc
-           set inc [expr {($commitidx($view) - $proglastnc) * 0.0002}]
-           set proglastnc $commitidx($view)
-           set l [lindex $progresscoords 0]
-           set r [lindex $progresscoords 1]
-           if {$progressdirn} {
-               set r [expr {$r + $inc}]
-               if {$r >= 1.0} {
-                   set r 1.0
-                   set progressdirn 0
-               }
-               if {$r > 0.2} {
-                   set l [expr {$r - 0.2}]
-               }
-           } else {
-               set l [expr {$l - $inc}]
-               if {$l <= 0.0} {
-                   set l 0.0
-                   set progressdirn 1
-               }
-               set r [expr {$l + 0.2}]
-           }
-           set progresscoords [list $l $r]
-           adjustprogress
+           set numcommits $commitidx($view)
+           run chewcommits
+       }
+       if {[info exists hlview] && $view == $hlview} {
+           # we never actually get here...
+           run vhighlightmore
+       }
+       foreach s $scripts {
+           eval $s
        }
     }
     return 2
 }
 
-proc chewcommits {view} {
+proc chewcommits {} {
     global curview hlview viewcomplete
-    global selectedline pending_select
-
-    if {$view == $curview} {
-       layoutmore
-       if {$viewcomplete($view)} {
-           global displayorder commitidx phase
-           global numcommits startmsecs
-
-           if {[info exists pending_select]} {
-               set row [first_real_row]
-               selectline $row 1
-           }
-           if {$commitidx($curview) > 0} {
-               #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
-               #puts "overall $ms ms for $numcommits commits"
-           } else {
-               show_status [mc "No commits selected"]
-           }
-           notbusy layout
-           set phase {}
+    global pending_select
+
+    layoutmore
+    if {$viewcomplete($curview)} {
+       global commitidx varctok
+       global numcommits startmsecs
+       global mainheadid nullid
+
+       if {[info exists pending_select]} {
+           set row [first_real_row]
+           selectline $row 1
+       }
+       if {$commitidx($curview) > 0} {
+           #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
+           #puts "overall $ms ms for $numcommits commits"
+           #puts "[llength $varctok($view)] arcs, $commitidx($view) commits"
+       } else {
+           show_status [mc "No commits selected"]
        }
-    }
-    if {[info exists hlview] && $view == $hlview} {
-       vhighlightmore
+       notbusy layout
     }
     return 0
 }
@@ -400,38 +1490,6 @@ proc readcommit {id} {
     parsecommit $id $contents 0
 }
 
-proc updatecommits {} {
-    global viewdata curview phase displayorder ordertok idpending
-    global children commitrow selectedline thickerline showneartags
-    global isworktree
-
-    set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
-
-    if {$phase ne {}} {
-       stop_rev_list
-       set phase {}
-    }
-    set n $curview
-    foreach id $displayorder {
-       catch {unset children($n,$id)}
-       catch {unset commitrow($n,$id)}
-       catch {unset ordertok($n,$id)}
-    }
-    foreach vid [array names idpending "$n,*"] {
-       unset idpending($vid)
-    }
-    set curview -1
-    catch {unset selectedline}
-    catch {unset thickerline}
-    catch {unset viewdata($n)}
-    readrefs
-    changedrefs
-    if {$showneartags} {
-       getallcommits
-    }
-    showview $n
-}
-
 proc parsecommit {id contents listed} {
     global commitinfo cdate
 
@@ -472,7 +1530,7 @@ proc parsecommit {id contents listed} {
        set headline [string trimright [string range $headline 0 $i]]
     }
     if {!$listed} {
-       # git rev-list indents the comment by 4 spaces;
+       # git log indents the comment by 4 spaces;
        # if we got this via git cat-file, add the indentation
        set newcomment {}
        foreach line [split $comment "\n"] {
@@ -558,10 +1616,10 @@ proc readrefs {} {
 
 # skip over fake commits
 proc first_real_row {} {
-    global nullid nullid2 displayorder numcommits
+    global nullid nullid2 numcommits
 
     for {set row 0} {$row < $numcommits} {incr row} {
-       set id [lindex $displayorder $row]
+       set id [commitonrow $row]
        if {$id ne $nullid && $id ne $nullid2} {
            break
        }
@@ -641,7 +1699,7 @@ proc setoptions {} {
 }
 
 proc makewindow {} {
-    global canv canv2 canv3 linespc charspc ctext cflist
+    global canv canv2 canv3 linespc charspc ctext cflist cscroll
     global tabstop
     global findtype findtypemenu findloc findstring fstring geometry
     global entries sha1entry sha1string sha1but
@@ -654,13 +1712,14 @@ proc makewindow {} {
     global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
     global headctxmenu progresscanv progressitem progresscoords statusw
     global fprogitem fprogcoord lastprogupdate progupdatepending
-    global rprogitem rprogcoord
+    global rprogitem rprogcoord rownumsel numcommits
     global have_tk85
 
     menu .bar
     .bar add cascade -label [mc "File"] -menu .bar.file
     menu .bar.file
     .bar.file add command -label [mc "Update"] -command updatecommits
+    .bar.file add command -label [mc "Reload"] -command reloadcommits
     .bar.file add command -label [mc "Reread references"] -command rereadrefs
     .bar.file add command -label [mc "List references"] -command showrefs
     .bar.file add command -label [mc "Quit"] -command doquit
@@ -769,6 +1828,18 @@ proc makewindow {} {
        -state disabled -width 26
     pack .tf.bar.rightbut -side left -fill y
 
+    label .tf.bar.rowlabel -text [mc "Row"]
+    set rownumsel {}
+    label .tf.bar.rownum -width 7 -font textfont -textvariable rownumsel \
+       -relief sunken -anchor e
+    label .tf.bar.rowlabel2 -text "/"
+    label .tf.bar.numcommits -width 7 -font textfont -textvariable numcommits \
+       -relief sunken -anchor e
+    pack .tf.bar.rowlabel .tf.bar.rownum .tf.bar.rowlabel2 .tf.bar.numcommits \
+       -side left
+    global selectedline
+    trace add variable selectedline {write unset} selectedline_change
+
     # Status label and progress bar
     set statusw .tf.bar.status
     label $statusw -width 15 -relief sunken
@@ -1016,7 +2087,7 @@ proc makewindow {} {
     bindkey k "selnextline 1"
     bindkey j "goback"
     bindkey l "goforw"
-    bindkey b "$ctext yview scroll -1 pages"
+    bindkey b prevfile
     bindkey d "$ctext yview scroll 18 units"
     bindkey u "$ctext yview scroll -18 units"
     bindkey / {dofind 1 1}
@@ -1088,6 +2159,8 @@ proc makewindow {} {
        -command {flist_hl 0}
     $flist_menu add command -label [mc "Highlight this only"] \
        -command {flist_hl 1}
+    $flist_menu add command -label [mc "External diff"] \
+        -command {external_diff}
 }
 
 # Windows sends all mouse wheel events to the current focused window, not
@@ -1108,6 +2181,17 @@ proc windows_mousewheel_redirector {W X Y D} {
     }
 }
 
+# Update row number label when selectedline changes
+proc selectedline_change {n1 n2 op} {
+    global selectedline rownumsel
+
+    if {$op eq "unset"} {
+       set rownumsel {}
+    } else {
+       set rownumsel [expr {$selectedline + 1}]
+    }
+}
+
 # mouse-2 makes all windows scan vertically, but only the one
 # the cursor is in scans horizontally
 proc canvscan {op w x y} {
@@ -1123,7 +2207,7 @@ proc canvscan {op w x y} {
 
 proc scrollcanv {cscroll f0 f1} {
     $cscroll set $f0 $f1
-    drawfrac $f0 $f1
+    drawvisible
     flushhighlights
 }
 
@@ -1192,7 +2276,7 @@ proc savestuff {w} {
     global viewname viewfiles viewargs viewargscmd viewperm nextviewnum
     global cmitmode wrapcomment datetimeformat limitdiffs
     global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
-    global autoselect
+    global autoselect extdifftool
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -1218,6 +2302,7 @@ proc savestuff {w} {
        puts $f [list set diffcolors $diffcolors]
        puts $f [list set diffcontext $diffcontext]
        puts $f [list set selectbgcolor $selectbgcolor]
+       puts $f [list set extdifftool $extdifftool]
 
        puts $f "set geometry(main) [wm geometry .]"
        puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -1318,7 +2403,7 @@ proc about {} {
     message $w.m -text [mc "
 Gitk - a commit viewer for git
 
-Copyright © 2005-2006 Paul Mackerras
+Copyright © 2005-2008 Paul Mackerras
 
 Use and redistribute under the terms of the GNU General Public License"] \
            -justify center -aspect 400 -border 2 -bg white -relief groove
@@ -1675,7 +2760,7 @@ image create bitmap reficon-o -background black -foreground "#ddddff" \
     -data $rectdata -maskdata $rectmask
 
 proc init_flist {first} {
-    global cflist cflist_top selectedline difffilestart
+    global cflist cflist_top difffilestart
 
     $cflist conf -state normal
     $cflist delete 0.0 end
@@ -1768,6 +2853,12 @@ proc pop_flist_menu {w X Y x y} {
        set e [lindex $treediffs($diffids) [expr {$l-2}]]
     }
     set flist_menu_file $e
+    set xdiffstate "normal"
+    if {$cmitmode eq "tree"} {
+       set xdiffstate "disabled"
+    }
+    # Disable "External diff" item in tree mode
+    $flist_menu entryconf 2 -state $xdiffstate
     tk_popup $flist_menu $X $Y
 }
 
@@ -1780,7 +2871,114 @@ proc flist_hl {only} {
     } else {
        append findstring " " $x
     }
-    set gdttype [mc "touching paths:"]
+    set gdttype [mc "touching paths:"]
+}
+
+proc save_file_from_commit {filename output what} {
+    global nullfile
+
+    if {[catch {exec git show $filename -- > $output} err]} {
+       if {[string match "fatal: bad revision *" $err]} {
+           return $nullfile
+       }
+       error_popup "Error getting \"$filename\" from $what: $err"
+       return {}
+    }
+    return $output
+}
+
+proc external_diff_get_one_file {diffid filename diffdir} {
+    global nullid nullid2 nullfile
+    global gitdir
+
+    if {$diffid == $nullid} {
+        set difffile [file join [file dirname $gitdir] $filename]
+       if {[file exists $difffile]} {
+           return $difffile
+       }
+       return $nullfile
+    }
+    if {$diffid == $nullid2} {
+        set difffile [file join $diffdir "\[index\] [file tail $filename]"]
+        return [save_file_from_commit :$filename $difffile index]
+    }
+    set difffile [file join $diffdir "\[$diffid\] [file tail $filename]"]
+    return [save_file_from_commit $diffid:$filename $difffile \
+              "revision $diffid"]
+}
+
+proc external_diff {} {
+    global gitktmpdir nullid nullid2
+    global flist_menu_file
+    global diffids
+    global diffnum
+    global gitdir extdifftool
+
+    if {[llength $diffids] == 1} {
+        # no reference commit given
+        set diffidto [lindex $diffids 0]
+        if {$diffidto eq $nullid} {
+            # diffing working copy with index
+            set diffidfrom $nullid2
+        } elseif {$diffidto eq $nullid2} {
+            # diffing index with HEAD
+            set diffidfrom "HEAD"
+        } else {
+            # use first parent commit
+            global parentlist selectedline
+            set diffidfrom [lindex $parentlist $selectedline 0]
+        }
+    } else {
+        set diffidfrom [lindex $diffids 0]
+        set diffidto [lindex $diffids 1]
+    }
+
+    # make sure that several diffs wont collide
+    if {![info exists gitktmpdir]} {
+       set gitktmpdir [file join [file dirname $gitdir] \
+                           [format ".gitk-tmp.%s" [pid]]]
+       if {[catch {file mkdir $gitktmpdir} err]} {
+           error_popup "Error creating temporary directory $gitktmpdir: $err"
+           unset gitktmpdir
+           return
+       }
+       set diffnum 0
+    }
+    incr diffnum
+    set diffdir [file join $gitktmpdir $diffnum]
+    if {[catch {file mkdir $diffdir} err]} {
+       error_popup "Error creating temporary directory $diffdir: $err"
+       return
+    }
+
+    # gather files to diff
+    set difffromfile [external_diff_get_one_file $diffidfrom $flist_menu_file $diffdir]
+    set difftofile [external_diff_get_one_file $diffidto $flist_menu_file $diffdir]
+
+    if {$difffromfile ne {} && $difftofile ne {}} {
+        set cmd [concat | [shellsplit $extdifftool] \
+                    [list $difffromfile $difftofile]]
+        if {[catch {set fl [open $cmd r]} err]} {
+            file delete -force $diffdir
+            error_popup [mc "$extdifftool: command failed: $err"]
+        } else {
+            fconfigure $fl -blocking 0
+            filerun $fl [list delete_at_eof $fl $diffdir]
+        }
+    }
+}
+
+# delete $dir when we see eof on $f (presumably because the child has exited)
+proc delete_at_eof {f dir} {
+    while {[gets $f line] >= 0} {}
+    if {[eof $f]} {
+       if {[catch {close $f} err]} {
+           error_popup "External diff viewer failed: $err"
+       }
+       file delete -force $dir
+       return 0
+    }
+    return 1
 }
 
 # Functions for adding and removing shell-type quoting
@@ -1925,7 +3123,7 @@ proc vieweditor {top n title} {
        -variable newviewperm($n)
     grid $top.perm - -pady 5 -sticky w
     message $top.al -aspect 1000 \
-       -text [mc "Commits to include (arguments to git rev-list):"]
+       -text [mc "Commits to include (arguments to git log):"]
     grid $top.al - -sticky w -pady 5
     entry $top.args -width 50 -textvariable newviewargs($n) \
        -background $bgcolor
@@ -2028,7 +3226,7 @@ proc newviewok {top n} {
            set viewargs($n) $newargs
            set viewargscmd($n) $newviewargscmd($n)
            if {$curview == $n} {
-               run updatecommits
+               run reloadcommits
            }
        }
     }
@@ -2036,7 +3234,7 @@ proc newviewok {top n} {
 }
 
 proc delview {} {
-    global curview viewdata viewperm hlview selectedhlview
+    global curview viewperm hlview selectedhlview
 
     if {$curview == 0} return
     if {[info exists hlview] && $hlview == $curview} {
@@ -2044,7 +3242,6 @@ proc delview {} {
        unset hlview
     }
     allviewmenus $curview delete
-    set viewdata($curview) {}
     set viewperm($curview) 0
     showview 0
 }
@@ -2058,48 +3255,28 @@ proc addviewmenu {n} {
     #  -command [list addvhighlight $n] -variable selectedhlview
 }
 
-proc flatten {var} {
-    global $var
-
-    set ret {}
-    foreach i [array names $var] {
-       lappend ret $i [set $var\($i\)]
-    }
-    return $ret
-}
-
-proc unflatten {var l} {
-    global $var
-
-    catch {unset $var}
-    foreach {i v} $l {
-       set $var\($i\) $v
-    }
-}
-
 proc showview {n} {
-    global curview viewdata viewfiles
+    global curview cached_commitrow ordertok
     global displayorder parentlist rowidlist rowisopt rowfinal
-    global colormap rowtextx commitrow nextcolor canvxmax
-    global numcommits commitlisted
+    global colormap rowtextx nextcolor canvxmax
+    global numcommits viewcomplete
     global selectedline currentid canv canvy0
     global treediffs
-    global pending_select phase
+    global pending_select mainheadid
     global commitidx
-    global commfd
-    global selectedview selectfirst
-    global vparentlist vdisporder vcmitlisted
+    global selectedview
     global hlview selectedhlview commitinterest
 
     if {$n == $curview} return
     set selid {}
+    set ymax [lindex [$canv cget -scrollregion] 3]
+    set span [$canv yview]
+    set ytop [expr {[lindex $span 0] * $ymax}]
+    set ybot [expr {[lindex $span 1] * $ymax}]
+    set yscreen [expr {($ybot - $ytop) / 2}]
     if {[info exists selectedline]} {
        set selid $currentid
        set y [yc $selectedline]
-       set ymax [lindex [$canv cget -scrollregion] 3]
-       set span [$canv yview]
-       set ytop [expr {[lindex $span 0] * $ymax}]
-       set ybot [expr {[lindex $span 1] * $ymax}]
        if {$ytop < $y && $y < $ybot} {
            set yscreen [expr {$y - $ytop}]
        }
@@ -2109,17 +3286,6 @@ proc showview {n} {
     }
     unselectline
     normalline
-    if {$curview >= 0} {
-       set vparentlist($curview) $parentlist
-       set vdisporder($curview) $displayorder
-       set vcmitlisted($curview) $commitlisted
-       if {$phase ne {} ||
-           ![info exists viewdata($curview)] ||
-           [lindex $viewdata($curview) 0] ne {}} {
-           set viewdata($curview) \
-               [list $phase $rowidlist $rowisopt $rowfinal]
-       }
-    }
     catch {unset treediffs}
     clear_display
     if {[info exists hlview] && $hlview == $n} {
@@ -2127,6 +3293,8 @@ proc showview {n} {
        set selectedhlview [mc "None"]
     }
     catch {unset commitinterest}
+    catch {unset cached_commitrow}
+    catch {unset ordertok}
 
     set curview $n
     set selectedview $n
@@ -2134,7 +3302,7 @@ proc showview {n} {
     .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
 
     run refill_reflist
-    if {![info exists viewdata($n)]} {
+    if {![info exists viewcomplete($n)]} {
        if {$selid ne {}} {
            set pending_select $selid
        }
@@ -2142,14 +3310,11 @@ proc showview {n} {
        return
     }
 
-    set v $viewdata($n)
-    set phase [lindex $v 0]
-    set displayorder $vdisporder($n)
-    set parentlist $vparentlist($n)
-    set commitlisted $vcmitlisted($n)
-    set rowidlist [lindex $v 1]
-    set rowisopt [lindex $v 2]
-    set rowfinal [lindex $v 3]
+    set displayorder {}
+    set parentlist {}
+    set rowidlist {}
+    set rowisopt {}
+    set rowfinal {}
     set numcommits $commitidx($n)
 
     catch {unset colormap}
@@ -2161,9 +3326,8 @@ proc showview {n} {
     setcanvscroll
     set yf 0
     set row {}
-    set selectfirst 0
-    if {[info exists yscreen] && [info exists commitrow($n,$selid)]} {
-       set row $commitrow($n,$selid)
+    if {$selid ne {} && [commitinview $selid $n]} {
+       set row [rowofcommit $selid]
        # try to get the selected row in the same position on the screen
        set ymax [lindex [$canv cget -scrollregion] 3]
        set ytop [expr {[yc $row] - $yscreen}]
@@ -2176,21 +3340,24 @@ proc showview {n} {
     drawvisible
     if {$row ne {}} {
        selectline $row 0
-    } elseif {$selid ne {}} {
-       set pending_select $selid
+    } elseif {$mainheadid ne {} && [commitinview $mainheadid $curview]} {
+       selectline [rowofcommit $mainheadid] 1
+    } elseif {!$viewcomplete($n)} {
+       if {$selid ne {}} {
+           set pending_select $selid
+       } else {
+           set pending_select $mainheadid
+       }
     } else {
        set row [first_real_row]
        if {$row < $numcommits} {
            selectline $row 0
-       } else {
-           set selectfirst 1
        }
     }
-    if {$phase ne {}} {
-       if {$phase eq "getcommits"} {
+    if {!$viewcomplete($n)} {
+       if {$numcommits == 0} {
            show_status [mc "Reading commits..."]
        }
-       run chewcommits $n
     } elseif {$numcommits == 0} {
        show_status [mc "No commits selected"]
     }
@@ -2198,20 +3365,20 @@ proc showview {n} {
 
 # Stuff relating to the highlighting facility
 
-proc ishighlighted {row} {
+proc ishighlighted {id} {
     global vhighlights fhighlights nhighlights rhighlights
 
-    if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
-       return $nhighlights($row)
+    if {[info exists nhighlights($id)] && $nhighlights($id) > 0} {
+       return $nhighlights($id)
     }
-    if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
-       return $vhighlights($row)
+    if {[info exists vhighlights($id)] && $vhighlights($id) > 0} {
+       return $vhighlights($id)
     }
-    if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
-       return $fhighlights($row)
+    if {[info exists fhighlights($id)] && $fhighlights($id) > 0} {
+       return $fhighlights($id)
     }
-    if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
-       return $rhighlights($row)
+    if {[info exists rhighlights($id)] && $rhighlights($id) > 0} {
+       return $rhighlights($id)
     }
     return 0
 }
@@ -2249,7 +3416,7 @@ proc unbolden {} {
 
     set stillbold {}
     foreach row $boldrows {
-       if {![ishighlighted $row]} {
+       if {![ishighlighted [commitonrow $row]]} {
            bolden $row mainfont
        } else {
            lappend stillbold $row
@@ -2259,17 +3426,13 @@ proc unbolden {} {
 }
 
 proc addvhighlight {n} {
-    global hlview curview viewdata vhl_done vhighlights commitidx
+    global hlview viewcomplete curview vhl_done commitidx
 
     if {[info exists hlview]} {
        delvhighlight
     }
     set hlview $n
-    if {$n != $curview && ![info exists viewdata($n)]} {
-       set viewdata($n) [list getcommits {{}} 0 0 0]
-       set vparentlist($n) {}
-       set vdisporder($n) {}
-       set vcmitlisted($n) {}
+    if {$n != $curview && ![info exists viewcomplete($n)]} {
        start_rev_list $n
     }
     set vhl_done $commitidx($hlview)
@@ -2288,43 +3451,38 @@ proc delvhighlight {} {
 }
 
 proc vhighlightmore {} {
-    global hlview vhl_done commitidx vhighlights
-    global displayorder vdisporder curview
+    global hlview vhl_done commitidx vhighlights curview
 
     set max $commitidx($hlview)
-    if {$hlview == $curview} {
-       set disp $displayorder
-    } else {
-       set disp $vdisporder($hlview)
-    }
     set vr [visiblerows]
     set r0 [lindex $vr 0]
     set r1 [lindex $vr 1]
     for {set i $vhl_done} {$i < $max} {incr i} {
-       set id [lindex $disp $i]
-       if {[info exists commitrow($curview,$id)]} {
-           set row $commitrow($curview,$id)
+       set id [commitonrow $i $hlview]
+       if {[commitinview $id $curview]} {
+           set row [rowofcommit $id]
            if {$r0 <= $row && $row <= $r1} {
                if {![highlighted $row]} {
                    bolden $row mainfontbold
                }
-               set vhighlights($row) 1
+               set vhighlights($id) 1
            }
        }
     }
     set vhl_done $max
+    return 0
 }
 
 proc askvhighlight {row id} {
-    global hlview vhighlights commitrow iddrawn
+    global hlview vhighlights iddrawn
 
-    if {[info exists commitrow($hlview,$id)]} {
-       if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
+    if {[commitinview $id $hlview]} {
+       if {[info exists iddrawn($id)] && ![ishighlighted $id]} {
            bolden $row mainfontbold
        }
-       set vhighlights($row) 1
+       set vhighlights($id) 1
     } else {
-       set vhighlights($row) 0
+       set vhighlights($id) 0
     }
 }
 
@@ -2462,12 +3620,12 @@ proc askfilehighlight {row id} {
     global filehighlight fhighlights fhl_list
 
     lappend fhl_list $id
-    set fhighlights($row) -1
+    set fhighlights($id) -1
     puts $filehighlight $id
 }
 
 proc readfhighlight {} {
-    global filehighlight fhighlights commitrow curview iddrawn
+    global filehighlight fhighlights curview iddrawn
     global fhl_list find_dirn
 
     if {![info exists filehighlight]} {
@@ -2480,18 +3638,16 @@ proc readfhighlight {} {
        if {$i < 0} continue
        for {set j 0} {$j < $i} {incr j} {
            set id [lindex $fhl_list $j]
-           if {[info exists commitrow($curview,$id)]} {
-               set fhighlights($commitrow($curview,$id)) 0
-           }
+           set fhighlights($id) 0
        }
        set fhl_list [lrange $fhl_list [expr {$i+1}] end]
        if {$line eq {}} continue
-       if {![info exists commitrow($curview,$line)]} continue
-       set row $commitrow($curview,$line)
-       if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
+       if {![commitinview $line $curview]} continue
+       set row [rowofcommit $line]
+       if {[info exists iddrawn($line)] && ![ishighlighted $line]} {
            bolden $row mainfontbold
        }
-       set fhighlights($row) 1
+       set fhighlights($line) 1
     }
     if {[eof $filehighlight]} {
        # strange...
@@ -2540,7 +3696,7 @@ proc askfindhighlight {row id} {
        }
     }
     if {$isbold && [info exists iddrawn($id)]} {
-       if {![ishighlighted $row]} {
+       if {![ishighlighted $id]} {
            bolden $row mainfontbold
            if {$isbold > 1} {
                bolden_name $row mainfontbold
@@ -2550,7 +3706,7 @@ proc askfindhighlight {row id} {
            markrowmatches $row $id
        }
     }
-    set nhighlights($row) $isbold
+    set nhighlights($id) $isbold
 }
 
 proc markrowmatches {row id} {
@@ -2588,7 +3744,7 @@ proc vrel_change {name ix op} {
 # prepare for testing whether commits are descendents or ancestors of a
 proc rhighlight_sel {a} {
     global descendent desc_todo ancestor anc_todo
-    global highlight_related rhighlights
+    global highlight_related
 
     catch {unset descendent}
     set desc_todo [list $a]
@@ -2608,16 +3764,16 @@ proc rhighlight_none {} {
 }
 
 proc is_descendent {a} {
-    global curview children commitrow descendent desc_todo
+    global curview children descendent desc_todo
 
     set v $curview
-    set la $commitrow($v,$a)
+    set la [rowofcommit $a]
     set todo $desc_todo
     set leftover {}
     set done 0
     for {set i 0} {$i < [llength $todo]} {incr i} {
        set do [lindex $todo $i]
-       if {$commitrow($v,$do) < $la} {
+       if {[rowofcommit $do] < $la} {
            lappend leftover $do
            continue
        }
@@ -2640,20 +3796,20 @@ proc is_descendent {a} {
 }
 
 proc is_ancestor {a} {
-    global curview parentlist commitrow ancestor anc_todo
+    global curview parents ancestor anc_todo
 
     set v $curview
-    set la $commitrow($v,$a)
+    set la [rowofcommit $a]
     set todo $anc_todo
     set leftover {}
     set done 0
     for {set i 0} {$i < [llength $todo]} {incr i} {
        set do [lindex $todo $i]
-       if {![info exists commitrow($v,$do)] || $commitrow($v,$do) > $la} {
+       if {![commitinview $do $v] || [rowofcommit $do] > $la} {
            lappend leftover $do
            continue
        }
-       foreach np [lindex $parentlist $commitrow($v,$do)] {
+       foreach np $parents($v,$do) {
            if {![info exists ancestor($np)]} {
                set ancestor($np) 1
                lappend todo $np
@@ -2695,11 +3851,11 @@ proc askrelhighlight {row id} {
        }
     }
     if {[info exists iddrawn($id)]} {
-       if {$isbold && ![ishighlighted $row]} {
+       if {$isbold && ![ishighlighted $id]} {
            bolden $row mainfontbold
        }
     }
-    set rhighlights($row) $isbold
+    set rhighlights($id) $isbold
 }
 
 # Graph layout functions
@@ -2730,40 +3886,81 @@ proc ntimes {n o} {
     return $ret
 }
 
+proc ordertoken {id} {
+    global ordertok curview varcid varcstart varctok curview parents children
+    global nullid nullid2
+
+    if {[info exists ordertok($id)]} {
+       return $ordertok($id)
+    }
+    set origid $id
+    set todo {}
+    while {1} {
+       if {[info exists varcid($curview,$id)]} {
+           set a $varcid($curview,$id)
+           set p [lindex $varcstart($curview) $a]
+       } else {
+           set p [lindex $children($curview,$id) 0]
+       }
+       if {[info exists ordertok($p)]} {
+           set tok $ordertok($p)
+           break
+       }
+       set id [first_real_child $curview,$p]
+       if {$id eq {}} {
+           # it's a root
+           set tok [lindex $varctok($curview) $varcid($curview,$p)]
+           break
+       }
+       if {[llength $parents($curview,$id)] == 1} {
+           lappend todo [list $p {}]
+       } else {
+           set j [lsearch -exact $parents($curview,$id) $p]
+           if {$j < 0} {
+               puts "oops didn't find [shortids $p] in parents of [shortids $id]"
+           }
+           lappend todo [list $p [strrep $j]]
+       }
+    }
+    for {set i [llength $todo]} {[incr i -1] >= 0} {} {
+       set p [lindex $todo $i 0]
+       append tok [lindex $todo $i 1]
+       set ordertok($p) $tok
+    }
+    set ordertok($origid) $tok
+    return $tok
+}
+
 # Work out where id should go in idlist so that order-token
 # values increase from left to right
 proc idcol {idlist id {i 0}} {
-    global ordertok curview
-
-    set t $ordertok($curview,$id)
-    if {$i >= [llength $idlist] ||
-       $t < $ordertok($curview,[lindex $idlist $i])} {
+    set t [ordertoken $id]
+    if {$i < 0} {
+       set i 0
+    }
+    if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
        if {$i > [llength $idlist]} {
            set i [llength $idlist]
        }
-       while {[incr i -1] >= 0 &&
-              $t < $ordertok($curview,[lindex $idlist $i])} {}
+       while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
        incr i
     } else {
-       if {$t > $ordertok($curview,[lindex $idlist $i])} {
+       if {$t > [ordertoken [lindex $idlist $i]]} {
            while {[incr i] < [llength $idlist] &&
-                  $t >= $ordertok($curview,[lindex $idlist $i])} {}
+                  $t >= [ordertoken [lindex $idlist $i]]} {}
        }
     }
     return $i
 }
 
 proc initlayout {} {
-    global rowidlist rowisopt rowfinal displayorder commitlisted
+    global rowidlist rowisopt rowfinal displayorder parentlist
     global numcommits canvxmax canv
     global nextcolor
-    global parentlist
     global colormap rowtextx
-    global selectfirst
 
     set numcommits 0
     set displayorder {}
-    set commitlisted {}
     set parentlist {}
     set nextcolor 0
     set rowidlist {}
@@ -2772,16 +3969,19 @@ proc initlayout {} {
     set canvxmax [$canv cget -width]
     catch {unset colormap}
     catch {unset rowtextx}
-    set selectfirst 1
+    setcanvscroll
 }
 
 proc setcanvscroll {} {
     global canv canv2 canv3 numcommits linespc canvxmax canvy0
+    global lastscrollset lastscrollrows
 
     set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
     $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
     $canv2 conf -scrollregion [list 0 0 0 $ymax]
     $canv3 conf -scrollregion [list 0 0 0 $ymax]
+    set lastscrollset [clock clicks -milliseconds]
+    set lastscrollrows $numcommits
 }
 
 proc visiblerows {} {
@@ -2804,102 +4004,57 @@ proc visiblerows {} {
 }
 
 proc layoutmore {} {
-    global commitidx viewcomplete numcommits
-    global uparrowlen downarrowlen mingaplen curview
+    global commitidx viewcomplete curview
+    global numcommits pending_select selectedline curview
+    global lastscrollset lastscrollrows commitinterest
 
-    set show $commitidx($curview)
-    if {$show > $numcommits || $viewcomplete($curview)} {
-       showstuff $show $viewcomplete($curview)
-    }
-}
-
-proc showstuff {canshow last} {
-    global numcommits commitrow pending_select selectedline curview
-    global mainheadid displayorder selectfirst
-    global lastscrollset commitinterest
-
-    if {$numcommits == 0} {
-       global phase
-       set phase "incrdraw"
-       allcanvs delete all
-    }
-    set r0 $numcommits
-    set prev $numcommits
-    set numcommits $canshow
-    set t [clock clicks -milliseconds]
-    if {$prev < 100 || $last || $t - $lastscrollset > 500} {
-       set lastscrollset $t
+    if {$lastscrollrows < 100 || $viewcomplete($curview) ||
+       [clock clicks -milliseconds] - $lastscrollset > 500} {
        setcanvscroll
     }
-    set rows [visiblerows]
-    set r1 [lindex $rows 1]
-    if {$r1 >= $canshow} {
-       set r1 [expr {$canshow - 1}]
-    }
-    if {$r0 <= $r1} {
-       drawcommits $r0 $r1
-    }
     if {[info exists pending_select] &&
-       [info exists commitrow($curview,$pending_select)] &&
-       $commitrow($curview,$pending_select) < $numcommits} {
-       selectline $commitrow($curview,$pending_select) 1
-    }
-    if {$selectfirst} {
-       if {[info exists selectedline] || [info exists pending_select]} {
-           set selectfirst 0
-       } else {
-           set l [first_real_row]
-           selectline $l 1
-           set selectfirst 0
-       }
+       [commitinview $pending_select $curview]} {
+       selectline [rowofcommit $pending_select] 1
     }
+    drawvisible
 }
 
 proc doshowlocalchanges {} {
-    global curview mainheadid phase commitrow
+    global curview mainheadid
 
-    if {[info exists commitrow($curview,$mainheadid)] &&
-       ($phase eq {} || $commitrow($curview,$mainheadid) < $numcommits - 1)} {
+    if {[commitinview $mainheadid $curview]} {
        dodiffindex
-    } elseif {$phase ne {}} {
-       lappend commitinterest($mainheadid) {}
+    } else {
+       lappend commitinterest($mainheadid) {dodiffindex}
     }
 }
 
 proc dohidelocalchanges {} {
-    global localfrow localirow lserial
+    global nullid nullid2 lserial curview
 
-    if {$localfrow >= 0} {
-       removerow $localfrow
-       set localfrow -1
-       if {$localirow > 0} {
-           incr localirow -1
-       }
+    if {[commitinview $nullid $curview]} {
+       removefakerow $nullid
     }
-    if {$localirow >= 0} {
-       removerow $localirow
-       set localirow -1
+    if {[commitinview $nullid2 $curview]} {
+       removefakerow $nullid2
     }
     incr lserial
 }
 
 # spawn off a process to do git diff-index --cached HEAD
 proc dodiffindex {} {
-    global localirow localfrow lserial showlocalchanges
+    global lserial showlocalchanges
     global isworktree
 
     if {!$showlocalchanges || !$isworktree} return
     incr lserial
-    set localfrow -1
-    set localirow -1
     set fd [open "|git diff-index --cached HEAD" r]
     fconfigure $fd -blocking 0
     filerun $fd [list readdiffindex $fd $lserial]
 }
 
 proc readdiffindex {fd serial} {
-    global localirow commitrow mainheadid nullid2 curview
-    global commitinfo commitdata lserial
+    global mainheadid nullid nullid2 curview commitinfo commitdata lserial
 
     set isdiff 1
     if {[gets $fd line] < 0} {
@@ -2911,26 +4066,32 @@ proc readdiffindex {fd serial} {
     # we only need to see one line and we don't really care what it says...
     close $fd
 
-    # now see if there are any local changes not checked in to the index
-    if {$serial == $lserial} {
-       set fd [open "|git diff-files" r]
-       fconfigure $fd -blocking 0
-       filerun $fd [list readdifffiles $fd $serial]
+    if {$serial != $lserial} {
+       return 0
     }
 
-    if {$isdiff && $serial == $lserial && $localirow == -1} {
+    # now see if there are any local changes not checked in to the index
+    set fd [open "|git diff-files" r]
+    fconfigure $fd -blocking 0
+    filerun $fd [list readdifffiles $fd $serial]
+
+    if {$isdiff && ![commitinview $nullid2 $curview]} {
        # add the line for the changes in the index to the graph
-       set localirow $commitrow($curview,$mainheadid)
        set hl [mc "Local changes checked in to index but not committed"]
        set commitinfo($nullid2) [list  $hl {} {} {} {} "    $hl\n"]
        set commitdata($nullid2) "\n    $hl\n"
-       insertrow $localirow $nullid2
+       if {[commitinview $nullid $curview]} {
+           removefakerow $nullid
+       }
+       insertfakerow $nullid2 $mainheadid
+    } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
+       removefakerow $nullid2
     }
     return 0
 }
 
 proc readdifffiles {fd serial} {
-    global localirow localfrow commitrow mainheadid nullid curview
+    global mainheadid nullid nullid2 curview
     global commitinfo commitdata lserial
 
     set isdiff 1
@@ -2943,50 +4104,55 @@ proc readdifffiles {fd serial} {
     # we only need to see one line and we don't really care what it says...
     close $fd
 
-    if {$isdiff && $serial == $lserial && $localfrow == -1} {
+    if {$serial != $lserial} {
+       return 0
+    }
+
+    if {$isdiff && ![commitinview $nullid $curview]} {
        # add the line for the local diff to the graph
-       if {$localirow >= 0} {
-           set localfrow $localirow
-           incr localirow
-       } else {
-           set localfrow $commitrow($curview,$mainheadid)
-       }
        set hl [mc "Local uncommitted changes, not checked in to index"]
        set commitinfo($nullid) [list  $hl {} {} {} {} "    $hl\n"]
        set commitdata($nullid) "\n    $hl\n"
-       insertrow $localfrow $nullid
+       if {[commitinview $nullid2 $curview]} {
+           set p $nullid2
+       } else {
+           set p $mainheadid
+       }
+       insertfakerow $nullid $p
+    } elseif {!$isdiff && [commitinview $nullid $curview]} {
+       removefakerow $nullid
     }
     return 0
 }
 
 proc nextuse {id row} {
-    global commitrow curview children
+    global curview children
 
     if {[info exists children($curview,$id)]} {
        foreach kid $children($curview,$id) {
-           if {![info exists commitrow($curview,$kid)]} {
+           if {![commitinview $kid $curview]} {
                return -1
            }
-           if {$commitrow($curview,$kid) > $row} {
-               return $commitrow($curview,$kid)
+           if {[rowofcommit $kid] > $row} {
+               return [rowofcommit $kid]
            }
        }
     }
-    if {[info exists commitrow($curview,$id)]} {
-       return $commitrow($curview,$id)
+    if {[commitinview $id $curview]} {
+       return [rowofcommit $id]
     }
     return -1
 }
 
 proc prevuse {id row} {
-    global commitrow curview children
+    global curview children
 
     set ret -1
     if {[info exists children($curview,$id)]} {
        foreach kid $children($curview,$id) {
-           if {![info exists commitrow($curview,$kid)]} break
-           if {$commitrow($curview,$kid) < $row} {
-               set ret $commitrow($curview,$kid)
+           if {![commitinview $kid $curview]} break
+           if {[rowofcommit $kid] < $row} {
+               set ret [rowofcommit $kid]
            }
        }
     }
@@ -2995,7 +4161,7 @@ proc prevuse {id row} {
 
 proc make_idlist {row} {
     global displayorder parentlist uparrowlen downarrowlen mingaplen
-    global commitidx curview ordertok children commitrow
+    global commitidx curview children
 
     set r [expr {$row - $mingaplen - $downarrowlen - 1}]
     if {$r < 0} {
@@ -3009,6 +4175,7 @@ proc make_idlist {row} {
     if {$rb > $commitidx($curview)} {
        set rb $commitidx($curview)
     }
+    make_disporder $r [expr {$rb + 1}]
     set ids {}
     for {} {$r < $ra} {incr r} {
        set nextid [lindex $displayorder [expr {$r + 1}]]
@@ -3017,7 +4184,7 @@ proc make_idlist {row} {
            set rn [nextuse $p $r]
            if {$rn >= $row &&
                $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
-               lappend ids [list $ordertok($curview,$p) $p]
+               lappend ids [list [ordertoken $p] $p]
            }
        }
     }
@@ -3027,25 +4194,25 @@ proc make_idlist {row} {
            if {$p eq $nextid} continue
            set rn [nextuse $p $r]
            if {$rn < 0 || $rn >= $row} {
-               lappend ids [list $ordertok($curview,$p) $p]
+               lappend ids [list [ordertoken $p] $p]
            }
        }
     }
     set id [lindex $displayorder $row]
-    lappend ids [list $ordertok($curview,$id) $id]
+    lappend ids [list [ordertoken $id] $id]
     while {$r < $rb} {
        foreach p [lindex $parentlist $r] {
            set firstkid [lindex $children($curview,$p) 0]
-           if {$commitrow($curview,$firstkid) < $row} {
-               lappend ids [list $ordertok($curview,$p) $p]
+           if {[rowofcommit $firstkid] < $row} {
+               lappend ids [list [ordertoken $p] $p]
            }
        }
        incr r
        set id [lindex $displayorder $r]
        if {$id ne {}} {
            set firstkid [lindex $children($curview,$id) 0]
-           if {$firstkid ne {} && $commitrow($curview,$firstkid) < $row} {
-               lappend ids [list $ordertok($curview,$id) $id]
+           if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
+               lappend ids [list [ordertoken $id] $id]
            }
        }
     }
@@ -3091,8 +4258,9 @@ proc layoutrows {row endrow} {
     global rowidlist rowisopt rowfinal displayorder
     global uparrowlen downarrowlen maxwidth mingaplen
     global children parentlist
-    global commitidx viewcomplete curview commitrow
+    global commitidx viewcomplete curview
 
+    make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
     set idlist {}
     if {$row > 0} {
        set rm1 [expr {$row - 1}]
@@ -3148,7 +4316,7 @@ proc layoutrows {row endrow} {
                foreach p [lindex $parentlist $r] {
                    if {[lsearch -exact $idlist $p] >= 0} continue
                    set fk [lindex $children($curview,$p) 0]
-                   if {$commitrow($curview,$fk) < $row} {
+                   if {[rowofcommit $fk] < $row} {
                        set x [idcol $idlist $p $x]
                        set idlist [linsert $idlist $x $p]
                    }
@@ -3157,7 +4325,7 @@ proc layoutrows {row endrow} {
                    set p [lindex $displayorder $r]
                    if {[lsearch -exact $idlist $p] < 0} {
                        set fk [lindex $children($curview,$p) 0]
-                       if {$fk ne {} && $commitrow($curview,$fk) < $row} {
+                       if {$fk ne {} && [rowofcommit $fk] < $row} {
                            set x [idcol $idlist $p $x]
                            set idlist [linsert $idlist $x $p]
                        }
@@ -3372,7 +4540,7 @@ proc linewidth {id} {
 }
 
 proc rowranges {id} {
-    global commitrow curview children uparrowlen downarrowlen
+    global curview children uparrowlen downarrowlen
     global rowidlist
 
     set kids $children($curview,$id)
@@ -3382,13 +4550,13 @@ proc rowranges {id} {
     set ret {}
     lappend kids $id
     foreach child $kids {
-       if {![info exists commitrow($curview,$child)]} break
-       set row $commitrow($curview,$child)
+       if {![commitinview $child $curview]} break
+       set row [rowofcommit $child]
        if {![info exists prev]} {
            lappend ret [expr {$row + 1}]
        } else {
            if {$row <= $prevrow} {
-               puts "oops children out of order [shortids $id] $row < [shortids $prev] $prevrow"
+               puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
            }
            # see if the line extends the whole way from prevrow to row
            if {$row > $prevrow + $uparrowlen + $downarrowlen &&
@@ -3421,7 +4589,7 @@ proc rowranges {id} {
        if {$child eq $id} {
            lappend ret $row
        }
-       set prev $id
+       set prev $child
        set prevrow $row
     }
     return $ret
@@ -3669,14 +4837,14 @@ proc drawlines {id} {
 }
 
 proc drawcmittext {id row col} {
-    global linespc canv canv2 canv3 canvy0 fgcolor curview
-    global commitlisted commitinfo rowidlist parentlist
+    global linespc canv canv2 canv3 fgcolor curview
+    global cmitlisted commitinfo rowidlist parentlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag selectedline
     global canvxmax boldrows boldnamerows fgcolor nullid nullid2
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
-    set listed [lindex $commitlisted $row]
+    set listed $cmitlisted($curview,$id)
     if {$id eq $nullid} {
        set ofill red
     } elseif {$id eq $nullid2} {
@@ -3732,7 +4900,7 @@ proc drawcmittext {id row col} {
     set date [formatdate $date]
     set font mainfont
     set nfont mainfont
-    set isbold [ishighlighted $row]
+    set isbold [ishighlighted $id]
     if {$isbold > 0} {
        lappend boldrows $row
        set font mainfontbold
@@ -3761,7 +4929,7 @@ proc drawcmittext {id row col} {
 proc drawcmitrow {row} {
     global displayorder rowidlist nrows_drawn
     global iddrawn markingmatches
-    global commitinfo parentlist numcommits
+    global commitinfo numcommits
     global filehighlight fhighlights findpattern nhighlights
     global hlview vhighlights
     global highlight_related rhighlights
@@ -3769,16 +4937,16 @@ proc drawcmitrow {row} {
     if {$row >= $numcommits} return
 
     set id [lindex $displayorder $row]
-    if {[info exists hlview] && ![info exists vhighlights($row)]} {
+    if {[info exists hlview] && ![info exists vhighlights($id)]} {
        askvhighlight $row $id
     }
-    if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
+    if {[info exists filehighlight] && ![info exists fhighlights($id)]} {
        askfilehighlight $row $id
     }
-    if {$findpattern ne {} && ![info exists nhighlights($row)]} {
+    if {$findpattern ne {} && ![info exists nhighlights($id)]} {
        askfindhighlight $row $id
     }
-    if {$highlight_related ne [mc "None"] && ![info exists rhighlights($row)]} {
+    if {$highlight_related ne [mc "None"] && ![info exists rhighlights($id)]} {
        askrelhighlight $row $id
     }
     if {![info exists iddrawn($id)]} {
@@ -3881,23 +5049,79 @@ proc drawcommits {row {endrow {}}} {
     }
 }
 
-proc drawfrac {f0 f1} {
-    global canv linespc
+proc undolayout {row} {
+    global uparrowlen mingaplen downarrowlen
+    global rowidlist rowisopt rowfinal need_redisplay
+
+    set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
+    if {$r < 0} {
+       set r 0
+    }
+    if {[llength $rowidlist] > $r} {
+       incr r -1
+       set rowidlist [lrange $rowidlist 0 $r]
+       set rowfinal [lrange $rowfinal 0 $r]
+       set rowisopt [lrange $rowisopt 0 $r]
+       set need_redisplay 1
+       run drawvisible
+    }
+}
+
+proc drawvisible {} {
+    global canv linespc curview vrowmod selectedline targetrow targetid
+    global need_redisplay cscroll numcommits
 
+    set fs [$canv yview]
     set ymax [lindex [$canv cget -scrollregion] 3]
-    if {$ymax eq {} || $ymax == 0} return
+    if {$ymax eq {} || $ymax == 0 || $numcommits == 0} return
+    set f0 [lindex $fs 0]
+    set f1 [lindex $fs 1]
     set y0 [expr {int($f0 * $ymax)}]
-    set row [expr {int(($y0 - 3) / $linespc) - 1}]
     set y1 [expr {int($f1 * $ymax)}]
+
+    if {[info exists targetid]} {
+       if {[commitinview $targetid $curview]} {
+           set r [rowofcommit $targetid]
+           if {$r != $targetrow} {
+               # Fix up the scrollregion and change the scrolling position
+               # now that our target row has moved.
+               set diff [expr {($r - $targetrow) * $linespc}]
+               set targetrow $r
+               setcanvscroll
+               set ymax [lindex [$canv cget -scrollregion] 3]
+               incr y0 $diff
+               incr y1 $diff
+               set f0 [expr {$y0 / $ymax}]
+               set f1 [expr {$y1 / $ymax}]
+               allcanvs yview moveto $f0
+               $cscroll set $f0 $f1
+               set need_redisplay 1
+           }
+       } else {
+           unset targetid
+       }
+    }
+
+    set row [expr {int(($y0 - 3) / $linespc) - 1}]
     set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
+    if {$endrow >= $vrowmod($curview)} {
+       update_arcrows $curview
+    }
+    if {[info exists selectedline] &&
+       $row <= $selectedline && $selectedline <= $endrow} {
+       set targetrow $selectedline
+    } elseif {[info exists targetid]} {
+       set targetrow [expr {int(($row + $endrow) / 2)}]
+    }
+    if {[info exists targetrow]} {
+       if {$targetrow >= $numcommits} {
+           set targetrow [expr {$numcommits - 1}]
+       }
+       set targetid [commitonrow $targetrow]
+    }
     drawcommits $row $endrow
 }
 
-proc drawvisible {} {
-    global canv
-    eval drawfrac [$canv yview]
-}
-
 proc clear_display {} {
     global iddrawn linesegs need_redisplay nrows_drawn
     global vhighlights fhighlights nhighlights rhighlights
@@ -3950,7 +5174,7 @@ proc findcrossings {id} {
 
 proc assigncolor {id} {
     global colormap colors nextcolor
-    global commitrow parentlist children children curview
+    global parents children children curview
 
     if {[info exists colormap($id)]} return
     set ncolors [llength $colors]
@@ -3962,7 +5186,7 @@ proc assigncolor {id} {
     if {[llength $kids] == 1} {
        set child [lindex $kids 0]
        if {[info exists colormap($child)]
-           && [llength [lindex $parentlist $commitrow($curview,$child)]] == 1} {
+           && [llength $parents($curview,$child)] == 1} {
            set colormap($id) $colormap($child)
            return
        }
@@ -3990,7 +5214,7 @@ proc assigncolor {id} {
                && [lsearch -exact $badcolors $colormap($child)] < 0} {
                lappend badcolors $colormap($child)
            }
-           foreach p [lindex $parentlist $commitrow($curview,$child)] {
+           foreach p $parents($curview,$child) {
                if {[info exists colormap($p)]
                    && [lsearch -exact $badcolors $colormap($p)] < 0} {
                    lappend badcolors $colormap($p)
@@ -4023,7 +5247,7 @@ proc bindline {t id} {
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
-    global canv commitrow rowtextx curview fgcolor bgcolor
+    global canv rowtextx curview fgcolor bgcolor
 
     set marks {}
     set ntags 0
@@ -4073,7 +5297,7 @@ proc drawtags {id x xt y1} {
                       $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
                       -width 1 -outline black -fill yellow -tags tag.$id]
            $canv bind $t <1> [list showtag $tag 1]
-           set rowtextx($commitrow($curview,$id)) [expr {$xr + $linespc}]
+           set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
        } else {
            # draw a head or other ref
            if {[incr nheads -1] >= 0} {
@@ -4127,103 +5351,6 @@ proc show_status {msg} {
        -tags text -fill $fgcolor
 }
 
-# Insert a new commit as the child of the commit on row $row.
-# The new commit will be displayed on row $row and the commits
-# on that row and below will move down one row.
-proc insertrow {row newcmit} {
-    global displayorder parentlist commitlisted children
-    global commitrow curview rowidlist rowisopt rowfinal numcommits
-    global numcommits
-    global selectedline commitidx ordertok
-
-    if {$row >= $numcommits} {
-       puts "oops, inserting new row $row but only have $numcommits rows"
-       return
-    }
-    set p [lindex $displayorder $row]
-    set displayorder [linsert $displayorder $row $newcmit]
-    set parentlist [linsert $parentlist $row $p]
-    set kids $children($curview,$p)
-    lappend kids $newcmit
-    set children($curview,$p) $kids
-    set children($curview,$newcmit) {}
-    set commitlisted [linsert $commitlisted $row 1]
-    set l [llength $displayorder]
-    for {set r $row} {$r < $l} {incr r} {
-       set id [lindex $displayorder $r]
-       set commitrow($curview,$id) $r
-    }
-    incr commitidx($curview)
-    set ordertok($curview,$newcmit) $ordertok($curview,$p)
-
-    if {$row < [llength $rowidlist]} {
-       set idlist [lindex $rowidlist $row]
-       if {$idlist ne {}} {
-           if {[llength $kids] == 1} {
-               set col [lsearch -exact $idlist $p]
-               lset idlist $col $newcmit
-           } else {
-               set col [llength $idlist]
-               lappend idlist $newcmit
-           }
-       }
-       set rowidlist [linsert $rowidlist $row $idlist]
-       set rowisopt [linsert $rowisopt $row 0]
-       set rowfinal [linsert $rowfinal $row [lindex $rowfinal $row]]
-    }
-
-    incr numcommits
-
-    if {[info exists selectedline] && $selectedline >= $row} {
-       incr selectedline
-    }
-    redisplay
-}
-
-# Remove a commit that was inserted with insertrow on row $row.
-proc removerow {row} {
-    global displayorder parentlist commitlisted children
-    global commitrow curview rowidlist rowisopt rowfinal numcommits
-    global numcommits
-    global linesegends selectedline commitidx
-
-    if {$row >= $numcommits} {
-       puts "oops, removing row $row but only have $numcommits rows"
-       return
-    }
-    set rp1 [expr {$row + 1}]
-    set id [lindex $displayorder $row]
-    set p [lindex $parentlist $row]
-    set displayorder [lreplace $displayorder $row $row]
-    set parentlist [lreplace $parentlist $row $row]
-    set commitlisted [lreplace $commitlisted $row $row]
-    set kids $children($curview,$p)
-    set i [lsearch -exact $kids $id]
-    if {$i >= 0} {
-       set kids [lreplace $kids $i $i]
-       set children($curview,$p) $kids
-    }
-    set l [llength $displayorder]
-    for {set r $row} {$r < $l} {incr r} {
-       set id [lindex $displayorder $r]
-       set commitrow($curview,$id) $r
-    }
-    incr commitidx($curview) -1
-
-    if {$row < [llength $rowidlist]} {
-       set rowidlist [lreplace $rowidlist $row $row]
-       set rowisopt [lreplace $rowisopt $row $row]
-       set rowfinal [lreplace $rowfinal $row $row]
-    }
-
-    incr numcommits -1
-
-    if {[info exists selectedline] && $selectedline > $row} {
-       incr selectedline -1
-    }
-    redisplay
-}
-
 # Don't change the text pane cursor if it is currently the hand cursor,
 # showing that we are over a sha1 ID link.
 proc settextcursor {c} {
@@ -4326,9 +5453,9 @@ proc stopfinding {} {
 
 proc findmore {} {
     global commitdata commitinfo numcommits findpattern findloc
-    global findstartline findcurline displayorder
+    global findstartline findcurline findallowwrap
     global find_dirn gdttype fhighlights fprogcoord
-    global findallowwrap
+    global curview varcorder vrownum varccommits vrowmod
 
     if {![info exists find_dirn]} {
        return 0
@@ -4364,14 +5491,31 @@ proc findmore {} {
        set n 500
        set moretodo 1
     }
+    if {$l + ($find_dirn > 0? $n: 1) > $vrowmod($curview)} {
+       update_arcrows $curview
+    }
     set found 0
     set domore 1
+    set ai [bsearch $vrownum($curview) $l]
+    set a [lindex $varcorder($curview) $ai]
+    set arow [lindex $vrownum($curview) $ai]
+    set ids [lindex $varccommits($curview,$a)]
+    set arowend [expr {$arow + [llength $ids]}]
     if {$gdttype eq [mc "containing:"]} {
        for {} {$n > 0} {incr n -1; incr l $find_dirn} {
-           set id [lindex $displayorder $l]
+           if {$l < $arow || $l >= $arowend} {
+               incr ai $find_dirn
+               set a [lindex $varcorder($curview) $ai]
+               set arow [lindex $vrownum($curview) $ai]
+               set ids [lindex $varccommits($curview,$a)]
+               set arowend [expr {$arow + [llength $ids]}]
+           }
+           set id [lindex $ids [expr {$l - $arow}]]
            # shouldn't happen unless git log doesn't give all the commits...
-           if {![info exists commitdata($id)]} continue
-           if {![doesmatch $commitdata($id)]} continue
+           if {![info exists commitdata($id)] ||
+               ![doesmatch $commitdata($id)]} {
+               continue
+           }
            if {![info exists commitinfo($id)]} {
                getcommit $id
            }
@@ -4387,16 +5531,27 @@ proc findmore {} {
        }
     } else {
        for {} {$n > 0} {incr n -1; incr l $find_dirn} {
-           set id [lindex $displayorder $l]
-           if {![info exists fhighlights($l)]} {
+           if {$l < $arow || $l >= $arowend} {
+               incr ai $find_dirn
+               set a [lindex $varcorder($curview) $ai]
+               set arow [lindex $vrownum($curview) $ai]
+               set ids [lindex $varccommits($curview,$a)]
+               set arowend [expr {$arow + [llength $ids]}]
+           }
+           set id [lindex $ids [expr {$l - $arow}]]
+           if {![info exists fhighlights($id)]} {
+               # this sets fhighlights($id) to -1
                askfilehighlight $l $id
+           }
+           if {$fhighlights($id) > 0} {
+               set found $domore
+               break
+           }
+           if {$fhighlights($id) < 0} {
                if {$domore} {
                    set domore 0
                    set findcurline [expr {$l - $find_dirn}]
                }
-           } elseif {$fhighlights($l)} {
-               set found $domore
-               break
            }
        }
     }
@@ -4490,7 +5645,9 @@ proc selcanvline {w x y} {
        set l 0
     }
     if {$w eq $canv} {
-       if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
+       set xmax [lindex [$canv cget -scrollregion] 2]
+       set xleft [expr {[lindex [$canv xview] 0] * $xmax}]
+       if {![info exists rowtextx($l)] || $xleft + $x < $rowtextx($l)} return
     }
     unmarkmatches
     selectline $l 1
@@ -4511,7 +5668,7 @@ proc commit_descriptor {p} {
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
 proc appendwithlinks {text tags} {
-    global ctext commitrow linknum curview pendinglinks
+    global ctext linknum curview pendinglinks
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
@@ -4529,11 +5686,11 @@ proc appendwithlinks {text tags} {
 }
 
 proc setlink {id lk} {
-    global curview commitrow ctext pendinglinks commitinterest
+    global curview ctext pendinglinks commitinterest
 
-    if {[info exists commitrow($curview,$id)]} {
+    if {[commitinview $id $curview]} {
        $ctext tag conf $lk -foreground blue -underline 1
-       $ctext tag bind $lk <1> [list selectline $commitrow($curview,$id) 1]
+       $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
        $ctext tag bind $lk <Enter> {linkcursor %W 1}
        $ctext tag bind $lk <Leave> {linkcursor %W -1}
     } else {
@@ -4584,7 +5741,7 @@ proc viewnextline {dir} {
 # add a list of tag or branch names at position pos
 # returns the number of names inserted
 proc appendrefs {pos ids var} {
-    global ctext commitrow linknum curview $var maxrefs
+    global ctext linknum curview $var maxrefs
 
     if {[catch {$ctext index $pos}]} {
        return 0
@@ -4687,12 +5844,12 @@ proc make_secsel {l} {
 
 proc selectline {l isnew} {
     global canv ctext commitinfo selectedline
-    global displayorder
-    global canvy0 linespc parentlist children curview
+    global canvy0 linespc parents children curview
     global currentid sha1entry
     global commentend idtags linknum
     global mergemax numcommits pending_select
     global cmitmode showneartags allcommits
+    global targetrow targetid lastscrollrows
     global autoselect
 
     catch {unset pending_select}
@@ -4701,6 +5858,15 @@ proc selectline {l isnew} {
     unsel_reflist
     stopfinding
     if {$l < 0 || $l >= $numcommits} return
+    set id [commitonrow $l]
+    set targetid $id
+    set targetrow $l
+    set selectedline $l
+    set currentid $id
+    if {$lastscrollrows < $numcommits} {
+       setcanvscroll
+    }
+
     set y [expr {$canvy0 + $l * $linespc}]
     set ymax [lindex [$canv cget -scrollregion] 3]
     set ytop [expr {$y - $linespc - 1}]
@@ -4740,13 +5906,9 @@ proc selectline {l isnew} {
     make_secsel $l
 
     if {$isnew} {
-       addtohistory [list selectline $l 0]
+       addtohistory [list selbyid $id]
     }
 
-    set selectedline $l
-
-    set id [lindex $displayorder $l]
-    set currentid $id
     $sha1entry delete 0 end
     $sha1entry insert 0 $id
     if {$autoselect} {
@@ -4758,6 +5920,9 @@ proc selectline {l isnew} {
     $ctext conf -state normal
     clear_ctext
     set linknum 0
+    if {![info exists commitinfo($id)]} {
+       getcommit $id
+    }
     set info $commitinfo($id)
     set date [formatdate [lindex $info 2]]
     $ctext insert end "[mc "Author"]: [lindex $info 1]  $date\n"
@@ -4772,7 +5937,7 @@ proc selectline {l isnew} {
     }
 
     set headers {}
-    set olds [lindex $parentlist $l]
+    set olds $parents($curview,$id)
     if {[llength $olds] > 1} {
        set np 0
        foreach p $olds {
@@ -4830,7 +5995,7 @@ proc selectline {l isnew} {
     } elseif {[llength $olds] <= 1} {
        startdiff $id
     } else {
-       mergediff $id $l
+       mergediff $id
     }
 }
 
@@ -4992,11 +6157,12 @@ proc gettreeline {gtf id} {
        if {$diffids eq $nullid} {
            set fname $line
        } else {
-           if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
            set i [string first "\t" $line]
            if {$i < 0} continue
-           set sha1 [lindex $line 2]
            set fname [string range $line [expr {$i+1}] end]
+           set line [string range $line 0 [expr {$i-1}]]
+           if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
+           set sha1 [lindex $line 2]
            if {[string index $fname 0] eq "\""} {
                set fname [lindex $fname 0]
            }
@@ -5075,19 +6241,19 @@ proc getblobline {bf id} {
     return [expr {$nl >= 1000? 2: 1}]
 }
 
-proc mergediff {id l} {
+proc mergediff {id} {
     global diffmergeid mdifffd
     global diffids
+    global parents
     global diffcontext
-    global parentlist
-    global limitdiffs viewfiles curview
+    global limitdiffs vfilelimit curview
 
     set diffmergeid $id
     set diffids $id
     # this doesn't seem to actually affect anything...
     set cmd [concat | git diff-tree --no-commit-id --cc -U$diffcontext $id]
-    if {$limitdiffs && $viewfiles($curview) ne {}} {
-       set cmd [concat $cmd -- $viewfiles($curview)]
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
+       set cmd [concat $cmd -- $vfilelimit($curview)]
     }
     if {[catch {set mdf [open $cmd r]} err]} {
        error_popup "[mc "Error getting merge diffs:"] $err"
@@ -5095,7 +6261,7 @@ proc mergediff {id l} {
     }
     fconfigure $mdf -blocking 0
     set mdifffd($id) $mdf
-    set np [llength [lindex $parentlist $l]]
+    set np [llength $parents($curview,$id)]
     settabs $np
     filerun $mdf [list getmergediffline $mdf $id $np]
 }
@@ -5265,7 +6431,7 @@ proc gettreediffs {ids} {
 
 proc gettreediffline {gdtf ids} {
     global treediff treediffs treepending diffids diffmergeid
-    global cmitmode viewfiles curview limitdiffs
+    global cmitmode vfilelimit curview limitdiffs
 
     set nr 0
     while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
@@ -5282,10 +6448,10 @@ proc gettreediffline {gdtf ids} {
        return [expr {$nr >= 1000? 2: 1}]
     }
     close $gdtf
-    if {$limitdiffs && $viewfiles($curview) ne {}} {
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
        set flist {}
        foreach f $treediff {
-           if {[path_filter $viewfiles($curview) $f]} {
+           if {[path_filter $vfilelimit($curview) $f]} {
                lappend flist $f
            }
        }
@@ -5331,14 +6497,14 @@ proc getblobdiffs {ids} {
     global diffinhdr treediffs
     global diffcontext
     global ignorespace
-    global limitdiffs viewfiles curview
+    global limitdiffs vfilelimit curview
 
     set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
     if {$ignorespace} {
        append cmd " -w"
     }
-    if {$limitdiffs && $viewfiles($curview) ne {}} {
-       set cmd [concat $cmd -- $viewfiles($curview)]
+    if {$limitdiffs && $vfilelimit($curview) ne {}} {
+       set cmd [concat $cmd -- $vfilelimit($curview)]
     }
     if {[catch {set bdf [open $cmd r]} err]} {
        puts "error getting diffs: $err"
@@ -5478,26 +6644,44 @@ proc changediffdisp {} {
     $ctext tag conf d1 -elide [lindex $diffelide 1]
 }
 
+proc highlightfile {loc cline} {
+    global ctext cflist cflist_top
+
+    $ctext yview $loc
+    $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
+    $cflist tag add highlight $cline.0 "$cline.0 lineend"
+    $cflist see $cline.0
+    set cflist_top $cline
+}
+
 proc prevfile {} {
-    global difffilestart ctext
-    set prev [lindex $difffilestart 0]
+    global difffilestart ctext cmitmode
+
+    if {$cmitmode eq "tree"} return
+    set prev 0.0
+    set prevline 1
     set here [$ctext index @0,0]
     foreach loc $difffilestart {
        if {[$ctext compare $loc >= $here]} {
-           $ctext yview $prev
+           highlightfile $prev $prevline
            return
        }
        set prev $loc
+       incr prevline
     }
-    $ctext yview $prev
+    highlightfile $prev $prevline
 }
 
 proc nextfile {} {
-    global difffilestart ctext
+    global difffilestart ctext cmitmode
+
+    if {$cmitmode eq "tree"} return
     set here [$ctext index @0,0]
+    set line 1
     foreach loc $difffilestart {
+       incr line
        if {[$ctext compare $loc > $here]} {
-           $ctext yview $loc
+           highlightfile $loc $line
            return
        }
     }
@@ -5731,7 +6915,7 @@ proc fontname {f} {
 }
 
 proc incrfont {inc} {
-    global mainfont textfont ctext canv phase cflist showrefstop
+    global mainfont textfont ctext canv cflist showrefstop
     global stopped entries fontattr
 
     unmarkmatches
@@ -5782,8 +6966,7 @@ proc sha1change {n1 n2 op} {
 }
 
 proc gotocommit {} {
-    global sha1string currentid commitrow tagids headids
-    global displayorder numcommits curview
+    global sha1string tagids headids curview varcid
 
     if {$sha1string == {}
        || ([info exists currentid] && $sha1string == $currentid)} return
@@ -5794,23 +6977,18 @@ proc gotocommit {} {
     } else {
        set id [string tolower $sha1string]
        if {[regexp {^[0-9a-f]{4,39}$} $id]} {
-           set matches {}
-           foreach i $displayorder {
-               if {[string match $id* $i]} {
-                   lappend matches $i
-               }
-           }
+           set matches [array names varcid "$curview,$id*"]
            if {$matches ne {}} {
                if {[llength $matches] > 1} {
                    error_popup [mc "Short SHA1 id %s is ambiguous" $id]
                    return
                }
-               set id [lindex $matches 0]
+               set id [lindex [split [lindex $matches 0] ","] 1]
            }
        }
     }
-    if {[info exists commitrow($curview,$id)]} {
-       selectline $commitrow($curview,$id) 1
+    if {[commitinview $id $curview]} {
+       selectline [rowofcommit $id] 1
        return
     }
     if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
@@ -5919,7 +7097,7 @@ proc arrowjump {id n y} {
 }
 
 proc lineclick {x y id isnew} {
-    global ctext commitinfo children canv thickerline curview commitrow
+    global ctext commitinfo children canv thickerline curview
 
     if {![info exists commitinfo($id)] && ![getcommit $id]} return
     unmarkmatches
@@ -5987,9 +7165,9 @@ proc normalline {} {
 }
 
 proc selbyid {id} {
-    global commitrow curview
-    if {[info exists commitrow($curview,$id)]} {
-       selectline $commitrow($curview,$id) 1
+    global curview
+    if {[commitinview $id $curview]} {
+       selectline [rowofcommit $id] 1
     }
 }
 
@@ -6002,20 +7180,24 @@ proc mstime {} {
 }
 
 proc rowmenu {x y id} {
-    global rowctxmenu commitrow selectedline rowmenuid curview
+    global rowctxmenu selectedline rowmenuid curview
     global nullid nullid2 fakerowmenu mainhead
 
     stopfinding
     set rowmenuid $id
     if {![info exists selectedline]
-       || $commitrow($curview,$id) eq $selectedline} {
+       || [rowofcommit $id] eq $selectedline} {
        set state disabled
     } else {
        set state normal
     }
     if {$id ne $nullid && $id ne $nullid2} {
        set menu $rowctxmenu
-       $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
+       if {$mainhead ne {}} {
+           $menu entryconfigure 7 -label [mc "Reset %s branch to here" $mainhead]
+       } else {
+           $menu entryconfigure 7 -label [mc "Detached head: can't reset" $mainhead] -state disabled
+       }
     } else {
        set menu $fakerowmenu
     }
@@ -6026,15 +7208,15 @@ proc rowmenu {x y id} {
 }
 
 proc diffvssel {dirn} {
-    global rowmenuid selectedline displayorder
+    global rowmenuid selectedline
 
     if {![info exists selectedline]} return
     if {$dirn} {
-       set oldid [lindex $displayorder $selectedline]
+       set oldid [commitonrow $selectedline]
        set newid $rowmenuid
     } else {
        set oldid $rowmenuid
-       set newid [lindex $displayorder $selectedline]
+       set newid [commitonrow $selectedline]
     }
     addtohistory [list doseldiff $oldid $newid]
     doseldiff $oldid $newid
@@ -6212,24 +7394,24 @@ proc domktag {} {
 }
 
 proc redrawtags {id} {
-    global canv linehtag commitrow idpos selectedline curview
+    global canv linehtag idpos currentid curview
     global canvxmax iddrawn
 
-    if {![info exists commitrow($curview,$id)]} return
+    if {![commitinview $id $curview]} return
     if {![info exists iddrawn($id)]} return
-    drawcommits $commitrow($curview,$id)
+    set row [rowofcommit $id]
     $canv delete tag.$id
     set xt [eval drawtags $id $idpos($id)]
-    $canv coords $linehtag($commitrow($curview,$id)) $xt [lindex $idpos($id) 2]
-    set text [$canv itemcget $linehtag($commitrow($curview,$id)) -text]
-    set xr [expr {$xt + [font measure mainfont $text]}]
+    $canv coords $linehtag($row) $xt [lindex $idpos($id) 2]
+    set text [$canv itemcget $linehtag($row) -text]
+    set font [$canv itemcget $linehtag($row) -font]
+    set xr [expr {$xt + [font measure $font $text]}]
     if {$xr > $canvxmax} {
        set canvxmax $xr
        setcanvscroll
     }
-    if {[info exists selectedline]
-       && $selectedline == $commitrow($curview,$id)} {
-       selectline $selectedline 0
+    if {[info exists currentid] && $currentid == $id} {
+       make_secsel $row
     }
 }
 
@@ -6355,8 +7537,8 @@ proc mkbrgo {top} {
 }
 
 proc cherrypick {} {
-    global rowmenuid curview commitrow
-    global mainhead
+    global rowmenuid curview
+    global mainhead mainheadid
 
     set oldhead [exec git rev-parse HEAD]
     set dheads [descheads $rowmenuid]
@@ -6382,20 +7564,22 @@ proc cherrypick {} {
        return
     }
     addnewchild $newhead $oldhead
-    if {[info exists commitrow($curview,$oldhead)]} {
-       insertrow $commitrow($curview,$oldhead) $newhead
+    if {[commitinview $oldhead $curview]} {
+       insertrow $newhead $oldhead $curview
        if {$mainhead ne {}} {
            movehead $newhead $mainhead
            movedhead $newhead $mainhead
+           set mainheadid $newhead
        }
        redrawtags $oldhead
        redrawtags $newhead
+       selbyid $newhead
     }
     notbusy cherrypick
 }
 
 proc resethead {} {
-    global mainheadid mainhead rowmenuid confirm_ok resettype
+    global mainhead rowmenuid confirm_ok resettype
 
     set confirm_ok 0
     set w ".confirmreset"
@@ -6428,12 +7612,13 @@ proc resethead {} {
     tkwait window $w
     if {!$confirm_ok} return
     if {[catch {set fd [open \
-           [list | sh -c "git reset --$resettype $rowmenuid 2>&1"] r]} err]} {
+           [list | git reset --$resettype $rowmenuid 2>@1] r]} err]} {
        error_popup $err
     } else {
        dohidelocalchanges
        filerun $fd [list readresetstat $fd]
        nowbusy reset [mc "Resetting"]
+       selbyid $rowmenuid
     }
 }
 
@@ -6489,24 +7674,46 @@ proc cobranch {} {
     global showlocalchanges mainheadid
 
     # check the tree is clean first??
-    set oldmainhead $mainhead
     nowbusy checkout [mc "Checking out"]
     update
     dohidelocalchanges
     if {[catch {
-       exec git checkout -q $headmenuhead
+       set fd [open [list | git checkout $headmenuhead 2>@1] r]
     } err]} {
        notbusy checkout
        error_popup $err
+       if {$showlocalchanges} {
+           dodiffindex
+       }
     } else {
-       notbusy checkout
-       set mainhead $headmenuhead
-       set mainheadid $headmenuid
-       if {[info exists headids($oldmainhead)]} {
-           redrawtags $headids($oldmainhead)
+       filerun $fd [list readcheckoutstat $fd $headmenuhead $headmenuid]
+    }
+}
+
+proc readcheckoutstat {fd newhead newheadid} {
+    global mainhead mainheadid headids showlocalchanges progresscoords
+
+    if {[gets $fd line] >= 0} {
+       if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
+           set progresscoords [list 0 [expr {1.0 * $m / $n}]]
+           adjustprogress
        }
-       redrawtags $headmenuid
+       return 1
+    }
+    set progresscoords {0 0}
+    adjustprogress
+    notbusy checkout
+    if {[catch {close $fd} err]} {
+       error_popup $err
+    }
+    set oldmainhead $mainhead
+    set mainhead $newhead
+    set mainheadid $newheadid
+    if {[info exists headids($oldmainhead)]} {
+       redrawtags $headids($oldmainhead)
     }
+    redrawtags $newheadid
+    selbyid $newheadid
     if {$showlocalchanges} {
        dodiffindex
     }
@@ -6620,13 +7827,13 @@ proc reflistfilter_change {n1 n2 op} {
 
 proc refill_reflist {} {
     global reflist reflistfilter showrefstop headids tagids otherrefids
-    global commitrow curview commitinterest
+    global curview commitinterest
 
     if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
     set refs {}
     foreach n [array names headids] {
        if {[string match $reflistfilter $n]} {
-           if {[info exists commitrow($curview,$headids($n))]} {
+           if {[commitinview $headids($n) $curview]} {
                lappend refs [list $n H]
            } else {
                set commitinterest($headids($n)) {run refill_reflist}
@@ -6635,7 +7842,7 @@ proc refill_reflist {} {
     }
     foreach n [array names tagids] {
        if {[string match $reflistfilter $n]} {
-           if {[info exists commitrow($curview,$tagids($n))]} {
+           if {[commitinview $tagids($n) $curview]} {
                lappend refs [list $n T]
            } else {
                set commitinterest($tagids($n)) {run refill_reflist}
@@ -6644,7 +7851,7 @@ proc refill_reflist {} {
     }
     foreach n [array names otherrefids] {
        if {[string match $reflistfilter $n]} {
-           if {[info exists commitrow($curview,$otherrefids($n))]} {
+           if {[commitinview $otherrefids($n) $curview]} {
                lappend refs [list $n o]
            } else {
                set commitinterest($otherrefids($n)) {run refill_reflist}
@@ -7788,7 +8995,7 @@ proc changedrefs {} {
 }
 
 proc rereadrefs {} {
-    global idtags idheads idotherrefs mainhead
+    global idtags idheads idotherrefs mainheadid
 
     set refids [concat [array names idtags] \
                    [array names idheads] [array names idotherrefs]]
@@ -7797,7 +9004,7 @@ proc rereadrefs {} {
            set ref($id) [listrefs $id]
        }
     }
-    set oldmainhead $mainhead
+    set oldmainhead $mainheadid
     readrefs
     changedrefs
     set refids [lsort -unique [concat $refids [array names idtags] \
@@ -7805,8 +9012,8 @@ proc rereadrefs {} {
     foreach id $refids {
        set v [listrefs $id]
        if {![info exists ref($id)] || $ref($id) != $v ||
-           ($id eq $oldmainhead && $id ne $mainhead) ||
-           ($id eq $mainhead && $id ne $oldmainhead)} {
+           ($id eq $oldmainhead && $id ne $mainheadid) ||
+           ($id eq $mainheadid && $id ne $oldmainhead)} {
            redrawtags $id
        }
     }
@@ -7858,9 +9065,15 @@ proc showtag {tag isnew} {
 
 proc doquit {} {
     global stopped
+    global gitktmpdir
+
     set stopped 100
     savestuff .
     destroy .
+
+    if {[info exists gitktmpdir]} {
+       catch {file delete -force $gitktmpdir}
+    }
 }
 
 proc mkfontdisp {font top which} {
@@ -7989,7 +9202,7 @@ proc doprefs {} {
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global bgcolor fgcolor ctext diffcolors selectbgcolor
-    global tabstop limitdiffs autoselect
+    global tabstop limitdiffs autoselect extdifftool
 
     set top .gitkprefs
     set prefstop $top
@@ -8041,6 +9254,15 @@ proc doprefs {} {
     pack $top.ldiff.b $top.ldiff.l -side left
     grid x $top.ldiff -sticky w
 
+    entry $top.extdifft -textvariable extdifftool
+    frame $top.extdifff
+    label $top.extdifff.l -text [mc "External diff tool" ] -font optionfont \
+       -padx 10
+    button $top.extdifff.b -text [mc "Choose..."] -font optionfont \
+       -command choose_extdiff
+    pack $top.extdifff.l $top.extdifff.b -side left
+    grid x $top.extdifff $top.extdifft -sticky w
+
     label $top.cdisp -text [mc "Colors: press to choose"]
     grid $top.cdisp - -sticky w -pady 10
     label $top.bg -padx 40 -relief sunk -background $bgcolor
@@ -8088,6 +9310,15 @@ proc doprefs {} {
     bind $top <Visibility> "focus $top.buts.ok"
 }
 
+proc choose_extdiff {} {
+    global extdifftool
+
+    set prog [tk_getOpenFile -title "External diff tool" -multiple false]
+    if {$prog ne {}} {
+       set extdifftool $prog
+    }
+}
+
 proc choosecolor {v vi w x cmd} {
     global $v
 
@@ -8479,7 +9710,6 @@ if {[catch {package require Tk 8.4} err]} {
 }
 
 # defaults...
-set datemode 0
 set wrcomcmd "git diff-tree --stdin -p --pretty"
 
 set gitencoding {}
@@ -8516,6 +9746,8 @@ set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
 
+set extdifftool "meld"
+
 set colors {green red blue magenta darkgrey brown orange}
 set bgcolor white
 set fgcolor black
@@ -8570,7 +9802,6 @@ if {![file isdirectory $gitdir]} {
     exit 1
 }
 
-set mergeonly 0
 set revtreeargs {}
 set cmdline_files {}
 set i 0
@@ -8578,11 +9809,6 @@ set revtreeargscmd {}
 foreach arg $argv {
     switch -glob -- $arg {
        "" { }
-       "-d" { set datemode 1 }
-       "--merge" {
-           set mergeonly 1
-           lappend revtreeargs $arg
-       }
        "--" {
            set cmdline_files [lrange $argv [expr {$i + 1}] end]
            break
@@ -8598,7 +9824,7 @@ foreach arg $argv {
 }
 
 if {$i >= [llength $argv] && $revtreeargs ne {}} {
-    # no -- on command line, but some arguments (other than -d)
+    # no -- on command line, but some arguments (other than --argscmd)
     if {[catch {
        set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
        set cmdline_files [split $f "\n"]
@@ -8626,42 +9852,9 @@ if {$i >= [llength $argv] && $revtreeargs ne {}} {
     }
 }
 
-if {$mergeonly} {
-    # find the list of unmerged files
-    set mlist {}
-    set nr_unmerged 0
-    if {[catch {
-       set fd [open "| git ls-files -u" r]
-    } err]} {
-       show_error {} . "[mc "Couldn't get list of unmerged files:"] $err"
-       exit 1
-    }
-    while {[gets $fd line] >= 0} {
-       set i [string first "\t" $line]
-       if {$i < 0} continue
-       set fname [string range $line [expr {$i+1}] end]
-       if {[lsearch -exact $mlist $fname] >= 0} continue
-       incr nr_unmerged
-       if {$cmdline_files eq {} || [path_filter $cmdline_files $fname]} {
-           lappend mlist $fname
-       }
-    }
-    catch {close $fd}
-    if {$mlist eq {}} {
-       if {$nr_unmerged == 0} {
-           show_error {} . [mc "No files selected: --merge specified but\
-                            no files are unmerged."]
-       } else {
-           show_error {} . [mc "No files selected: --merge specified but\
-                            no unmerged files are within file limit."]
-       }
-       exit 1
-    }
-    set cmdline_files $mlist
-}
-
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
+set nullfile "/dev/null"
 
 set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
 
@@ -8693,12 +9886,12 @@ set viewperm(0) 0
 set viewargs(0) {}
 set viewargscmd(0) {}
 
+set numcommits 0
+set loginstance 0
 set cmdlineok 0
 set stopped 0
 set stuffsaved 0
 set patchnum 0
-set localirow -1
-set localfrow -1
 set lserial 0
 set isworktree [expr {[exec git rev-parse --is-inside-work-tree] == "true"}]
 setcoords
@@ -8718,6 +9911,7 @@ if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
     set viewargs(1) $revtreeargs
     set viewargscmd(1) $revtreeargscmd
     set viewperm(1) 0
+    set vdatemode(1) 0
     addviewmenu 1
     .bar.view entryconf [mc "Edit view..."] -state normal
     .bar.view entryconf [mc "Delete view"] -state normal
diff --git a/gitk-git/gitk-git/po/es.po b/gitk-git/gitk-git/po/es.po
new file mode 100644 (file)
index 0000000..2cb1486
--- /dev/null
@@ -0,0 +1,890 @@
+# Translation of gitk
+# Copyright (C) 2005-2008 Santiago Gala
+# This file is distributed under the same license as the gitk package.
+# Santiago Gala <santiago.gala@gmail.com>, 2008.
+#
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: gitk\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"PO-Revision-Date: 2008-03-25 11:20+0100\n"
+"Last-Translator: Santiago Gala <santiago.gala@gmail.com>\n"
+"Language-Team: Spanish\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: gitk:111
+msgid "Error executing git rev-list:"
+msgstr "Error al ejecutar git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Leyendo"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Leyendo revisiones..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Error analizando la salida de git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "No se seleccionaron revisiones"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Falta información sobre las revisiones"
+
+#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+msgid "OK"
+msgstr "Aceptar"
+
+#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
+#: gitk:7924 gitk:8083
+msgid "Cancel"
+msgstr "Cancelar"
+
+#: gitk:661
+msgid "File"
+msgstr "Archivo"
+
+#: gitk:663
+msgid "Update"
+msgstr "Actualizar"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Releer referencias"
+
+#: gitk:665
+msgid "List references"
+msgstr "Lista de referencias"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Salir"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Editar"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Preferencias"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Vista"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Nueva vista..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Modificar vista..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Eliminar vista"
+
+#: gitk:678
+msgid "All files"
+msgstr "Todos los archivos"
+
+#: gitk:682
+msgid "Help"
+msgstr "Ayuda"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Acerca de gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Combinaciones de teclas"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1 ID: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Buscar"
+
+#: gitk:792
+msgid "next"
+msgstr "<<"
+
+#: gitk:793
+msgid "prev"
+msgstr ">>"
+
+#: gitk:794
+msgid "commit"
+msgstr "revisión"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "que contiene:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "que modifica la ruta:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "que añade/elimina cadena:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Exacto"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr "NoMayús"
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr "Regex"
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Todos los campos"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Título"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Comentarios"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Autor"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr ""
+
+#: gitk:845
+msgid "Search"
+msgstr "Buscar"
+
+#: gitk:852
+msgid "Diff"
+msgstr "Diferencia"
+
+#: gitk:854
+msgid "Old version"
+msgstr "Versión antigua"
+
+#: gitk:856
+msgid "New version"
+msgstr "Versión nueva"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Líneas de contexto"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignora cambios de espaciado"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Parche"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Árbol"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diferencia de esta -> seleccionada"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diferencia de seleccionada -> esta"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Crear patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Crear etiqueta"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Escribir revisiones a archivo"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Crear nueva rama"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Añadir esta revisión a la rama actual (cherry-pick)"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Traer la rama HEAD aquí"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Cambiar a esta rama"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Eliminar esta rama"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Seleccionar también"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Seleccionar sólo"
+
+#: gitk:1318
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - un visualizador de revisiones para git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Uso y redistribución permitidos según los términos de la Licencia Pública General de "
+"GNU (GNU GPL)"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Cerrar"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Combinaciones de tecla de Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Combinaciones de tecla de Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tSalir"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tIr a la primera revisión"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tIr a la última revisión"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Up>, p, i\tSubir una revisión"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Down>, n, k\tBajar una revisión"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Left>, z, j\tRetroceder en la historia"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\tAvanzar en la historia"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tSubir una página en la lista de revisiones"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tBajar una página en la lista de revisiones"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tDesplazarse al inicio de la lista de revisiones"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tDesplazarse al final de la lista de revisiones"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\tDesplazar una línea hacia arriba la lista de revisiones"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\tDesplazar una línea hacia abajo la lista de revisiones"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tDesplazar una página hacia arriba la lista de revisiones"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tDesplazar una página hacia abajo la lista de revisiones"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\tBuscar hacia atrás (arriba, revisiones siguientes)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\tBuscar hacia adelante (abajo, revisiones anteriores)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tDesplaza hacia arriba una página la vista de diferencias"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\tDesplaza hacia arriba una página la vista de diferencias"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\tDesplaza hacia abajo una página la vista de diferencias"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tDesplaza hacia arriba 18 líneas la vista de diferencias"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tDesplaza hacia abajo 18 líneas la vista de diferencias"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tBuscar"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tBuscar el siguiente"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\tBuscar el siguiente"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tBuscar el siguiente, o reiniciar la búsqueda"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tBuscar el anterior"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tBuscar siguiente en la vista de diferencias"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tBuscar anterior en la vista de diferencias"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\tAumentar tamaño del texto"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tAumentar tamaño del texto"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\tDisminuir tamaño del texto"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tDisminuir tamaño del texto"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tActualizar"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Definición de vistas de Gitk"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Nombre"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Recordar esta vista"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Revisiones a incluir (argumentos a git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Comando que genera más revisiones a incluir:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Introducir archivos y directorios a incluir, uno por línea:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Error en los argumentos de selección de las revisiones:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Ninguno"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Fecha"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr "Fecha de creación"
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Descendiente"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "No descendiente"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Antepasado"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "No antepasado"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Cambios locales añadidos al índice pero sin completar revisión"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Cambios locales sin añadir al índice"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Buscando"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Etiquetas:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Padre"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Hija"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Rama"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Sigue-a"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Precede-a"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Error al leer las diferencias de fusión:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Ir a:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "La id SHA1 abreviada %s es ambigua"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "La id SHA1 %s es desconocida"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "La etiqueta/rama %s es deconocida"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Hijas"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Poner la rama %s en esta revisión"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Origen"
+
+#: gitk:6050
+msgid "From"
+msgstr "De"
+
+#: gitk:6055
+msgid "To"
+msgstr "A"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Generar parche"
+
+#: gitk:6080
+msgid "From:"
+msgstr "De:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "Para:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Invertir"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Escribir a archivo:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Generar"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Error en la creación del parche:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Nombre de etiqueta:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Crear"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "No se ha especificado etiqueta"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "La etiqueta \"%s\" ya existe"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Error al crear la etiqueta:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Comando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Escribir"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Error al escribir revisión:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Nombre:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Especifique un nombre para la nueva rama"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "La revisión %s ya está incluida en la rama %s -- ¿Volver a aplicarla?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr "Eligiendo revisiones (cherry-picking)"
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "No se han guardado cambios"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Confirmar git reset"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "¿Reponer la rama %s a %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Tipo de reposición:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Suave: No altera la copia de trabajo ni el índice"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Mixta: Actualiza el índice, no altera la copia de trabajo"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Dura: Actualiza el índice y la copia de trabajo\n"
+"(abandona TODAS las modificaciones locales)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "Reponiendo"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Creando copia de trabajo"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "No se puede borrar la rama actual"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Las revisiones de la rama %s no están presentes en otras ramas.\n"
+"¿Borrar la rama %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Etiquetas y ramas: %s"
+
+#: gitk:6574
+msgid "Filter"
+msgstr "Filtro"
+
+#: gitk:6868
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Error al leer la topología de revisiones: la información sobre "
+"las ramas y etiquetas precedentes y siguientes será incompleta."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Etiqueta"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Selector de tipografías gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "B"
+
+#: gitk:7912
+msgid "I"
+msgstr "I"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Preferencias de gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Opciones de visualización de la lista de revisiones"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Ancho máximo del gráfico (en líneas)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Ancho máximo del gráfico (en % del panel)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Mostrar cambios locales"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Seleccionar automáticamente SHA1 hash"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Opciones de visualización de diferencias"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Espaciado de tabulador"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Mostrar etiquetas cercanas"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Limitar las diferencias a las rutas seleccionadas"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Colores: pulse para seleccionar"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Fondo"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Primer plano"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: líneas viejas"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: líneas nuevas"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: cabecera de fragmento"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Color de fondo de la selección"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Tipografías: pulse para elegir"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Tipografía principal"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Tipografía para diferencias"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Tipografía para interfaz de usuario"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: elegir color para %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Esta versión de Tcl/Tk es demasiado antigua.\n"
+" Gitk requiere Tcl/Tk versión 8.4 o superior."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "No hay un repositorio git aquí."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "No hay directorio git \"%s\"."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Argumentos incorrectos a Gitk:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Imposible obtener la lista de archivos pendientes de fusión:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"No hay archivos seleccionados: se seleccionó la opción --merge pero no hay "
+"archivos pendientes de fusión."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"No hay archivos seleccionados: se seleccionó la opción --merge pero los archivos "
+"especificados no necesitan fusión."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Línea de comandos"
index 5ee2fca8b264d2b64310cd15bb6c2b36ea215b78..b9867bf8e05d06f7effb9919f00879f77690e185 100644 (file)
@@ -7,249 +7,253 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-01-09 22:20+0100\n"
-"PO-Revision-Date: 2008-01-09 22:21+0100\n"
+"POT-Creation-Date: 2008-05-01 11:54+0200\n"
+"PO-Revision-Date: 2008-05-02 21:12+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:101
+#: gitk:111
 msgid "Error executing git rev-list:"
 msgstr "Fehler beim Ausführen von git-rev-list:"
 
-#: gitk:114
+#: gitk:124
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:141 gitk:2143
+#: gitk:151 gitk:2191
 msgid "Reading commits..."
 msgstr "Versionen lesen..."
 
-#: gitk:264
+#: gitk:275
 msgid "Can't parse git log output:"
-msgstr "Git log Ausgabe kann nicht erkannt werden:"
+msgstr "Ausgabe von git-log kann nicht erkannt werden:"
 
-#: gitk:375 gitk:2147
+#: gitk:386 gitk:2195
 msgid "No commits selected"
 msgstr "Keine Versionen ausgewählt."
 
-#: gitk:486
+#: gitk:500
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:585 gitk:607 gitk:1908 gitk:6366 gitk:7866 gitk:8020
+#: gitk:599 gitk:621 gitk:1955 gitk:6424 gitk:7924 gitk:8083
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:609 gitk:1909 gitk:6046 gitk:6117 gitk:6218 gitk:6264 gitk:6368
-#: gitk:7867 gitk:8021
+#: gitk:623 gitk:1956 gitk:6108 gitk:6179 gitk:6276 gitk:6322 gitk:6426
+#: gitk:7925 gitk:8084
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:646
+#: gitk:661
 msgid "File"
 msgstr "Datei"
 
-#: gitk:648
+#: gitk:663
 msgid "Update"
 msgstr "Aktualisieren"
 
-#: gitk:649
+#: gitk:664
 msgid "Reread references"
 msgstr "Zweige neu laden"
 
-#: gitk:650
+#: gitk:665
 msgid "List references"
-msgstr "Zweige auflisten"
+msgstr "Zweige/Markierungen auflisten"
 
-#: gitk:651
+#: gitk:666
 msgid "Quit"
 msgstr "Beenden"
 
-#: gitk:653
+#: gitk:668
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: gitk:654
+#: gitk:669
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: gitk:657
+#: gitk:672 gitk:1892
 msgid "View"
 msgstr "Ansicht"
 
-#: gitk:658
+#: gitk:673
 msgid "New view..."
 msgstr "Neue Ansicht..."
 
-#: gitk:659 gitk:2085 gitk:8651
+#: gitk:674 gitk:2133 gitk:8723
 msgid "Edit view..."
 msgstr "Ansicht bearbeiten..."
 
-#: gitk:661 gitk:2086 gitk:8652
+#: gitk:676 gitk:2134 gitk:8724
 msgid "Delete view"
 msgstr "Ansicht löschen"
 
-#: gitk:663
+#: gitk:678
 msgid "All files"
 msgstr "Alle Dateien"
 
-#: gitk:667
+#: gitk:682
 msgid "Help"
 msgstr "Hilfe"
 
-#: gitk:668 gitk:1280
+#: gitk:683 gitk:1317
 msgid "About gitk"
 msgstr "Über gitk"
 
-#: gitk:669
+#: gitk:684
 msgid "Key bindings"
 msgstr "Tastenkürzel"
 
-#: gitk:726
+#: gitk:741
 msgid "SHA1 ID: "
 msgstr "SHA1:"
 
-#: gitk:776
+#: gitk:791
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:777
+#: gitk:792
 msgid "next"
 msgstr "nächste"
 
-#: gitk:778
+#: gitk:793
 msgid "prev"
 msgstr "vorige"
 
-#: gitk:779
+#: gitk:794
 msgid "commit"
 msgstr "Version"
 
-#: gitk:782 gitk:784 gitk:2308 gitk:2331 gitk:2355 gitk:4257 gitk:4320
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
 msgid "containing:"
 msgstr "enthaltend:"
 
-#: gitk:785 gitk:1741 gitk:1746 gitk:2383
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
 msgid "touching paths:"
 msgstr "Pfad betreffend:"
 
-#: gitk:786 gitk:2388
+#: gitk:801 gitk:2436
 msgid "adding/removing string:"
-msgstr "String dazu/löschen:"
+msgstr "Zeichenkette ändernd:"
 
-#: gitk:795 gitk:797
+#: gitk:810 gitk:812
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:797 gitk:2466 gitk:4225
+#: gitk:812 gitk:2514 gitk:4274
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:797 gitk:2357 gitk:2464 gitk:4221
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:799 gitk:800 gitk:2485 gitk:2515 gitk:2522 gitk:4331 gitk:4387
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:800 gitk:2483 gitk:2515 gitk:4287
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:801 gitk:2483 gitk:4287 gitk:4387 gitk:4775
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:801 gitk:2483 gitk:2487 gitk:2522 gitk:4287 gitk:4711 gitk:5895
-#: gitk:5910
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5957
+#: gitk:5972
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:801 gitk:2483 gitk:4287 gitk:4713
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:829
+#: gitk:845
 msgid "Search"
 msgstr "Suche"
 
-#: gitk:836
+#: gitk:852
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:838
+#: gitk:854
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:840
+#: gitk:856
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:842
+#: gitk:858
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:900
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Leerzeichenänderungen ignorieren"
+
+#: gitk:926
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:902
+#: gitk:928
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:1018 gitk:1033 gitk:5961
+#: gitk:1053 gitk:1068 gitk:6023
 msgid "Diff this -> selected"
 msgstr "Vergleich diese -> gewählte"
 
-#: gitk:1020 gitk:1035 gitk:5962
+#: gitk:1055 gitk:1070 gitk:6024
 msgid "Diff selected -> this"
 msgstr "Vergleich gewählte -> diese"
 
-#: gitk:1022 gitk:1037 gitk:5963
+#: gitk:1057 gitk:1072 gitk:6025
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:1023 gitk:6101
+#: gitk:1058 gitk:6163
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:1024 gitk:6198
+#: gitk:1059 gitk:6256
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:1025 gitk:6252
+#: gitk:1060 gitk:6310
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:1026
+#: gitk:1061
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:1028
+#: gitk:1063
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:1044
+#: gitk:1079
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:1046
+#: gitk:1081
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:1052
+#: gitk:1087
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:1054
+#: gitk:1089
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:1281
+#: gitk:1318
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -267,262 +271,425 @@ msgstr ""
 "License\n"
 "        "
 
-#: gitk:1289 gitk:1350 gitk:6524
+#: gitk:1326 gitk:1387 gitk:6582
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:1308
+#: gitk:1345
 msgid "Gitk key bindings"
 msgstr "Gitk Tastaturbelegung"
 
-#: gitk:1858
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Gitk Tastaturbelegung:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tBeenden"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Pos1>\t\tZur neuesten Version springen"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<Ende>\t\tZur ältesten Version springen"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Hoch>, p, i\tNächste neuere Version"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Runter>, n, k\tNächste ältere Version"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Links>, z, j\tEine Version zurückgehen"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Rechts>, x, l\tEine Version weitergehen"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<BildHoch>\tEine Seite nach oben blättern"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<BildRunter>\tEine Seite nach unten blättern"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-BildHoch>\tVersionsliste eine Seite hoch blättern"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tVergleich um 18 Zeilen nach oben (»up«) blättern"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tVergleich um 18 Zeilen nach unten (»down«) blättern"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tSuchen"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tWeitersuchen"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Eingabetaste>\tWeitersuchen"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tWeitersuchen oder neue Suche beginnen"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tRückwärts weitersuchen"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tVergleich zur nächsten Datei (»file«) blättern"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tRückwärts weitersuchen im Vergleich"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-Nummerblock-Plus>\tSchriftgröße vergrößern"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-Plus>\tSchriftgröße vergrößern"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-Nummernblock-> Schriftgröße verkleinern"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-Minus>\tSchriftgröße verkleinern"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tAktualisieren"
+
+#: gitk:1896
 msgid "Gitk view definition"
 msgstr "Gitk Ansichten"
 
-#: gitk:1882
+#: gitk:1921
 msgid "Name"
 msgstr "Name"
 
-#: gitk:1885
+#: gitk:1924
 msgid "Remember this view"
 msgstr "Diese Ansicht speichern"
 
-#: gitk:1889
+#: gitk:1928
 msgid "Commits to include (arguments to git rev-list):"
 msgstr "Versionen anzeigen (Argumente von git-rev-list):"
 
-#: gitk:1895
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
+
+#: gitk:1942
 msgid "Enter files and directories to include, one per line:"
 msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
 
-#: gitk:1942
+#: gitk:1989
 msgid "Error in commit selection arguments:"
 msgstr "Fehler in den ausgewählten Versionen:"
 
-#: gitk:1993 gitk:2079 gitk:2535 gitk:2549 gitk:3732 gitk:8620 gitk:8621
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8689 gitk:8690
 msgid "None"
 msgstr "Keine"
 
-#: gitk:2483 gitk:4287 gitk:5897 gitk:5912
+#: gitk:2531 gitk:4336 gitk:5959 gitk:5974
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:2483 gitk:4287
+#: gitk:2531 gitk:4336
 msgid "CDate"
 msgstr "Eintragedatum"
 
-#: gitk:2632 gitk:2637
+#: gitk:2680 gitk:2685
 msgid "Descendant"
 msgstr "Abkömmling"
 
-#: gitk:2633
+#: gitk:2681
 msgid "Not descendant"
 msgstr "Nicht Abkömmling"
 
-#: gitk:2640 gitk:2645
+#: gitk:2688 gitk:2693
 msgid "Ancestor"
 msgstr "Vorgänger"
 
-#: gitk:2641
+#: gitk:2689
 msgid "Not ancestor"
 msgstr "Nicht Vorgänger"
 
-#: gitk:2875
+#: gitk:2924
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
 
-#: gitk:2905
+#: gitk:2954
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokale Änderungen, nicht bereitgestellt"
 
-#: gitk:4256
+#: gitk:4305
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:4715
+#: gitk:4767
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:4732 gitk:4738 gitk:5890
+#: gitk:4784 gitk:4790 gitk:5952
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:4743
+#: gitk:4795
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:4752
+#: gitk:4804
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:4755
+#: gitk:4807
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:4758
+#: gitk:4810
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:5040
+#: gitk:5094
 msgid "Error getting merge diffs:"
 msgstr "Fehler beim Laden des Vergleichs:"
 
-#: gitk:5717
+#: gitk:5779
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:5719
+#: gitk:5781
 msgid "SHA1 ID:"
-msgstr "SHA1 Kennung:"
+msgstr "SHA1-Hashwert:"
 
-#: gitk:5744
+#: gitk:5806
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
-msgstr "Kurze SHA1-Kennung »%s« ist mehrdeutig"
+msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:5756
+#: gitk:5818
 #, tcl-format
 msgid "SHA1 id %s is not known"
-msgstr "SHA1-Kennung »%s« unbekannt"
+msgstr "SHA1-Hashwert »%s« unbekannt"
 
-#: gitk:5758
+#: gitk:5820
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Markierung/Zweig »%s« ist unbekannt"
 
-#: gitk:5900
+#: gitk:5962
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:5957
+#: gitk:6019
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:5988
+#: gitk:6050
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:5989
+#: gitk:6051
 msgid "From"
 msgstr "Von"
 
-#: gitk:5994
+#: gitk:6056
 msgid "To"
 msgstr "bis"
 
-#: gitk:6017
+#: gitk:6079
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:6019
+#: gitk:6081
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:6028
+#: gitk:6090
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:6037
+#: gitk:6099
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:6039 gitk:6212
+#: gitk:6101 gitk:6270
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:6045
+#: gitk:6107
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:6081
+#: gitk:6143
 msgid "Error creating patch:"
 msgstr "Fehler beim Patch erzeugen:"
 
-#: gitk:6103 gitk:6200 gitk:6254
+#: gitk:6165 gitk:6258 gitk:6312
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6112
+#: gitk:6174
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:6116 gitk:6263
+#: gitk:6178 gitk:6321
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:6131
+#: gitk:6193
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:6135
+#: gitk:6197
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:6145
+#: gitk:6203
 msgid "Error creating tag:"
 msgstr "Fehler bei Markierung erstellen:"
 
-#: gitk:6209
+#: gitk:6267
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:6217
+#: gitk:6275
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:6233
+#: gitk:6291
 msgid "Error writing commit:"
-msgstr "Fehler beim Version eintragen:"
+msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:6259
+#: gitk:6317
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:6278
+#: gitk:6336
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:6307
+#: gitk:6365
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr ""
 "Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut "
 "eintragen?"
 
-#: gitk:6312
+#: gitk:6370
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:6324
+#: gitk:6382
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:6347
+#: gitk:6405
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:6349
+#: gitk:6407
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:6353
+#: gitk:6411
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:6357
+#: gitk:6415
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:6360
+#: gitk:6418
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:6363
+#: gitk:6421
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -530,21 +697,21 @@ msgstr ""
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:6379
+#: gitk:6437
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:6436
+#: gitk:6494
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:6466
+#: gitk:6524
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:6472
+#: gitk:6530
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -553,16 +720,16 @@ msgstr ""
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:6503
+#: gitk:6561
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:6517
+#: gitk:6575
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:6811
+#: gitk:6869
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -570,113 +737,117 @@ msgstr ""
 "Fehler beim Lesen der Strukturinformationen; Zweige und Vorgänger/Nachfolger "
 "Informationen werden unvollständig sein."
 
-#: gitk:7795
+#: gitk:7853
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:7795
+#: gitk:7853
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7835
+#: gitk:7893
 msgid "Gitk font chooser"
 msgstr "Gitk Schriften wählen"
 
-#: gitk:7852
+#: gitk:7910
 msgid "B"
 msgstr "F"
 
-#: gitk:7855
+#: gitk:7913
 msgid "I"
 msgstr "K"
 
-#: gitk:7948
+#: gitk:8006
 msgid "Gitk preferences"
 msgstr "Gitk Einstellungen"
 
-#: gitk:7949
+#: gitk:8007
 msgid "Commit list display options"
 msgstr "Anzeige Versionsliste"
 
-#: gitk:7952
+#: gitk:8010
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:7956
+#: gitk:8014
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:7961
+#: gitk:8019
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:7966
+#: gitk:8024
+msgid "Auto-select SHA1"
+msgstr "SHA1-Hashwert automatisch markieren"
+
+#: gitk:8029
 msgid "Diff display options"
 msgstr "Anzeige Vergleich"
 
-#: gitk:7968
+#: gitk:8031
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:7972
+#: gitk:8035
 msgid "Display nearby tags"
 msgstr "Naheliegende Überschriften anzeigen"
 
-#: gitk:7977
+#: gitk:8040
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:7982
+#: gitk:8045
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:7985
+#: gitk:8048
 msgid "Background"
-msgstr "Vordergrund"
+msgstr "Hintergrund"
 
-#: gitk:7989
+#: gitk:8052
 msgid "Foreground"
-msgstr "Hintergrund"
+msgstr "Vordergrund"
 
-#: gitk:7993
+#: gitk:8056
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:7998
+#: gitk:8061
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:8003
+#: gitk:8066
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:8009
+#: gitk:8072
 msgid "Select bg"
 msgstr "Hintergrundfarbe Auswählen"
 
-#: gitk:8013
+#: gitk:8076
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:8015
+#: gitk:8078
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:8016
+#: gitk:8079
 msgid "Diff display font"
 msgstr "Vergleich"
 
-#: gitk:8017
+#: gitk:8080
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:8033
+#: gitk:8096
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:8414
+#: gitk:8477
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -684,35 +855,33 @@ msgstr ""
 "Gitk läuft nicht mit dieser Version von Tcl/Tk.\n"
 "Gitk benötigt mindestens Tcl/Tk 8.4."
 
-#: gitk:8501
+#: gitk:8566
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:8505
+#: gitk:8570
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Git-Verzeichnis »%s« wurde nicht gefunden."
 
-#: gitk:8544
+#: gitk:8613
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:8556
+#: gitk:8625
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-#: gitk:8568
+#: gitk:8637
 msgid "Couldn't get list of unmerged files:"
 msgstr "Liste der nicht-zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:8584
+#: gitk:8653
 msgid "No files selected: --merge specified but no files are unmerged."
-msgstr ""
-"Keine Dateien ausgewähle: --merge angegeben, es existieren aber keine nicht-"
-"zusammengeführten Dateien."
+msgstr "Keine Dateien ausgewählt: --merge angegeben, es existieren aber keine nicht-zusammengeführten Dateien."
 
-#: gitk:8587
+#: gitk:8656
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -720,6 +889,6 @@ msgstr ""
 "Keine Dateien ausgewähle: --merge angegeben, aber keine nicht-"
 "zusammengeführten Dateien sind in der Dateiauswahl."
 
-#: gitk:8646
+#: gitk:8717
 msgid "Command line"
 msgstr "Kommandozeile"
diff --git a/gitk-git/po/sv.po b/gitk-git/po/sv.po
new file mode 100644 (file)
index 0000000..f6b080d
--- /dev/null
@@ -0,0 +1,887 @@
+# Swedish translation for gitk
+# Copyright (C) 2005-2008 Paul Mackerras
+# This file is distributed under the same license as the gitk package.
+#
+# Peter Karlsson <peter@softwolves.pp.se>, 2008.
+msgid ""
+msgstr ""
+"Project-Id-Version: sv\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-03-14 15:03+0100\n"
+"PO-Revision-Date: 2008-03-14 16:06CET-1\n"
+"Last-Translator: Peter Karlsson <peter@softwolves.pp.se>\n"
+"Language-Team: Swedish <sv@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit"
+
+#: gitk:111
+msgid "Error executing git rev-list:"
+msgstr "Fel vid körning av git rev-list:"
+
+#: gitk:124
+msgid "Reading"
+msgstr "Läser"
+
+#: gitk:151 gitk:2191
+msgid "Reading commits..."
+msgstr "Läser incheckningar..."
+
+#: gitk:275
+msgid "Can't parse git log output:"
+msgstr "Kan inte tolka utdata från git log:"
+
+#: gitk:386 gitk:2195
+msgid "No commits selected"
+msgstr "Inga incheckningar markerade"
+
+#: gitk:500
+msgid "No commit information available"
+msgstr "Ingen incheckningsinformation är tillgänglig"
+
+#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+msgid "OK"
+msgstr "OK"
+
+#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
+#: gitk:7924 gitk:8083
+msgid "Cancel"
+msgstr "Avbryt"
+
+#: gitk:661
+msgid "File"
+msgstr "Arkiv"
+
+#: gitk:663
+msgid "Update"
+msgstr "Uppdatera"
+
+#: gitk:664
+msgid "Reread references"
+msgstr "Läs om referenser"
+
+#: gitk:665
+msgid "List references"
+msgstr "Visa referenser"
+
+#: gitk:666
+msgid "Quit"
+msgstr "Avsluta"
+
+#: gitk:668
+msgid "Edit"
+msgstr "Redigera"
+
+#: gitk:669
+msgid "Preferences"
+msgstr "Inställningar"
+
+#: gitk:672 gitk:1892
+msgid "View"
+msgstr "Visa"
+
+#: gitk:673
+msgid "New view..."
+msgstr "Ny vy..."
+
+#: gitk:674 gitk:2133 gitk:8722
+msgid "Edit view..."
+msgstr "Ändra vy..."
+
+#: gitk:676 gitk:2134 gitk:8723
+msgid "Delete view"
+msgstr "Ta bort vy"
+
+#: gitk:678
+msgid "All files"
+msgstr "Alla filer"
+
+#: gitk:682
+msgid "Help"
+msgstr "Hjälp"
+
+#: gitk:683 gitk:1317
+msgid "About gitk"
+msgstr "Om gitk"
+
+#: gitk:684
+msgid "Key bindings"
+msgstr "Tangentbordsbindningar"
+
+#: gitk:741
+msgid "SHA1 ID: "
+msgstr "SHA1-id: "
+
+#: gitk:791
+msgid "Find"
+msgstr "Sök"
+
+#: gitk:792
+msgid "next"
+msgstr "nästa"
+
+#: gitk:793
+msgid "prev"
+msgstr "föreg"
+
+#: gitk:794
+msgid "commit"
+msgstr "incheckning"
+
+#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+msgid "containing:"
+msgstr "som innehåller:"
+
+#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+msgid "touching paths:"
+msgstr "som rör sökväg:"
+
+#: gitk:801 gitk:2436
+msgid "adding/removing string:"
+msgstr "som lägger/till tar bort sträng:"
+
+#: gitk:810 gitk:812
+msgid "Exact"
+msgstr "Exakt"
+
+#: gitk:812 gitk:2514 gitk:4274
+msgid "IgnCase"
+msgstr "IgnVersaler"
+
+#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+msgid "Regexp"
+msgstr "Reg.uttr."
+
+#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+msgid "All fields"
+msgstr "Alla fält"
+
+#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+msgid "Headline"
+msgstr "Rubrik"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+msgid "Comments"
+msgstr "Kommentarer"
+
+#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
+#: gitk:5971
+msgid "Author"
+msgstr "Författare"
+
+#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+msgid "Committer"
+msgstr "Incheckare"
+
+#: gitk:845
+msgid "Search"
+msgstr "Sök"
+
+#: gitk:852
+msgid "Diff"
+msgstr "Diff"
+
+#: gitk:854
+msgid "Old version"
+msgstr "Gammal version"
+
+#: gitk:856
+msgid "New version"
+msgstr "Ny version"
+
+#: gitk:858
+msgid "Lines of context"
+msgstr "Rader sammanhang"
+
+#: gitk:868
+msgid "Ignore space change"
+msgstr "Ignorera ändringar i blanksteg"
+
+#: gitk:926
+msgid "Patch"
+msgstr "Patch"
+
+#: gitk:928
+msgid "Tree"
+msgstr "Träd"
+
+#: gitk:1053 gitk:1068 gitk:6022
+msgid "Diff this -> selected"
+msgstr "Diff denna -> markerad"
+
+#: gitk:1055 gitk:1070 gitk:6023
+msgid "Diff selected -> this"
+msgstr "Diff markerad -> denna"
+
+#: gitk:1057 gitk:1072 gitk:6024
+msgid "Make patch"
+msgstr "Skapa patch"
+
+#: gitk:1058 gitk:6162
+msgid "Create tag"
+msgstr "Skapa tagg"
+
+#: gitk:1059 gitk:6255
+msgid "Write commit to file"
+msgstr "Skriv incheckning till fil"
+
+#: gitk:1060 gitk:6309
+msgid "Create new branch"
+msgstr "Skapa ny gren"
+
+#: gitk:1061
+msgid "Cherry-pick this commit"
+msgstr "Plocka denna incheckning"
+
+#: gitk:1063
+msgid "Reset HEAD branch to here"
+msgstr "Återställ HEAD-grenen hit"
+
+#: gitk:1079
+msgid "Check out this branch"
+msgstr "Checka ut denna gren"
+
+#: gitk:1081
+msgid "Remove this branch"
+msgstr "Ta bort denna gren"
+
+#: gitk:1087
+msgid "Highlight this too"
+msgstr "Markera även detta"
+
+#: gitk:1089
+msgid "Highlight this only"
+msgstr "Markera bara detta"
+
+#: gitk:1318
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr ""
+"\n"
+"Gitk - en incheckningsvisare för git\n"
+"\n"
+"Copyright © 2005-2006 Paul Mackerras\n"
+"\n"
+"Använd och vidareförmedla enligt villkoren i GNU General Public License"
+
+#: gitk:1326 gitk:1387 gitk:6581
+msgid "Close"
+msgstr "Stäng"
+
+#: gitk:1345
+msgid "Gitk key bindings"
+msgstr "Tangentbordsbindningar för Gitk"
+
+#: gitk:1347
+msgid "Gitk key bindings:"
+msgstr "Tangentbordsbindningar för Gitk:"
+
+#: gitk:1349
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\tAvsluta"
+
+#: gitk:1350
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\tGå till första incheckning"
+
+#: gitk:1351
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\tGå till sista incheckning"
+
+#: gitk:1352
+msgid "<Up>, p, i\tMove up one commit"
+msgstr "<Upp>, p, i\tGå en incheckning upp"
+
+#: gitk:1353
+msgid "<Down>, n, k\tMove down one commit"
+msgstr "<Ned>, n, k\tGå en incheckning ned"
+
+#: gitk:1354
+msgid "<Left>, z, j\tGo back in history list"
+msgstr "<Vänster>, z, j\tGå bakåt i historiken"
+
+#: gitk:1355
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Höger>, x, l\tGå framåt i historiken"
+
+#: gitk:1356
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
+
+#: gitk:1357
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
+
+#: gitk:1358
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\tRulla till början av incheckningslistan"
+
+#: gitk:1359
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
+
+#: gitk:1360
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
+
+#: gitk:1361
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
+
+#: gitk:1362
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
+
+#: gitk:1363
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
+
+#: gitk:1364
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
+
+#: gitk:1365
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
+
+#: gitk:1366
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
+
+#: gitk:1367
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
+
+#: gitk:1368
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
+
+#: gitk:1369
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\tRulla diffvisningen upp 18 rader"
+
+#: gitk:1370
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\tRulla diffvisningen ned 18 rader"
+
+#: gitk:1371
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\tSök"
+
+#: gitk:1372
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\tGå till nästa sökträff"
+
+#: gitk:1373
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\t\tGå till nästa sökträff"
+
+#: gitk:1374
+msgid "/\t\tMove to next find hit, or redo find"
+msgstr "/\t\tGå till nästa sökträff, eller sök på nytt"
+
+#: gitk:1375
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\tGå till föregående sökträff"
+
+#: gitk:1376
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\tRulla diffvisningen till nästa fil"
+
+#: gitk:1377
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\tGå till nästa sökträff i diffvisningen"
+
+#: gitk:1378
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\tGå till föregående sökträff i diffvisningen"
+
+#: gitk:1379
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-Num+>\tÖka teckenstorlek"
+
+#: gitk:1380
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\tÖka teckenstorlek"
+
+#: gitk:1381
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-Num->\tMinska teckenstorlek"
+
+#: gitk:1382
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\tMinska teckenstorlek"
+
+#: gitk:1383
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\tUppdatera"
+
+#: gitk:1896
+msgid "Gitk view definition"
+msgstr "Definition av Gitk-vy"
+
+#: gitk:1921
+msgid "Name"
+msgstr "Namn"
+
+#: gitk:1924
+msgid "Remember this view"
+msgstr "Spara denna vy"
+
+#: gitk:1928
+msgid "Commits to include (arguments to git rev-list):"
+msgstr "Incheckningar att ta med (argument till git rev-list):"
+
+#: gitk:1935
+msgid "Command to generate more commits to include:"
+msgstr "Kommando för att generera fler incheckningar att ta med:"
+
+#: gitk:1942
+msgid "Enter files and directories to include, one per line:"
+msgstr "Ange filer och kataloger att ta med, en per rad:"
+
+#: gitk:1989
+msgid "Error in commit selection arguments:"
+msgstr "Fel i argument för val av incheckningar:"
+
+#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+msgid "None"
+msgstr "Inget"
+
+#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+msgid "Date"
+msgstr "Datum"
+
+#: gitk:2531 gitk:4336
+msgid "CDate"
+msgstr "Skapat datum"
+
+#: gitk:2680 gitk:2685
+msgid "Descendant"
+msgstr "Avkomling"
+
+#: gitk:2681
+msgid "Not descendant"
+msgstr "Inte avkomling"
+
+#: gitk:2688 gitk:2693
+msgid "Ancestor"
+msgstr "Förfader"
+
+#: gitk:2689
+msgid "Not ancestor"
+msgstr "Inte förfader"
+
+#: gitk:2924
+msgid "Local changes checked in to index but not committed"
+msgstr "Lokala ändringar sparade i indexet men inte incheckade"
+
+#: gitk:2954
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "Lokala ändringar, ej sparade i indexet"
+
+#: gitk:4305
+msgid "Searching"
+msgstr "Söker"
+
+#: gitk:4767
+msgid "Tags:"
+msgstr "Taggar:"
+
+#: gitk:4784 gitk:4790 gitk:5951
+msgid "Parent"
+msgstr "Förälder"
+
+#: gitk:4795
+msgid "Child"
+msgstr "Barn"
+
+#: gitk:4804
+msgid "Branch"
+msgstr "Gren"
+
+#: gitk:4807
+msgid "Follows"
+msgstr "Följer"
+
+#: gitk:4810
+msgid "Precedes"
+msgstr "Föregår"
+
+#: gitk:5093
+msgid "Error getting merge diffs:"
+msgstr "Fel vid hämtning av sammanslagningsdiff:"
+
+#: gitk:5778
+msgid "Goto:"
+msgstr "Gå till:"
+
+#: gitk:5780
+msgid "SHA1 ID:"
+msgstr "SHA1-id:"
+
+#: gitk:5805
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "Förkortat SHA1-id %s är tvetydigt"
+
+#: gitk:5817
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "SHA-id:t %s är inte känt"
+
+#: gitk:5819
+#, tcl-format
+msgid "Tag/Head %s is not known"
+msgstr "Tagg/huvud %s är okänt"
+
+#: gitk:5961
+msgid "Children"
+msgstr "Barn"
+
+#: gitk:6018
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "Återställ grenen %s hit"
+
+#: gitk:6049
+msgid "Top"
+msgstr "Topp"
+
+#: gitk:6050
+msgid "From"
+msgstr "Från"
+
+#: gitk:6055
+msgid "To"
+msgstr "Till"
+
+#: gitk:6078
+msgid "Generate patch"
+msgstr "Generera patch"
+
+#: gitk:6080
+msgid "From:"
+msgstr "Från:"
+
+#: gitk:6089
+msgid "To:"
+msgstr "Till:"
+
+#: gitk:6098
+msgid "Reverse"
+msgstr "Vänd"
+
+#: gitk:6100 gitk:6269
+msgid "Output file:"
+msgstr "Utdatafil:"
+
+#: gitk:6106
+msgid "Generate"
+msgstr "Generera"
+
+#: gitk:6142
+msgid "Error creating patch:"
+msgstr "Fel vid generering av patch:"
+
+#: gitk:6164 gitk:6257 gitk:6311
+msgid "ID:"
+msgstr "Id:"
+
+#: gitk:6173
+msgid "Tag name:"
+msgstr "Taggnamn:"
+
+#: gitk:6177 gitk:6320
+msgid "Create"
+msgstr "Skapa"
+
+#: gitk:6192
+msgid "No tag name specified"
+msgstr "Inget taggnamn angavs"
+
+#: gitk:6196
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Taggen \"%s\" finns redan"
+
+#: gitk:6202
+msgid "Error creating tag:"
+msgstr "Fel vid skapande av tagg:"
+
+#: gitk:6266
+msgid "Command:"
+msgstr "Kommando:"
+
+#: gitk:6274
+msgid "Write"
+msgstr "Skriv"
+
+#: gitk:6290
+msgid "Error writing commit:"
+msgstr "Fel vid skrivning av incheckning:"
+
+#: gitk:6316
+msgid "Name:"
+msgstr "Namn:"
+
+#: gitk:6335
+msgid "Please specify a name for the new branch"
+msgstr "Ange ett namn för den nya grenen"
+
+#: gitk:6364
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "Incheckningen %s finns redan på grenen %s -- skall den verkligen appliceras på nytt?"
+
+#: gitk:6369
+msgid "Cherry-picking"
+msgstr "Plockar"
+
+#: gitk:6381
+msgid "No changes committed"
+msgstr "Inga ändringar incheckade"
+
+#: gitk:6404
+msgid "Confirm reset"
+msgstr "Bekräfta återställning"
+
+#: gitk:6406
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "Återställa grenen %s till %s?"
+
+#: gitk:6410
+msgid "Reset type:"
+msgstr "Typ av återställning:"
+
+#: gitk:6414
+msgid "Soft: Leave working tree and index untouched"
+msgstr "Mjuk: Rör inte utcheckning och index"
+
+#: gitk:6417
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "Blandad: Rör inte utcheckning, återställ index"
+
+#: gitk:6420
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr ""
+"Hård: Återställ utcheckning och index\n"
+"(förkastar ALLA lokala ändringar)"
+
+#: gitk:6436
+msgid "Resetting"
+msgstr "Återställer"
+
+#: gitk:6493
+msgid "Checking out"
+msgstr "Checkar ut"
+
+#: gitk:6523
+msgid "Cannot delete the currently checked-out branch"
+msgstr "Kan inte ta bort den just nu utcheckade grenen"
+
+#: gitk:6529
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr ""
+"Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
+"Vill du verkligen ta bort grenen %s?"
+
+#: gitk:6560
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Taggar och huvuden: %s"
+
+#: gitk:6574
+msgid "Filter"
+msgstr "Filter"
+
+#: gitk:6868
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr ""
+"Fel vid läsning av information om incheckningstopologi; information om "
+"grenar och föregående/senare taggar kommer inte vara komplett."
+
+#: gitk:7852
+msgid "Tag"
+msgstr "Tagg"
+
+#: gitk:7852
+msgid "Id"
+msgstr "Id"
+
+#: gitk:7892
+msgid "Gitk font chooser"
+msgstr "Teckensnittsväljare för Gitk"
+
+#: gitk:7909
+msgid "B"
+msgstr "F"
+
+#: gitk:7912
+msgid "I"
+msgstr "K"
+
+#: gitk:8005
+msgid "Gitk preferences"
+msgstr "Inställningar för Gitk"
+
+#: gitk:8006
+msgid "Commit list display options"
+msgstr "Alternativ för incheckningslistvy"
+
+#: gitk:8009
+msgid "Maximum graph width (lines)"
+msgstr "Maximal grafbredd (rader)"
+
+#: gitk:8013
+#, tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "Maximal grafbredd (% av ruta)"
+
+#: gitk:8018
+msgid "Show local changes"
+msgstr "Visa lokala ändringar"
+
+#: gitk:8023
+msgid "Auto-select SHA1"
+msgstr "Välj SHA1 automatiskt"
+
+#: gitk:8028
+msgid "Diff display options"
+msgstr "Alternativ för diffvy"
+
+#: gitk:8030
+msgid "Tab spacing"
+msgstr "Blanksteg för tabulatortecken"
+
+#: gitk:8034
+msgid "Display nearby tags"
+msgstr "Visa närliggande taggar"
+
+#: gitk:8039
+msgid "Limit diffs to listed paths"
+msgstr "Begränsa diff till listade sökvägar"
+
+#: gitk:8044
+msgid "Colors: press to choose"
+msgstr "Färger: tryck för att välja"
+
+#: gitk:8047
+msgid "Background"
+msgstr "Bakgrund"
+
+#: gitk:8051
+msgid "Foreground"
+msgstr "Förgrund"
+
+#: gitk:8055
+msgid "Diff: old lines"
+msgstr "Diff: gamla rader"
+
+#: gitk:8060
+msgid "Diff: new lines"
+msgstr "Diff: nya rader"
+
+#: gitk:8065
+msgid "Diff: hunk header"
+msgstr "Diff: delhuvud"
+
+#: gitk:8071
+msgid "Select bg"
+msgstr "Markerad bakgrund"
+
+#: gitk:8075
+msgid "Fonts: press to choose"
+msgstr "Teckensnitt: tryck för att välja"
+
+#: gitk:8077
+msgid "Main font"
+msgstr "Huvudteckensnitt"
+
+#: gitk:8078
+msgid "Diff display font"
+msgstr "Teckensnitt för diffvisning"
+
+#: gitk:8079
+msgid "User interface font"
+msgstr "Teckensnitt för användargränssnitt"
+
+#: gitk:8095
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk: välj färg för %s"
+
+#: gitk:8476
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr ""
+"Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
+" Gitk kräver åtminstone Tcl/Tk 8.4."
+
+#: gitk:8565
+msgid "Cannot find a git repository here."
+msgstr "Hittar inget gitk-arkiv här."
+
+#: gitk:8569
+#, tcl-format
+msgid "Cannot find the git directory \"%s\"."
+msgstr "Hittar inte git-katalogen \"%s\"."
+
+#: gitk:8612
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
+
+#: gitk:8624
+msgid "Bad arguments to gitk:"
+msgstr "Felaktiga argument till gitk:"
+
+#: gitk:8636
+msgid "Couldn't get list of unmerged files:"
+msgstr "Kunde inta hämta lista över ej sammanslagna filer:"
+
+#: gitk:8652
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr "Inga filer valdes: --merge angavs men det finns inga filer som inte har slagits samman."
+
+#: gitk:8655
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Inga filer valdes: --merge angavs men det finns inga filer inom "
+"filbegränsningen."
+
+#: gitk:8716
+msgid "Command line"
+msgstr "Kommandorad"
+
index 446a1c333bd55bb25db5e53794277e9e6d2c51da..aa0eeca24786dbd5143354fc3bb5e8cdb3ef831f 100644 (file)
@@ -464,6 +464,14 @@ a.rss_logo:hover {
        background-color: #ee5500;
 }
 
+a.rss_logo.generic {
+       background-color: #ff8800;
+}
+
+a.rss_logo.generic:hover {
+       background-color: #ee7700;
+}
+
 span.refs span {
        padding: 0px 4px;
        font-size: 70%;
index f83567ec39e8781cc10d3f3f3cb39c7cb6aa11ef..8308e2208e2a0064724367f3bcb6d33282b9e587 100755 (executable)
@@ -592,7 +592,7 @@ sub evaluate_path_info {
 ## ======================================================================
 ## action links
 
-sub href(%) {
+sub href (%) {
        my %params = @_;
        # default is to use -absolute url() i.e. $my_uri
        my $href = $params{-full} ? $my_url : $my_uri;
@@ -866,6 +866,10 @@ sub chop_str {
        my $add_len = shift || 10;
        my $where = shift || 'right'; # 'left' | 'center' | 'right'
 
+       # Make sure perl knows it is utf8 encoded so we don't
+       # cut in the middle of a utf8 multibyte char.
+       $str = to_utf8($str);
+
        # allow only $len chars, but don't cut a word if it would fit in $add_len
        # if it doesn't fit, cut it if it's still longer than the dots we would add
        # remove chopped character entities entirely
@@ -1448,6 +1452,46 @@ sub format_snapshot_links {
        }
 }
 
+## ......................................................................
+## functions returning values to be passed, perhaps after some
+## transformation, to other functions; e.g. returning arguments to href()
+
+# returns hash to be passed to href to generate gitweb URL
+# in -title key it returns description of link
+sub get_feed_info {
+       my $format = shift || 'Atom';
+       my %res = (action => lc($format));
+
+       # feed links are possible only for project views
+       return unless (defined $project);
+       # some views should link to OPML, or to generic project feed,
+       # or don't have specific feed yet (so they should use generic)
+       return if ($action =~ /^(?:tags|heads|forks|tag|search)$/x);
+
+       my $branch;
+       # branches refs uses 'refs/heads/' prefix (fullname) to differentiate
+       # from tag links; this also makes possible to detect branch links
+       if ((defined $hash_base && $hash_base =~ m!^refs/heads/(.*)$!) ||
+           (defined $hash      && $hash      =~ m!^refs/heads/(.*)$!)) {
+               $branch = $1;
+       }
+       # find log type for feed description (title)
+       my $type = 'log';
+       if (defined $file_name) {
+               $type  = "history of $file_name";
+               $type .= "/" if ($action eq 'tree');
+               $type .= " on '$branch'" if (defined $branch);
+       } else {
+               $type = "log of $branch" if (defined $branch);
+       }
+
+       $res{-title} = $type;
+       $res{'hash'} = (defined $branch ? "refs/heads/$branch" : undef);
+       $res{'file_name'} = $file_name;
+
+       return %res;
+}
+
 ## ----------------------------------------------------------------------
 ## git utility subroutines, invoking git commands
 
@@ -2510,30 +2554,49 @@ sub git_header_html {
                }
        }
        if (defined $project) {
-               printf('<link rel="alternate" title="%s log RSS feed" '.
-                      'href="%s" type="application/rss+xml" />'."\n",
-                      esc_param($project), href(action=>"rss"));
-               printf('<link rel="alternate" title="%s log RSS feed (no merges)" '.
-                      'href="%s" type="application/rss+xml" />'."\n",
-                      esc_param($project), href(action=>"rss",
-                                                extra_options=>"--no-merges"));
-               printf('<link rel="alternate" title="%s log Atom feed" '.
-                      'href="%s" type="application/atom+xml" />'."\n",
-                      esc_param($project), href(action=>"atom"));
-               printf('<link rel="alternate" title="%s log Atom feed (no merges)" '.
-                      'href="%s" type="application/atom+xml" />'."\n",
-                      esc_param($project), href(action=>"atom",
-                                                extra_options=>"--no-merges"));
+               my %href_params = get_feed_info();
+               if (!exists $href_params{'-title'}) {
+                       $href_params{'-title'} = 'log';
+               }
+
+               foreach my $format qw(RSS Atom) {
+                       my $type = lc($format);
+                       my %link_attr = (
+                               '-rel' => 'alternate',
+                               '-title' => "$project - $href_params{'-title'} - $format feed",
+                               '-type' => "application/$type+xml"
+                       );
+
+                       $href_params{'action'} = $type;
+                       $link_attr{'-href'} = href(%href_params);
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+
+                       $href_params{'extra_options'} = '--no-merges';
+                       $link_attr{'-href'} = href(%href_params);
+                       $link_attr{'-title'} .= ' (no merges)';
+                       print "<link ".
+                             "rel=\"$link_attr{'-rel'}\" ".
+                             "title=\"$link_attr{'-title'}\" ".
+                             "href=\"$link_attr{'-href'}\" ".
+                             "type=\"$link_attr{'-type'}\" ".
+                             "/>\n";
+               }
+
        } else {
                printf('<link rel="alternate" title="%s projects list" '.
-                      'href="%s" type="text/plain; charset=utf-8"/>'."\n",
+                      'href="%s" type="text/plain; charset=utf-8" />'."\n",
                       $site_name, href(project=>undef, action=>"project_index"));
                printf('<link rel="alternate" title="%s projects feeds" '.
-                      'href="%s" type="text/x-opml"/>'."\n",
+                      'href="%s" type="text/x-opml" />'."\n",
                       $site_name, href(project=>undef, action=>"opml"));
        }
        if (defined $favicon) {
-               print qq(<link rel="shortcut icon" href="$favicon" type="image/png"/>\n);
+               print qq(<link rel="shortcut icon" href="$favicon" type="image/png" />\n);
        }
 
        print "</head>\n" .
@@ -2601,23 +2664,35 @@ sub git_header_html {
 }
 
 sub git_footer_html {
+       my $feed_class = 'rss_logo';
+
        print "<div class=\"page_footer\">\n";
        if (defined $project) {
                my $descr = git_get_project_description($project);
                if (defined $descr) {
                        print "<div class=\"page_footer_text\">" . esc_html($descr) . "</div>\n";
                }
-               print $cgi->a({-href => href(action=>"rss"),
-                             -class => "rss_logo"}, "RSS") . " ";
-               print $cgi->a({-href => href(action=>"atom"),
-                             -class => "rss_logo"}, "Atom") . "\n";
+
+               my %href_params = get_feed_info();
+               if (!%href_params) {
+                       $feed_class .= ' generic';
+               }
+               $href_params{'-title'} ||= 'log';
+
+               foreach my $format qw(RSS Atom) {
+                       $href_params{'action'} = lc($format);
+                       print $cgi->a({-href => href(%href_params),
+                                     -title => "$href_params{'-title'} $format feed",
+                                     -class => $feed_class}, $format)."\n";
+               }
+
        } else {
                print $cgi->a({-href => href(project=>undef, action=>"opml"),
-                             -class => "rss_logo"}, "OPML") . " ";
+                             -class => $feed_class}, "OPML") . " ";
                print $cgi->a({-href => href(project=>undef, action=>"project_index"),
-                             -class => "rss_logo"}, "TXT") . "\n";
+                             -class => $feed_class}, "TXT") . "\n";
        }
-       print "</div>\n" ;
+       print "</div>\n"; # class="page_footer"
 
        if (-f $site_footer) {
                open (my $fd, $site_footer);
diff --git a/graph.c b/graph.c
new file mode 100644 (file)
index 0000000..9d6ed30
--- /dev/null
+++ b/graph.c
@@ -0,0 +1,945 @@
+#include "cache.h"
+#include "commit.h"
+#include "graph.h"
+#include "diff.h"
+#include "revision.h"
+
+/*
+ * TODO:
+ * - Add colors to the graph.
+ *   Pick a color for each column, and print all characters
+ *   in that column with the specified color.
+ *
+ * - Limit the number of columns, similar to the way gitk does.
+ *   If we reach more than a specified number of columns, omit
+ *   sections of some columns.
+ *
+ * - The output during the GRAPH_PRE_COMMIT and GRAPH_COLLAPSING states
+ *   could be made more compact by printing horizontal lines, instead of
+ *   long diagonal lines.  For example, during collapsing, something like
+ *   this:          instead of this:
+ *   | | | | |      | | | | |
+ *   | |_|_|/       | | | |/
+ *   |/| | |        | | |/|
+ *   | | | |        | |/| |
+ *                  |/| | |
+ *                  | | | |
+ *
+ *   If there are several parallel diagonal lines, they will need to be
+ *   replaced with horizontal lines on subsequent rows.
+ */
+
+struct column {
+       /*
+        * The parent commit of this column.
+        */
+       struct commit *commit;
+       /*
+        * XXX: Once we add support for colors, struct column could also
+        * contain the color of its branch line.
+        */
+};
+
+enum graph_state {
+       GRAPH_PADDING,
+       GRAPH_SKIP,
+       GRAPH_PRE_COMMIT,
+       GRAPH_COMMIT,
+       GRAPH_POST_MERGE,
+       GRAPH_COLLAPSING
+};
+
+struct git_graph {
+       /*
+        * The commit currently being processed
+        */
+       struct commit *commit;
+       /*
+        * The number of parents this commit has.
+        * (Stored so we don't have to walk over them each time we need
+        * this number)
+        */
+       int num_parents;
+       /*
+        * The width of the graph output for this commit.
+        * All rows for this commit are padded to this width, so that
+        * messages printed after the graph output are aligned.
+        */
+       int width;
+       /*
+        * The next expansion row to print
+        * when state is GRAPH_PRE_COMMIT
+        */
+       int expansion_row;
+       /*
+        * The current output state.
+        * This tells us what kind of line graph_next_line() should output.
+        */
+       enum graph_state state;
+       /*
+        * The maximum number of columns that can be stored in the columns
+        * and new_columns arrays.  This is also half the number of entries
+        * that can be stored in the mapping and new_mapping arrays.
+        */
+       int column_capacity;
+       /*
+        * The number of columns (also called "branch lines" in some places)
+        */
+       int num_columns;
+       /*
+        * The number of columns in the new_columns array
+        */
+       int num_new_columns;
+       /*
+        * The number of entries in the mapping array
+        */
+       int mapping_size;
+       /*
+        * The column state before we output the current commit.
+        */
+       struct column *columns;
+       /*
+        * The new column state after we output the current commit.
+        * Only valid when state is GRAPH_COLLAPSING.
+        */
+       struct column *new_columns;
+       /*
+        * An array that tracks the current state of each
+        * character in the output line during state GRAPH_COLLAPSING.
+        * Each entry is -1 if this character is empty, or a non-negative
+        * integer if the character contains a branch line.  The value of
+        * the integer indicates the target position for this branch line.
+        * (I.e., this array maps the current column positions to their
+        * desired positions.)
+        *
+        * The maximum capacity of this array is always
+        * sizeof(int) * 2 * column_capacity.
+        */
+       int *mapping;
+       /*
+        * A temporary array for computing the next mapping state
+        * while we are outputting a mapping line.  This is stored as part
+        * of the git_graph simply so we don't have to allocate a new
+        * temporary array each time we have to output a collapsing line.
+        */
+       int *new_mapping;
+};
+
+struct git_graph *graph_init(void)
+{
+       struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+       graph->commit = NULL;
+       graph->num_parents = 0;
+       graph->expansion_row = 0;
+       graph->state = GRAPH_PADDING;
+       graph->num_columns = 0;
+       graph->num_new_columns = 0;
+       graph->mapping_size = 0;
+
+       /*
+        * Allocate a reasonably large default number of columns
+        * We'll automatically grow columns later if we need more room.
+        */
+       graph->column_capacity = 30;
+       graph->columns = xmalloc(sizeof(struct column) *
+                                graph->column_capacity);
+       graph->new_columns = xmalloc(sizeof(struct column) *
+                                    graph->column_capacity);
+       graph->mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
+       graph->new_mapping = xmalloc(sizeof(int) * 2 * graph->column_capacity);
+
+       return graph;
+}
+
+void graph_release(struct git_graph *graph)
+{
+       free(graph->columns);
+       free(graph->new_columns);
+       free(graph->mapping);
+       free(graph);
+}
+
+static void graph_ensure_capacity(struct git_graph *graph, int num_columns)
+{
+       if (graph->column_capacity >= num_columns)
+               return;
+
+       do {
+               graph->column_capacity *= 2;
+       } while (graph->column_capacity < num_columns);
+
+       graph->columns = xrealloc(graph->columns,
+                                 sizeof(struct column) *
+                                 graph->column_capacity);
+       graph->new_columns = xrealloc(graph->new_columns,
+                                     sizeof(struct column) *
+                                     graph->column_capacity);
+       graph->mapping = xrealloc(graph->mapping,
+                                 sizeof(int) * 2 * graph->column_capacity);
+       graph->new_mapping = xrealloc(graph->new_mapping,
+                                     sizeof(int) * 2 * graph->column_capacity);
+}
+
+static void graph_insert_into_new_columns(struct git_graph *graph,
+                                         struct commit *commit,
+                                         int *mapping_index)
+{
+       int i;
+
+       /*
+        * Ignore uinteresting and pruned commits
+        */
+       if (commit->object.flags & (UNINTERESTING | TREESAME))
+               return;
+
+       /*
+        * If the commit is already in the new_columns list, we don't need to
+        * add it.  Just update the mapping correctly.
+        */
+       for (i = 0; i < graph->num_new_columns; i++) {
+               if (graph->new_columns[i].commit == commit) {
+                       graph->mapping[*mapping_index] = i;
+                       *mapping_index += 2;
+                       return;
+               }
+       }
+
+       /*
+        * This commit isn't already in new_columns.  Add it.
+        */
+       graph->new_columns[graph->num_new_columns].commit = commit;
+       graph->mapping[*mapping_index] = graph->num_new_columns;
+       *mapping_index += 2;
+       graph->num_new_columns++;
+}
+
+static void graph_update_width(struct git_graph *graph,
+                              int is_commit_in_existing_columns)
+{
+       /*
+        * Compute the width needed to display the graph for this commit.
+        * This is the maximum width needed for any row.  All other rows
+        * will be padded to this width.
+        *
+        * Compute the number of columns in the widest row:
+        * Count each existing column (graph->num_columns), and each new
+        * column added by this commit.
+        */
+       int max_cols = graph->num_columns + graph->num_parents;
+
+       /*
+        * Even if the current commit has no parents, it still takes up a
+        * column for itself.
+        */
+       if (graph->num_parents < 1)
+               max_cols++;
+
+       /*
+        * We added a column for the the current commit as part of
+        * graph->num_parents.  If the current commit was already in
+        * graph->columns, then we have double counted it.
+        */
+       if (is_commit_in_existing_columns)
+               max_cols--;
+
+       /*
+        * Each column takes up 2 spaces
+        */
+       graph->width = max_cols * 2;
+}
+
+static void graph_update_columns(struct git_graph *graph)
+{
+       struct commit_list *parent;
+       struct column *tmp_columns;
+       int max_new_columns;
+       int mapping_idx;
+       int i, seen_this, is_commit_in_columns;
+
+       /*
+        * Swap graph->columns with graph->new_columns
+        * graph->columns contains the state for the previous commit,
+        * and new_columns now contains the state for our commit.
+        *
+        * We'll re-use the old columns array as storage to compute the new
+        * columns list for the commit after this one.
+        */
+       tmp_columns = graph->columns;
+       graph->columns = graph->new_columns;
+       graph->num_columns = graph->num_new_columns;
+
+       graph->new_columns = tmp_columns;
+       graph->num_new_columns = 0;
+
+       /*
+        * Now update new_columns and mapping with the information for the
+        * commit after this one.
+        *
+        * First, make sure we have enough room.  At most, there will
+        * be graph->num_columns + graph->num_parents columns for the next
+        * commit.
+        */
+       max_new_columns = graph->num_columns + graph->num_parents;
+       graph_ensure_capacity(graph, max_new_columns);
+
+       /*
+        * Clear out graph->mapping
+        */
+       graph->mapping_size = 2 * max_new_columns;
+       for (i = 0; i < graph->mapping_size; i++)
+               graph->mapping[i] = -1;
+
+       /*
+        * Populate graph->new_columns and graph->mapping
+        *
+        * Some of the parents of this commit may already be in
+        * graph->columns.  If so, graph->new_columns should only contain a
+        * single entry for each such commit.  graph->mapping should
+        * contain information about where each current branch line is
+        * supposed to end up after the collapsing is performed.
+        */
+       seen_this = 0;
+       mapping_idx = 0;
+       is_commit_in_columns = 1;
+       for (i = 0; i <= graph->num_columns; i++) {
+               struct commit *col_commit;
+               if (i == graph->num_columns) {
+                       if (seen_this)
+                               break;
+                       is_commit_in_columns = 0;
+                       col_commit = graph->commit;
+               } else {
+                       col_commit = graph->columns[i].commit;
+               }
+
+               if (col_commit == graph->commit) {
+                       seen_this = 1;
+                       for (parent = graph->commit->parents;
+                            parent;
+                            parent = parent->next) {
+                               graph_insert_into_new_columns(graph,
+                                                             parent->item,
+                                                             &mapping_idx);
+                       }
+               } else {
+                       graph_insert_into_new_columns(graph, col_commit,
+                                                     &mapping_idx);
+               }
+       }
+
+       /*
+        * Shrink mapping_size to be the minimum necessary
+        */
+       while (graph->mapping_size > 1 &&
+              graph->mapping[graph->mapping_size - 1] < 0)
+               graph->mapping_size--;
+
+       /*
+        * Compute graph->width for this commit
+        */
+       graph_update_width(graph, is_commit_in_columns);
+}
+
+void graph_update(struct git_graph *graph, struct commit *commit)
+{
+       struct commit_list *parent;
+
+       /*
+        * Set the new commit
+        */
+       graph->commit = commit;
+
+       /*
+        * Count how many parents this commit has
+        */
+       graph->num_parents = 0;
+       for (parent = commit->parents; parent; parent = parent->next)
+               graph->num_parents++;
+
+       /*
+        * Call graph_update_columns() to update
+        * columns, new_columns, and mapping.
+        */
+       graph_update_columns(graph);
+
+       graph->expansion_row = 0;
+
+       /*
+        * Update graph->state.
+        *
+        * If the previous commit didn't get to the GRAPH_PADDING state,
+        * it never finished its output.  Goto GRAPH_SKIP, to print out
+        * a line to indicate that portion of the graph is missing.
+        *
+        * Otherwise, if there are 3 or more parents, we need to print
+        * extra rows before the commit, to expand the branch lines around
+        * it and make room for it.
+        *
+        * If there are less than 3 parents, we can immediately print the
+        * commit line.
+        */
+       if (graph->state != GRAPH_PADDING)
+               graph->state = GRAPH_SKIP;
+       else if (graph->num_parents >= 3)
+               graph->state = GRAPH_PRE_COMMIT;
+       else
+               graph->state = GRAPH_COMMIT;
+}
+
+static int graph_is_mapping_correct(struct git_graph *graph)
+{
+       int i;
+
+       /*
+        * The mapping is up to date if each entry is at its target,
+        * or is 1 greater than its target.
+        * (If it is 1 greater than the target, '/' will be printed, so it
+        * will look correct on the next row.)
+        */
+       for (i = 0; i < graph->mapping_size; i++) {
+               int target = graph->mapping[i];
+               if (target < 0)
+                       continue;
+               if (target == (i / 2))
+                       continue;
+               return 0;
+       }
+
+       return 1;
+}
+
+static void graph_pad_horizontally(struct git_graph *graph, struct strbuf *sb)
+{
+       /*
+        * Add additional spaces to the end of the strbuf, so that all
+        * lines for a particular commit have the same width.
+        *
+        * This way, fields printed to the right of the graph will remain
+        * aligned for the entire commit.
+        */
+       int extra;
+       if (sb->len >= graph->width)
+               return;
+
+       extra = graph->width - sb->len;
+       strbuf_addf(sb, "%*s", (int) extra, "");
+}
+
+static void graph_output_padding_line(struct git_graph *graph,
+                                     struct strbuf *sb)
+{
+       int i;
+
+       /*
+        * We could conceivable be called with a NULL commit
+        * if our caller has a bug, and invokes graph_next_line()
+        * immediately after graph_init(), without first calling
+        * graph_update().  Return without outputting anything in this
+        * case.
+        */
+       if (!graph->commit)
+               return;
+
+       /*
+        * Output a padding row, that leaves all branch lines unchanged
+        */
+       for (i = 0; i < graph->num_new_columns; i++) {
+               strbuf_addstr(sb, "| ");
+       }
+
+       graph_pad_horizontally(graph, sb);
+}
+
+static void graph_output_skip_line(struct git_graph *graph, struct strbuf *sb)
+{
+       /*
+        * Output an ellipsis to indicate that a portion
+        * of the graph is missing.
+        */
+       strbuf_addstr(sb, "...");
+       graph_pad_horizontally(graph, sb);
+
+       if (graph->num_parents >= 3)
+               graph->state = GRAPH_PRE_COMMIT;
+       else
+               graph->state = GRAPH_COMMIT;
+}
+
+static void graph_output_pre_commit_line(struct git_graph *graph,
+                                        struct strbuf *sb)
+{
+       int num_expansion_rows;
+       int i, seen_this;
+
+       /*
+        * This function formats a row that increases the space around a commit
+        * with multiple parents, to make room for it.  It should only be
+        * called when there are 3 or more parents.
+        *
+        * We need 2 extra rows for every parent over 2.
+        */
+       assert(graph->num_parents >= 3);
+       num_expansion_rows = (graph->num_parents - 2) * 2;
+
+       /*
+        * graph->expansion_row tracks the current expansion row we are on.
+        * It should be in the range [0, num_expansion_rows - 1]
+        */
+       assert(0 <= graph->expansion_row &&
+              graph->expansion_row < num_expansion_rows);
+
+       /*
+        * Output the row
+        */
+       seen_this = 0;
+       for (i = 0; i < graph->num_columns; i++) {
+               struct column *col = &graph->columns[i];
+               if (col->commit == graph->commit) {
+                       seen_this = 1;
+                       strbuf_addf(sb, "| %*s", graph->expansion_row, "");
+               } else if (seen_this) {
+                       strbuf_addstr(sb, "\\ ");
+               } else {
+                       strbuf_addstr(sb, "| ");
+               }
+       }
+
+       graph_pad_horizontally(graph, sb);
+
+       /*
+        * Increment graph->expansion_row,
+        * and move to state GRAPH_COMMIT if necessary
+        */
+       graph->expansion_row++;
+       if (graph->expansion_row >= num_expansion_rows)
+               graph->state = GRAPH_COMMIT;
+}
+
+void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
+{
+       int seen_this = 0;
+       int i, j;
+
+       /*
+        * Output the row containing this commit
+        * Iterate up to and including graph->num_columns,
+        * since the current commit may not be in any of the existing
+        * columns.  (This happens when the current commit doesn't have any
+        * children that we have already processed.)
+        */
+       seen_this = 0;
+       for (i = 0; i <= graph->num_columns; i++) {
+               struct commit *col_commit;
+               if (i == graph->num_columns) {
+                       if (seen_this)
+                               break;
+                       col_commit = graph->commit;
+               } else {
+                       col_commit = graph->columns[i].commit;
+               }
+
+               if (col_commit == graph->commit) {
+                       seen_this = 1;
+                       if (graph->num_parents > 1)
+                               strbuf_addch(sb, 'M');
+                       else
+                               strbuf_addch(sb, '*');
+
+                       if (graph->num_parents < 2)
+                               strbuf_addch(sb, ' ');
+                       else if (graph->num_parents == 2)
+                               strbuf_addstr(sb, "  ");
+                       else {
+                               int num_dashes =
+                                       ((graph->num_parents - 2) * 2) - 1;
+                               for (j = 0; j < num_dashes; j++)
+                                       strbuf_addch(sb, '-');
+                               strbuf_addstr(sb, ". ");
+                       }
+               } else if (seen_this && (graph->num_parents > 1)) {
+                       strbuf_addstr(sb, "\\ ");
+               } else {
+                       strbuf_addstr(sb, "| ");
+               }
+       }
+
+       graph_pad_horizontally(graph, sb);
+
+       /*
+        * Update graph->state
+        */
+       if (graph->num_parents > 1)
+               graph->state = GRAPH_POST_MERGE;
+       else if (graph_is_mapping_correct(graph))
+               graph->state = GRAPH_PADDING;
+       else
+               graph->state = GRAPH_COLLAPSING;
+}
+
+void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
+{
+       int seen_this = 0;
+       int i, j;
+
+       /*
+        * Output the post-merge row
+        */
+       for (i = 0; i <= graph->num_columns; i++) {
+               struct commit *col_commit;
+               if (i == graph->num_columns) {
+                       if (seen_this)
+                               break;
+                       col_commit = graph->commit;
+               } else {
+                       col_commit = graph->columns[i].commit;
+               }
+
+               if (col_commit == graph->commit) {
+                       seen_this = 1;
+                       strbuf_addch(sb, '|');
+                       for (j = 0; j < graph->num_parents - 1; j++)
+                               strbuf_addstr(sb, "\\ ");
+                       if (graph->num_parents == 2)
+                               strbuf_addch(sb, ' ');
+               } else if (seen_this && (graph->num_parents > 2)) {
+                       strbuf_addstr(sb, "\\ ");
+               } else {
+                       strbuf_addstr(sb, "| ");
+               }
+       }
+
+       graph_pad_horizontally(graph, sb);
+
+       /*
+        * Update graph->state
+        */
+       if (graph_is_mapping_correct(graph))
+               graph->state = GRAPH_PADDING;
+       else
+               graph->state = GRAPH_COLLAPSING;
+}
+
+void graph_output_collapsing_line(struct git_graph *graph, struct strbuf *sb)
+{
+       int i;
+       int *tmp_mapping;
+
+       /*
+        * Clear out the new_mapping array
+        */
+       for (i = 0; i < graph->mapping_size; i++)
+               graph->new_mapping[i] = -1;
+
+       for (i = 0; i < graph->mapping_size; i++) {
+               int target = graph->mapping[i];
+               if (target < 0)
+                       continue;
+
+               /*
+                * Since update_columns() always inserts the leftmost
+                * column first, each branch's target location should
+                * always be either its current location or to the left of
+                * its current location.
+                *
+                * We never have to move branches to the right.  This makes
+                * the graph much more legible, since whenever branches
+                * cross, only one is moving directions.
+                */
+               assert(target * 2 <= i);
+
+               if (target * 2 == i) {
+                       /*
+                        * This column is already in the
+                        * correct place
+                        */
+                       assert(graph->new_mapping[i] == -1);
+                       graph->new_mapping[i] = target;
+               } else if (graph->new_mapping[i - 1] < 0) {
+                       /*
+                        * Nothing is to the left.
+                        * Move to the left by one
+                        */
+                       graph->new_mapping[i - 1] = target;
+               } else if (graph->new_mapping[i - 1] == target) {
+                       /*
+                        * There is a branch line to our left
+                        * already, and it is our target.  We
+                        * combine with this line, since we share
+                        * the same parent commit.
+                        *
+                        * We don't have to add anything to the
+                        * output or new_mapping, since the
+                        * existing branch line has already taken
+                        * care of it.
+                        */
+               } else {
+                       /*
+                        * There is a branch line to our left,
+                        * but it isn't our target.  We need to
+                        * cross over it.
+                        *
+                        * The space just to the left of this
+                        * branch should always be empty.
+                        */
+                       assert(graph->new_mapping[i - 1] > target);
+                       assert(graph->new_mapping[i - 2] < 0);
+                       graph->new_mapping[i - 2] = target;
+               }
+       }
+
+       /*
+        * The new mapping may be 1 smaller than the old mapping
+        */
+       if (graph->new_mapping[graph->mapping_size - 1] < 0)
+               graph->mapping_size--;
+
+       /*
+        * Output out a line based on the new mapping info
+        */
+       for (i = 0; i < graph->mapping_size; i++) {
+               int target = graph->new_mapping[i];
+               if (target < 0)
+                       strbuf_addch(sb, ' ');
+               else if (target * 2 == i)
+                       strbuf_addch(sb, '|');
+               else
+                       strbuf_addch(sb, '/');
+       }
+
+       graph_pad_horizontally(graph, sb);
+
+       /*
+        * Swap mapping and new_mapping
+        */
+       tmp_mapping = graph->mapping;
+       graph->mapping = graph->new_mapping;
+       graph->new_mapping = tmp_mapping;
+
+       /*
+        * If graph->mapping indicates that all of the branch lines
+        * are already in the correct positions, we are done.
+        * Otherwise, we need to collapse some branch lines together.
+        */
+       if (graph_is_mapping_correct(graph))
+               graph->state = GRAPH_PADDING;
+}
+
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+{
+       switch (graph->state) {
+       case GRAPH_PADDING:
+               graph_output_padding_line(graph, sb);
+               return 0;
+       case GRAPH_SKIP:
+               graph_output_skip_line(graph, sb);
+               return 0;
+       case GRAPH_PRE_COMMIT:
+               graph_output_pre_commit_line(graph, sb);
+               return 0;
+       case GRAPH_COMMIT:
+               graph_output_commit_line(graph, sb);
+               return 1;
+       case GRAPH_POST_MERGE:
+               graph_output_post_merge_line(graph, sb);
+               return 0;
+       case GRAPH_COLLAPSING:
+               graph_output_collapsing_line(graph, sb);
+               return 0;
+       }
+
+       assert(0);
+       return 0;
+}
+
+void graph_padding_line(struct git_graph *graph, struct strbuf *sb)
+{
+       int i, j;
+
+       if (graph->state != GRAPH_COMMIT) {
+               graph_next_line(graph, sb);
+               return;
+       }
+
+       /*
+        * Output the row containing this commit
+        * Iterate up to and including graph->num_columns,
+        * since the current commit may not be in any of the existing
+        * columns.  (This happens when the current commit doesn't have any
+        * children that we have already processed.)
+        */
+       for (i = 0; i < graph->num_columns; i++) {
+               struct commit *col_commit = graph->columns[i].commit;
+               if (col_commit == graph->commit) {
+                       strbuf_addch(sb, '|');
+
+                       if (graph->num_parents < 3)
+                               strbuf_addch(sb, ' ');
+                       else {
+                               int num_spaces = ((graph->num_parents - 2) * 2);
+                               for (j = 0; j < num_spaces; j++)
+                                       strbuf_addch(sb, ' ');
+                       }
+               } else {
+                       strbuf_addstr(sb, "| ");
+               }
+       }
+
+       graph_pad_horizontally(graph, sb);
+}
+
+int graph_is_commit_finished(struct git_graph const *graph)
+{
+       return (graph->state == GRAPH_PADDING);
+}
+
+void graph_show_commit(struct git_graph *graph)
+{
+       struct strbuf msgbuf;
+       int shown_commit_line = 0;
+
+       if (!graph)
+               return;
+
+       strbuf_init(&msgbuf, 0);
+
+       while (!shown_commit_line) {
+               shown_commit_line = graph_next_line(graph, &msgbuf);
+               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+               if (!shown_commit_line)
+                       putchar('\n');
+               strbuf_setlen(&msgbuf, 0);
+       }
+
+       strbuf_release(&msgbuf);
+}
+
+void graph_show_oneline(struct git_graph *graph)
+{
+       struct strbuf msgbuf;
+
+       if (!graph)
+               return;
+
+       strbuf_init(&msgbuf, 0);
+       graph_next_line(graph, &msgbuf);
+       fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+       strbuf_release(&msgbuf);
+}
+
+void graph_show_padding(struct git_graph *graph)
+{
+       struct strbuf msgbuf;
+
+       if (!graph)
+               return;
+
+       strbuf_init(&msgbuf, 0);
+       graph_padding_line(graph, &msgbuf);
+       fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+       strbuf_release(&msgbuf);
+}
+
+int graph_show_remainder(struct git_graph *graph)
+{
+       struct strbuf msgbuf;
+       int shown = 0;
+
+       if (!graph)
+               return 0;
+
+       if (graph_is_commit_finished(graph))
+               return 0;
+
+       strbuf_init(&msgbuf, 0);
+       for (;;) {
+               graph_next_line(graph, &msgbuf);
+               fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
+               strbuf_setlen(&msgbuf, 0);
+               shown = 1;
+
+               if (!graph_is_commit_finished(graph))
+                       putchar('\n');
+               else
+                       break;
+       }
+       strbuf_release(&msgbuf);
+
+       return shown;
+}
+
+
+void graph_show_strbuf(struct git_graph *graph, struct strbuf const *sb)
+{
+       char *p;
+
+       if (!graph) {
+               fwrite(sb->buf, sizeof(char), sb->len, stdout);
+               return;
+       }
+
+       /*
+        * Print the strbuf line by line,
+        * and display the graph info before each line but the first.
+        */
+       p = sb->buf;
+       while (p) {
+               size_t len;
+               char *next_p = strchr(p, '\n');
+               if (next_p) {
+                       next_p++;
+                       len = next_p - p;
+               } else {
+                       len = (sb->buf + sb->len) - p;
+               }
+               fwrite(p, sizeof(char), len, stdout);
+               if (next_p && *next_p != '\0')
+                       graph_show_oneline(graph);
+               p = next_p;
+       }
+}
+
+void graph_show_commit_msg(struct git_graph *graph,
+                          struct strbuf const *sb)
+{
+       int newline_terminated;
+
+       if (!graph) {
+               /*
+                * If there's no graph, just print the message buffer.
+                *
+                * The message buffer for CMIT_FMT_ONELINE and
+                * CMIT_FMT_USERFORMAT are already missing a terminating
+                * newline.  All of the other formats should have it.
+                */
+               fwrite(sb->buf, sizeof(char), sb->len, stdout);
+               return;
+       }
+
+       newline_terminated = (sb->len && sb->buf[sb->len - 1] == '\n');
+
+       /*
+        * Show the commit message
+        */
+       graph_show_strbuf(graph, sb);
+
+       /*
+        * If there is more output needed for this commit, show it now
+        */
+       if (!graph_is_commit_finished(graph)) {
+               /*
+                * If sb doesn't have a terminating newline, print one now,
+                * so we can start the remainder of the graph output on a
+                * new line.
+                */
+               if (!newline_terminated)
+                       putchar('\n');
+
+               graph_show_remainder(graph);
+
+               /*
+                * If sb ends with a newline, our output should too.
+                */
+               if (newline_terminated)
+                       putchar('\n');
+       }
+}
diff --git a/graph.h b/graph.h
new file mode 100644 (file)
index 0000000..a7748a5
--- /dev/null
+++ b/graph.h
@@ -0,0 +1,121 @@
+#ifndef GRAPH_H
+#define GRAPH_H
+
+/* A graph is a pointer to this opaque structure */
+struct git_graph;
+
+/*
+ * Create a new struct git_graph.
+ * The graph should be freed with graph_release() when no longer needed.
+ */
+struct git_graph *graph_init();
+
+/*
+ * 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
+ * the next time graph_next_line() is called.
+ *
+ * If graph_update() is called before graph_is_commit_finished() returns 1,
+ * the next call to graph_next_line() will output an ellipsis ("...")
+ * to indicate that a portion of the graph is missing.
+ */
+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.
+ *
+ * Returns 1 if graph_next_line() needs to be called again before
+ * graph_update() should be called.  Returns 0 if no more lines are needed
+ * for this commit.  If 0 is returned, graph_next_line() may still be
+ * called without calling graph_update(), and it will merely output
+ * appropriate "vertical padding" in the graph.
+ */
+int graph_is_commit_finished(struct git_graph const *graph);
+
+
+/*
+ * graph_show_*: helper functions for printing to stdout
+ */
+
+
+/*
+ * If the graph is non-NULL, print the history graph to stdout,
+ * up to and including the line containing this commit.
+ * Does not print a terminating newline on the last line.
+ */
+void graph_show_commit(struct git_graph *graph);
+
+/*
+ * If the graph is non-NULL, print one line of the history graph to stdout.
+ * Does not print a terminating newline on the last line.
+ */
+void graph_show_oneline(struct git_graph *graph);
+
+/*
+ * If the graph is non-NULL, print one line of vertical graph padding to
+ * stdout.  Does not print a terminating newline on the last line.
+ */
+void graph_show_padding(struct git_graph *graph);
+
+/*
+ * If the graph is non-NULL, print the rest of the history graph for this
+ * commit to stdout.  Does not print a terminating newline on the last line.
+ */
+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.
+ *
+ * This is similar to graph_show_strbuf(), but it always prints the
+ * remainder of the graph.
+ *
+ * If the strbuf ends with a newline, the output printed by
+ * graph_show_commit_msg() will end with a newline.  If the strbuf is
+ * missing a terminating newline (including if it is empty), the output
+ * printed by graph_show_commit_msg() will also be missing a terminating
+ * newline.
+ */
+void graph_show_commit_msg(struct git_graph *graph, struct strbuf const *sb);
+
+#endif /* GRAPH_H */
diff --git a/help.c b/help.c
index 10298fb0a197f18008783214de11e424fbadb77d..af80979fcb177faace150a7cfa7cb66daeb59f49 100644 (file)
--- a/help.c
+++ b/help.c
 #include "run-command.h"
 
 static struct man_viewer_list {
-       void (*exec)(const char *);
        struct man_viewer_list *next;
+       char name[FLEX_ARRAY];
 } *man_viewer_list;
 
+static struct man_viewer_info_list {
+       struct man_viewer_info_list *next;
+       const char *info;
+       char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
 enum help_format {
        HELP_FORMAT_MAN,
        HELP_FORMAT_INFO,
@@ -49,6 +55,18 @@ static enum help_format parse_help_format(const char *format)
        die("unrecognized help format '%s'", format);
 }
 
+static const char *get_man_viewer_info(const char *name)
+{
+       struct man_viewer_info_list *viewer;
+
+       for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+       {
+               if (!strcasecmp(name, viewer->name))
+                       return viewer->info;
+       }
+       return NULL;
+}
+
 static int check_emacsclient_version(void)
 {
        struct strbuf buffer = STRBUF_INIT;
@@ -95,56 +113,145 @@ static int check_emacsclient_version(void)
        return 0;
 }
 
-static void exec_woman_emacs(const char *page)
+static void exec_woman_emacs(const char* path, const char *page)
 {
        if (!check_emacsclient_version()) {
                /* This works only with emacsclient version >= 22. */
                struct strbuf man_page = STRBUF_INIT;
+
+               if (!path)
+                       path = "emacsclient";
                strbuf_addf(&man_page, "(woman \"%s\")", page);
-               execlp("emacsclient", "emacsclient", "-e", man_page.buf, NULL);
+               execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
        }
 }
 
-static void exec_man_konqueror(const char *page)
+static void exec_man_konqueror(const char* path, const char *page)
 {
        const char *display = getenv("DISPLAY");
        if (display && *display) {
                struct strbuf man_page = STRBUF_INIT;
+               const char *filename = "kfmclient";
+
+               /* It's simpler to launch konqueror using kfmclient. */
+               if (path) {
+                       const char *file = strrchr(path, '/');
+                       if (file && !strcmp(file + 1, "konqueror")) {
+                               char *new = xstrdup(path);
+                               char *dest = strrchr(new, '/');
+
+                               /* strlen("konqueror") == strlen("kfmclient") */
+                               strcpy(dest + 1, "kfmclient");
+                               path = new;
+                       }
+                       if (file)
+                               filename = file;
+               } else
+                       path = "kfmclient";
                strbuf_addf(&man_page, "man:%s(1)", page);
-               execlp("kfmclient", "kfmclient", "newTab", man_page.buf, NULL);
+               execlp(path, filename, "newTab", man_page.buf, NULL);
+               warning("failed to exec '%s': %s", path, strerror(errno));
        }
 }
 
-static void exec_man_man(const char *page)
+static void exec_man_man(const char* path, const char *page)
+{
+       if (!path)
+               path = "man";
+       execlp(path, "man", page, NULL);
+       warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
 {
-       execlp("man", "man", page, NULL);
+       struct strbuf shell_cmd = STRBUF_INIT;
+       strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+       execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+       warning("failed to exec '%s': %s", cmd, strerror(errno));
 }
 
-static void do_add_man_viewer(void (*exec)(const char *))
+static void add_man_viewer(const char *name)
 {
        struct man_viewer_list **p = &man_viewer_list;
+       size_t len = strlen(name);
 
        while (*p)
                p = &((*p)->next);
-       *p = xmalloc(sizeof(**p));
-       (*p)->next = NULL;
-       (*p)->exec = exec;
+       *p = xcalloc(1, (sizeof(**p) + len + 1));
+       strncpy((*p)->name, name, len);
 }
 
-static int add_man_viewer(const char *value)
+static int supported_man_viewer(const char *name, size_t len)
 {
-       if (!strcasecmp(value, "man"))
-               do_add_man_viewer(exec_man_man);
-       else if (!strcasecmp(value, "woman"))
-               do_add_man_viewer(exec_woman_emacs);
-       else if (!strcasecmp(value, "konqueror"))
-               do_add_man_viewer(exec_man_konqueror);
+       return (!strncasecmp("man", name, len) ||
+               !strncasecmp("woman", name, len) ||
+               !strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+                                  size_t len,
+                                  const char *value)
+{
+       struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
+
+       strncpy(new->name, name, len);
+       new->info = xstrdup(value);
+       new->next = man_viewer_info_list;
+       man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+                              size_t len,
+                              const char *value)
+{
+       if (supported_man_viewer(name, len))
+               do_add_man_viewer_info(name, len, value);
        else
-               warning("'%s': unsupported man viewer.", value);
+               warning("'%s': path for unsupported man viewer.\n"
+                       "Please consider using 'man.<tool>.cmd' instead.",
+                       name);
 
        return 0;
 }
 
+static int add_man_viewer_cmd(const char *name,
+                             size_t len,
+                             const char *value)
+{
+       if (supported_man_viewer(name, len))
+               warning("'%s': cmd for supported man viewer.\n"
+                       "Please consider using 'man.<tool>.path' instead.",
+                       name);
+       else
+               do_add_man_viewer_info(name, len, value);
+
+       return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+       const char *name = var + 4;
+       const char *subkey = strrchr(name, '.');
+
+       if (!subkey)
+               return error("Config with no key for man viewer: %s", name);
+
+       if (!strcmp(subkey, ".path")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_path(name, subkey - name, value);
+       }
+       if (!strcmp(subkey, ".cmd")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               return add_man_viewer_cmd(name, subkey - name, value);
+       }
+
+       warning("'%s': unsupported man viewer sub key.", subkey);
+       return 0;
+}
+
 static int git_help_config(const char *var, const char *value)
 {
        if (!strcmp(var, "help.format")) {
@@ -156,8 +263,12 @@ static int git_help_config(const char *var, const char *value)
        if (!strcmp(var, "man.viewer")) {
                if (!value)
                        return config_error_nonbool(var);
-               return add_man_viewer(value);
+               add_man_viewer(value);
+               return 0;
        }
+       if (!prefixcmp(var, "man."))
+               return add_man_viewer_info(var, value);
+
        return git_default_config(var, value);
 }
 
@@ -453,6 +564,22 @@ static void setup_man_path(void)
        strbuf_release(&new_path);
 }
 
+static void exec_viewer(const char *name, const char *page)
+{
+       const char *info = get_man_viewer_info(name);
+
+       if (!strcasecmp(name, "man"))
+               exec_man_man(info, page);
+       else if (!strcasecmp(name, "woman"))
+               exec_woman_emacs(info, page);
+       else if (!strcasecmp(name, "konqueror"))
+               exec_man_konqueror(info, page);
+       else if (info)
+               exec_man_cmd(info, page);
+       else
+               warning("'%s': unknown man viewer.", name);
+}
+
 static void show_man_page(const char *git_cmd)
 {
        struct man_viewer_list *viewer;
@@ -461,9 +588,9 @@ static void show_man_page(const char *git_cmd)
        setup_man_path();
        for (viewer = man_viewer_list; viewer; viewer = viewer->next)
        {
-               viewer->exec(page); /* will return when unable */
+               exec_viewer(viewer->name, page); /* will return when unable */
        }
-       exec_man_man(page);
+       exec_viewer("man", page);
        die("no man viewer handled the request");
 }
 
index 5b230380ccd33d8b04f8dfce24050237ae9206ad..42727c8a45c7b50e8e6b2b04a0799c9ba85a0114 100644 (file)
@@ -1759,15 +1759,15 @@ static int one_local_ref(const char *refname, const unsigned char *sha1, int fla
 static void one_remote_ref(char *refname)
 {
        struct ref *ref;
-       unsigned char remote_sha1[20];
        struct object *obj;
-       int len = strlen(refname) + 1;
 
-       if (http_fetch_ref(remote->url, refname + 5 /* "refs/" */,
-                          remote_sha1) != 0) {
+       ref = alloc_ref_from_str(refname);
+
+       if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
                        "Unable to fetch ref %s from %s\n",
                        refname, remote->url);
+               free(ref);
                return;
        }
 
@@ -1775,18 +1775,15 @@ static void one_remote_ref(char *refname)
         * Fetch a copy of the object if it doesn't exist locally - it
         * may be required for updating server info later.
         */
-       if (remote->can_update_info_refs && !has_sha1_file(remote_sha1)) {
-               obj = lookup_unknown_object(remote_sha1);
+       if (remote->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
+               obj = lookup_unknown_object(ref->old_sha1);
                if (obj) {
                        fprintf(stderr, "  fetch %s for %s\n",
-                               sha1_to_hex(remote_sha1), refname);
+                               sha1_to_hex(ref->old_sha1), refname);
                        add_fetch_request(obj);
                }
        }
 
-       ref = xcalloc(1, sizeof(*ref) + len);
-       hashcpy(ref->old_sha1, remote_sha1);
-       memcpy(ref->name, refname, len);
        *remote_tail = ref;
        remote_tail = &ref->next;
 }
@@ -1891,33 +1888,36 @@ static void mark_edges_uninteresting(struct commit_list *list)
 static void add_remote_info_ref(struct remote_ls_ctx *ls)
 {
        struct strbuf *buf = (struct strbuf *)ls->userData;
-       unsigned char remote_sha1[20];
        struct object *o;
        int len;
        char *ref_info;
+       struct ref *ref;
+
+       ref = alloc_ref_from_str(ls->dentry_name);
 
-       if (http_fetch_ref(remote->url, ls->dentry_name + 5 /* "refs/" */,
-                          remote_sha1) != 0) {
+       if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
                        "Unable to fetch ref %s from %s\n",
                        ls->dentry_name, remote->url);
                aborted = 1;
+               free(ref);
                return;
        }
 
-       o = parse_object(remote_sha1);
+       o = parse_object(ref->old_sha1);
        if (!o) {
                fprintf(stderr,
                        "Unable to parse object %s for remote ref %s\n",
-                       sha1_to_hex(remote_sha1), ls->dentry_name);
+                       sha1_to_hex(ref->old_sha1), ls->dentry_name);
                aborted = 1;
+               free(ref);
                return;
        }
 
        len = strlen(ls->dentry_name) + 42;
        ref_info = xcalloc(len + 1, 1);
        sprintf(ref_info, "%s   %s\n",
-               sha1_to_hex(remote_sha1), ls->dentry_name);
+               sha1_to_hex(ref->old_sha1), ls->dentry_name);
        fwrite_buffer(ref_info, 1, len, buf);
        free(ref_info);
 
@@ -1932,6 +1932,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
                        free(ref_info);
                }
        }
+       free(ref);
 }
 
 static void update_remote_info_refs(struct remote_lock *lock)
index 7bda34d91498c3959569327ea2132851230999e7..99f397e32b673e289c2f9d298b1287a97cb7a49e 100644 (file)
@@ -888,10 +888,10 @@ static int fetch(struct walker *walker, unsigned char *sha1)
                     data->alt->base);
 }
 
-static int fetch_ref(struct walker *walker, char *ref, unsigned char *sha1)
+static int fetch_ref(struct walker *walker, struct ref *ref)
 {
        struct walker_data *data = walker->data;
-       return http_fetch_ref(data->alt->base, ref, sha1);
+       return http_fetch_ref(data->alt->base, ref);
 }
 
 static void cleanup(struct walker *walker)
diff --git a/http.c b/http.c
index 256a5f15f40a8d9389560e1fb08e34a56e9f7140..acf746a12da0f0b5e3fe3f097a6626e17da8852c 100644 (file)
--- a/http.c
+++ b/http.c
@@ -589,8 +589,9 @@ static char *quote_ref_url(const char *base, const char *ref)
                        len += 2; /* extra two hex plus replacement % */
        qref = xmalloc(len);
        memcpy(qref, base, baselen);
-       memcpy(qref + baselen, "/refs/", 6);
-       for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
+       dp = qref + baselen;
+       *(dp++) = '/';
+       for (cp = ref; (ch = *cp) != 0; cp++) {
                if (needs_quote(ch)) {
                        *dp++ = '%';
                        *dp++ = hex((ch >> 4) & 0xF);
@@ -604,7 +605,7 @@ static char *quote_ref_url(const char *base, const char *ref)
        return qref;
 }
 
-int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
+int http_fetch_ref(const char *base, struct ref *ref)
 {
        char *url;
        struct strbuf buffer = STRBUF_INIT;
@@ -612,7 +613,7 @@ int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
        struct slot_results results;
        int ret;
 
-       url = quote_ref_url(base, ref);
+       url = quote_ref_url(base, ref->name);
        slot = get_active_slot();
        slot->results = &results;
        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
@@ -624,12 +625,15 @@ int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1)
                if (results.curl_result == CURLE_OK) {
                        strbuf_rtrim(&buffer);
                        if (buffer.len == 40)
-                               ret = get_sha1_hex(buffer.buf, sha1);
-                       else
+                               ret = get_sha1_hex(buffer.buf, ref->old_sha1);
+                       else if (!prefixcmp(buffer.buf, "ref: ")) {
+                               ref->symref = xstrdup(buffer.buf + 5);
+                               ret = 0;
+                       } else
                                ret = 1;
                } else {
                        ret = error("Couldn't get %s for %s\n%s",
-                                   url, ref, curl_errorstr);
+                                   url, ref->name, curl_errorstr);
                }
        } else {
                ret = error("Unable to start request");
diff --git a/http.h b/http.h
index 04169d5f9c8fa4cb82ad720b9e6d371f02be83d3..a04fc6a9277945a7084e718753c133ed47d13691 100644 (file)
--- a/http.h
+++ b/http.h
@@ -105,6 +105,6 @@ static inline int missing__target(int code, int result)
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
-extern int http_fetch_ref(const char *base, const char *ref, unsigned char *sha1);
+extern int http_fetch_ref(const char *base, struct ref *ref);
 
 #endif /* HTTP_H */
diff --git a/ident.c b/ident.c
index ed44a5345a47d4843e8328c0f879ac925dde2b07..b35504a8d25594a8d243ae7490733eae5a262712 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -250,6 +250,9 @@ const char *git_author_info(int flag)
 
 const char *git_committer_info(int flag)
 {
+       if (getenv("GIT_COMMITTER_NAME") &&
+           getenv("GIT_COMMITTER_EMAIL"))
+               user_ident_explicitly_given = 1;
        return fmt_ident(getenv("GIT_COMMITTER_NAME"),
                         getenv("GIT_COMMITTER_EMAIL"),
                         getenv("GIT_COMMITTER_DATE"),
index d3fb0e520c749e0d57afaab7666861a1a4e7cd9f..1474d1f5d96c9b04b0d5c190f88b2fdefd2ef679 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "diff.h"
 #include "commit.h"
+#include "graph.h"
 #include "log-tree.h"
 #include "reflog-walk.h"
 
@@ -165,11 +166,16 @@ void log_write_email_headers(struct rev_info *opt, const char *name,
        }
 
        printf("From %s Mon Sep 17 00:00:00 2001\n", name);
-       if (opt->message_id)
+       graph_show_oneline(opt->graph);
+       if (opt->message_id) {
                printf("Message-Id: <%s>\n", opt->message_id);
-       if (opt->ref_message_id)
+               graph_show_oneline(opt->graph);
+       }
+       if (opt->ref_message_id) {
                printf("In-Reply-To: <%s>\nReferences: <%s>\n",
                       opt->ref_message_id, opt->ref_message_id);
+               graph_show_oneline(opt->graph);
+       }
        if (opt->mime_boundary) {
                static char subject_buffer[1024];
                static char buffer[1024];
@@ -220,6 +226,8 @@ void show_log(struct rev_info *opt)
 
        opt->loginfo = NULL;
        if (!opt->verbose_header) {
+               graph_show_commit(opt->graph);
+
                if (commit->object.flags & BOUNDARY)
                        putchar('-');
                else if (commit->object.flags & UNINTERESTING)
@@ -231,9 +239,13 @@ void show_log(struct rev_info *opt)
                                putchar('>');
                }
                fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
-               if (opt->parents)
+               if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
                show_decorations(commit);
+               if (opt->graph && !graph_is_commit_finished(opt->graph)) {
+                       putchar('\n');
+                       graph_show_remainder(opt->graph);
+               }
                putchar(opt->diffopt.line_termination);
                return;
        }
@@ -243,10 +255,32 @@ void show_log(struct rev_info *opt)
         * Otherwise, add a diffopt.line_termination character before all
         * entries but the first.  (IOW, as a separator between entries)
         */
-       if (opt->shown_one && !opt->use_terminator)
+       if (opt->shown_one && !opt->use_terminator) {
+               /*
+                * If entries are separated by a newline, the output
+                * should look human-readable.  If the last entry ended
+                * with a newline, print the graph output before this
+                * newline.  Otherwise it will end up as a completely blank
+                * line and will look like a gap in the graph.
+                *
+                * If the entry separator is not a newline, the output is
+                * primarily intended for programmatic consumption, and we
+                * never want the extra graph output before the entry
+                * separator.
+                */
+               if (opt->diffopt.line_termination == '\n' &&
+                   !opt->missing_newline)
+                       graph_show_padding(opt->graph);
                putchar(opt->diffopt.line_termination);
+       }
        opt->shown_one = 1;
 
+       /*
+        * If the history graph was requested,
+        * print the graph, up to this commit's line
+        */
+       graph_show_commit(opt->graph);
+
        /*
         * Print header line of header..
         */
@@ -271,7 +305,7 @@ void show_log(struct rev_info *opt)
                }
                fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit),
                      stdout);
-               if (opt->parents)
+               if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
                if (parent)
                        printf(" (from %s)",
@@ -279,8 +313,19 @@ void show_log(struct rev_info *opt)
                                                  abbrev_commit));
                show_decorations(commit);
                printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
-               putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
+               if (opt->commit_format == CMIT_FMT_ONELINE) {
+                       putchar(' ');
+               } else {
+                       putchar('\n');
+                       graph_show_oneline(opt->graph);
+               }
                if (opt->reflog_info) {
+                       /*
+                        * setup_revisions() ensures that opt->reflog_info
+                        * and opt->graph cannot both be set,
+                        * so we don't need to worry about printing the
+                        * graph info here.
+                        */
                        show_reflog_message(opt->reflog_info,
                                    opt->commit_format == CMIT_FMT_ONELINE,
                                    opt->date_mode);
@@ -304,13 +349,30 @@ void show_log(struct rev_info *opt)
 
        if (opt->add_signoff)
                append_signoff(&msgbuf, opt->add_signoff);
-       if (opt->show_log_size)
+       if (opt->show_log_size) {
                printf("log size %i\n", (int)msgbuf.len);
+               graph_show_oneline(opt->graph);
+       }
 
-       if (msgbuf.len)
+       /*
+        * Set opt->missing_newline if msgbuf doesn't
+        * end in a newline (including if it is empty)
+        */
+       if (!msgbuf.len || msgbuf.buf[msgbuf.len - 1] != '\n')
+               opt->missing_newline = 1;
+       else
+               opt->missing_newline = 0;
+
+       if (opt->graph)
+               graph_show_commit_msg(opt->graph, &msgbuf);
+       else
                fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
-       if (opt->use_terminator)
+       if (opt->use_terminator) {
+               if (!opt->missing_newline)
+                       graph_show_padding(opt->graph);
                putchar('\n');
+       }
+
        strbuf_release(&msgbuf);
 }
 
diff --git a/name-hash.c b/name-hash.c
new file mode 100644 (file)
index 0000000..0031d78
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * name-hash.c
+ *
+ * Hashing names in the index state
+ *
+ * Copyright (C) 2008 Linus Torvalds
+ */
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
+#include "cache.h"
+
+/*
+ * This removes bit 5 if bit 6 is set.
+ *
+ * That will make US-ASCII characters hash to their upper-case
+ * equivalent. We could easily do this one whole word at a time,
+ * but that's for future worries.
+ */
+static inline unsigned char icase_hash(unsigned char c)
+{
+       return c & ~((c & 0x40) >> 1);
+}
+
+static unsigned int hash_name(const char *name, int namelen)
+{
+       unsigned int hash = 0x123;
+
+       do {
+               unsigned char c = *name++;
+               c = icase_hash(c);
+               hash = hash*101 + c;
+       } while (--namelen);
+       return hash;
+}
+
+static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
+{
+       void **pos;
+       unsigned int hash;
+
+       if (ce->ce_flags & CE_HASHED)
+               return;
+       ce->ce_flags |= CE_HASHED;
+       ce->next = NULL;
+       hash = hash_name(ce->name, ce_namelen(ce));
+       pos = insert_hash(hash, ce, &istate->name_hash);
+       if (pos) {
+               ce->next = *pos;
+               *pos = ce;
+       }
+}
+
+static void lazy_init_name_hash(struct index_state *istate)
+{
+       int nr;
+
+       if (istate->name_hash_initialized)
+               return;
+       for (nr = 0; nr < istate->cache_nr; nr++)
+               hash_index_entry(istate, istate->cache[nr]);
+       istate->name_hash_initialized = 1;
+}
+
+void add_name_hash(struct index_state *istate, struct cache_entry *ce)
+{
+       ce->ce_flags &= ~CE_UNHASHED;
+       if (istate->name_hash_initialized)
+               hash_index_entry(istate, ce);
+}
+
+static int slow_same_name(const char *name1, int len1, const char *name2, int len2)
+{
+       if (len1 != len2)
+               return 0;
+
+       while (len1) {
+               unsigned char c1 = *name1++;
+               unsigned char c2 = *name2++;
+               len1--;
+               if (c1 != c2) {
+                       c1 = toupper(c1);
+                       c2 = toupper(c2);
+                       if (c1 != c2)
+                               return 0;
+               }
+       }
+       return 1;
+}
+
+static int same_name(const struct cache_entry *ce, const char *name, int namelen, int icase)
+{
+       int len = ce_namelen(ce);
+
+       /*
+        * Always do exact compare, even if we want a case-ignoring comparison;
+        * we do the quick exact one first, because it will be the common case.
+        */
+       if (len == namelen && !cache_name_compare(name, namelen, ce->name, len))
+               return 1;
+
+       return icase && slow_same_name(name, namelen, ce->name, len);
+}
+
+struct cache_entry *index_name_exists(struct index_state *istate, const char *name, int namelen, int icase)
+{
+       unsigned int hash = hash_name(name, namelen);
+       struct cache_entry *ce;
+
+       lazy_init_name_hash(istate);
+       ce = lookup_hash(hash, &istate->name_hash);
+
+       while (ce) {
+               if (!(ce->ce_flags & CE_UNHASHED)) {
+                       if (same_name(ce, name, namelen, icase))
+                               return ce;
+               }
+               ce = ce->next;
+       }
+       return NULL;
+}
index a92b25b59bf0e096942bca126542a1ea411b525b..8b467f8f415b68db6e6ea83720ce6ae63dedb9e4 100644 (file)
 
 struct index_state the_index;
 
-static unsigned int hash_name(const char *name, int namelen)
-{
-       unsigned int hash = 0x123;
-
-       do {
-               unsigned char c = *name++;
-               hash = hash*101 + c;
-       } while (--namelen);
-       return hash;
-}
-
-static void hash_index_entry(struct index_state *istate, struct cache_entry *ce)
-{
-       void **pos;
-       unsigned int hash;
-
-       if (ce->ce_flags & CE_HASHED)
-               return;
-       ce->ce_flags |= CE_HASHED;
-       ce->next = NULL;
-       hash = hash_name(ce->name, ce_namelen(ce));
-       pos = insert_hash(hash, ce, &istate->name_hash);
-       if (pos) {
-               ce->next = *pos;
-               *pos = ce;
-       }
-}
-
-static void lazy_init_name_hash(struct index_state *istate)
-{
-       int nr;
-
-       if (istate->name_hash_initialized)
-               return;
-       for (nr = 0; nr < istate->cache_nr; nr++)
-               hash_index_entry(istate, istate->cache[nr]);
-       istate->name_hash_initialized = 1;
-}
-
 static void set_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
-       ce->ce_flags &= ~CE_UNHASHED;
        istate->cache[nr] = ce;
-       if (istate->name_hash_initialized)
-               hash_index_entry(istate, ce);
+       add_name_hash(istate, ce);
 }
 
 static void replace_index_entry(struct index_state *istate, int nr, struct cache_entry *ce)
 {
        struct cache_entry *old = istate->cache[nr];
 
-       remove_index_entry(old);
+       remove_name_hash(old);
        set_index_entry(istate, nr, ce);
        istate->cache_changed = 1;
 }
 
-int index_name_exists(struct index_state *istate, const char *name, int namelen)
-{
-       unsigned int hash = hash_name(name, namelen);
-       struct cache_entry *ce;
-
-       lazy_init_name_hash(istate);
-       ce = lookup_hash(hash, &istate->name_hash);
-
-       while (ce) {
-               if (!(ce->ce_flags & CE_UNHASHED)) {
-                       if (!cache_name_compare(name, namelen, ce->name, ce->ce_flags))
-                               return 1;
-               }
-               ce = ce->next;
-       }
-       return 0;
-}
-
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -257,7 +198,8 @@ static int ce_match_stat_basic(struct cache_entry *ce, struct stat *st)
 
 static int is_racy_timestamp(const struct index_state *istate, struct cache_entry *ce)
 {
-       return (istate->timestamp &&
+       return (!S_ISGITLINK(ce->ce_mode) &&
+               istate->timestamp &&
                ((unsigned int)istate->timestamp) <= ce->ce_mtime);
 }
 
@@ -438,7 +380,7 @@ int remove_index_entry_at(struct index_state *istate, int pos)
 {
        struct cache_entry *ce = istate->cache[pos];
 
-       remove_index_entry(ce);
+       remove_name_hash(ce);
        istate->cache_changed = 1;
        istate->cache_nr--;
        if (pos >= istate->cache_nr)
@@ -488,21 +430,50 @@ static int index_name_pos_also_unmerged(struct index_state *istate,
        return pos;
 }
 
-int add_file_to_index(struct index_state *istate, const char *path, int verbose)
+static int different_name(struct cache_entry *ce, struct cache_entry *alias)
 {
-       int size, namelen, pos;
-       struct stat st;
-       struct cache_entry *ce;
-       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
+       int len = ce_namelen(ce);
+       return ce_namelen(alias) != len || memcmp(ce->name, alias->name, len);
+}
 
-       if (lstat(path, &st))
-               die("%s: unable to stat (%s)", path, strerror(errno));
+/*
+ * If we add a filename that aliases in the cache, we will use the
+ * name that we already have - but we don't want to update the same
+ * alias twice, because that implies that there were actually two
+ * different files with aliasing names!
+ *
+ * So we use the CE_ADDED flag to verify that the alias was an old
+ * one before we accept it as
+ */
+static struct cache_entry *create_alias_ce(struct cache_entry *ce, struct cache_entry *alias)
+{
+       int len;
+       struct cache_entry *new;
+
+       if (alias->ce_flags & CE_ADDED)
+               die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name);
+
+       /* Ok, create the new entry using the name of the existing alias */
+       len = ce_namelen(alias);
+       new = xcalloc(1, cache_entry_size(len));
+       memcpy(new->name, alias->name, len);
+       copy_cache_entry(new, ce);
+       free(ce);
+       return new;
+}
+
+int add_to_index(struct index_state *istate, const char *path, struct stat *st, int verbose)
+{
+       int size, namelen;
+       mode_t st_mode = st->st_mode;
+       struct cache_entry *ce, *alias;
+       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
 
-       if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
-               die("%s: can only add regular files, symbolic links or git-directories", path);
+       if (!S_ISREG(st_mode) && !S_ISLNK(st_mode) && !S_ISDIR(st_mode))
+               return error("%s: can only add regular files, symbolic links or git-directories", path);
 
        namelen = strlen(path);
-       if (S_ISDIR(st.st_mode)) {
+       if (S_ISDIR(st_mode)) {
                while (namelen && path[namelen-1] == '/')
                        namelen--;
        }
@@ -510,10 +481,10 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
        ce = xcalloc(1, size);
        memcpy(ce->name, path, namelen);
        ce->ce_flags = namelen;
-       fill_stat_cache_info(ce, &st);
+       fill_stat_cache_info(ce, st);
 
        if (trust_executable_bit && has_symlinks)
-               ce->ce_mode = create_ce_mode(st.st_mode);
+               ce->ce_mode = create_ce_mode(st_mode);
        else {
                /* If there is an existing entry, pick the mode bits and type
                 * from it, otherwise assume unexecutable regular file.
@@ -522,28 +493,37 @@ int add_file_to_index(struct index_state *istate, const char *path, int verbose)
                int pos = index_name_pos_also_unmerged(istate, path, namelen);
 
                ent = (0 <= pos) ? istate->cache[pos] : NULL;
-               ce->ce_mode = ce_mode_from_stat(ent, st.st_mode);
+               ce->ce_mode = ce_mode_from_stat(ent, st_mode);
        }
 
-       pos = index_name_pos(istate, ce->name, namelen);
-       if (0 <= pos &&
-           !ce_stage(istate->cache[pos]) &&
-           !ie_match_stat(istate, istate->cache[pos], &st, ce_option)) {
+       alias = index_name_exists(istate, ce->name, ce_namelen(ce), ignore_case);
+       if (alias && !ce_stage(alias) && !ie_match_stat(istate, alias, st, ce_option)) {
                /* Nothing changed, really */
                free(ce);
-               ce_mark_uptodate(istate->cache[pos]);
+               ce_mark_uptodate(alias);
+               alias->ce_flags |= CE_ADDED;
                return 0;
        }
-
-       if (index_path(ce->sha1, path, &st, 1))
-               die("unable to index file %s", path);
+       if (index_path(ce->sha1, path, st, 1))
+               return error("unable to index file %s", path);
+       if (ignore_case && alias && different_name(ce, alias))
+               ce = create_alias_ce(ce, alias);
+       ce->ce_flags |= CE_ADDED;
        if (add_index_entry(istate, ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
-               die("unable to add %s to index",path);
+               return error("unable to add %s to index",path);
        if (verbose)
                printf("add '%s'\n", path);
        return 0;
 }
 
+int add_file_to_index(struct index_state *istate, const char *path, int verbose)
+{
+       struct stat st;
+       if (lstat(path, &st))
+               die("%s: unable to stat (%s)", path, strerror(errno));
+       return add_to_index(istate, path, &st, verbose);
+}
+
 struct cache_entry *make_cache_entry(unsigned int mode,
                const unsigned char *sha1, const char *path, int stage,
                int refresh)
@@ -1370,7 +1350,7 @@ int write_index(const struct index_state *istate, int newfd)
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
                        continue;
-               if (is_racy_timestamp(istate, ce))
+               if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
                        ce_smudge_racily_clean_entry(ce);
                if (ce_write_entry(&c, newfd, ce) < 0)
                        return -1;
diff --git a/refs.c b/refs.c
index 4db73ed8f01200fca4008bafb23bda36a8e23446..9b495eb16ef03b14791aa44e1098cd918859f0cc 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -352,6 +352,7 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
 {
        int len = strlen(path), retval;
        char *gitdir;
+       const char *tmp;
 
        while (len && path[len-1] == '/')
                len--;
@@ -359,9 +360,19 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
                return -1;
        gitdir = xmalloc(len + MAXREFLEN + 8);
        memcpy(gitdir, path, len);
-       memcpy(gitdir + len, "/.git/", 7);
-
-       retval = resolve_gitlink_ref_recursive(gitdir, len+6, refname, result, 0);
+       memcpy(gitdir + len, "/.git", 6);
+       len += 5;
+
+       tmp = read_gitfile_gently(gitdir);
+       if (tmp) {
+               free(gitdir);
+               len = strlen(tmp);
+               gitdir = xmalloc(len + MAXREFLEN + 3);
+               memcpy(gitdir, tmp, len);
+       }
+       gitdir[len] = '/';
+       gitdir[++len] = '\0';
+       retval = resolve_gitlink_ref_recursive(gitdir, len, refname, result, 0);
        free(gitdir);
        return retval;
 }
index 7c496f981e89ee2833214f27dcd94a86976f9e67..9e4f2b84d90cb97a6cb19779325f9ea443a76e43 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -337,44 +337,49 @@ static int handle_config(const char *key, const char *value)
                return 0;
        }
        remote = make_remote(name, subkey - name);
-       if (!value) {
-               /* if we ever have a boolean variable, e.g. "remote.*.disabled"
-                * [remote "frotz"]
-                *      disabled
-                * is a valid way to set it to true; we get NULL in value so
-                * we need to handle it here.
-                *
-                * if (!strcmp(subkey, ".disabled")) {
-                *      val = git_config_bool(key, value);
-                *      return 0;
-                * } else
-                *
-                */
-               return 0; /* ignore unknown booleans */
-       }
-       if (!strcmp(subkey, ".url")) {
-               add_url(remote, xstrdup(value));
+       if (!strcmp(subkey, ".mirror"))
+               remote->mirror = git_config_bool(key, value);
+       else if (!strcmp(subkey, ".skipdefaultupdate"))
+               remote->skip_default_update = git_config_bool(key, value);
+
+       else if (!strcmp(subkey, ".url")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_url(remote, v);
        } else if (!strcmp(subkey, ".push")) {
-               add_push_refspec(remote, xstrdup(value));
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_push_refspec(remote, v);
        } else if (!strcmp(subkey, ".fetch")) {
-               add_fetch_refspec(remote, xstrdup(value));
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
+               add_fetch_refspec(remote, v);
        } else if (!strcmp(subkey, ".receivepack")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
                if (!remote->receivepack)
-                       remote->receivepack = xstrdup(value);
+                       remote->receivepack = v;
                else
                        error("more than one receivepack given, using the first");
        } else if (!strcmp(subkey, ".uploadpack")) {
+               const char *v;
+               if (git_config_string(&v, key, value))
+                       return -1;
                if (!remote->uploadpack)
-                       remote->uploadpack = xstrdup(value);
+                       remote->uploadpack = v;
                else
                        error("more than one uploadpack given, using the first");
        } else if (!strcmp(subkey, ".tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
        } else if (!strcmp(subkey, ".proxy")) {
-               remote->http_proxy = xstrdup(value);
-       } else if (!strcmp(subkey, ".skipdefaultupdate"))
-               remote->skip_default_update = 1;
+               return git_config_string((const char **)&remote->http_proxy,
+                                        key, value);
+       }
        return 0;
 }
 
@@ -696,6 +701,13 @@ struct ref *alloc_ref(unsigned namelen)
        return ret;
 }
 
+struct ref *alloc_ref_from_str(const char* str)
+{
+       struct ref *ret = alloc_ref(strlen(str) + 1);
+       strcpy(ret->name, str);
+       return ret;
+}
+
 static struct ref *copy_ref(const struct ref *ref)
 {
        struct ref *ret = xmalloc(sizeof(struct ref) + strlen(ref->name) + 1);
@@ -716,13 +728,22 @@ struct ref *copy_ref_list(const struct ref *ref)
        return ret;
 }
 
+void free_ref(struct ref *ref)
+{
+       if (!ref)
+               return;
+       free(ref->remote_status);
+       free(ref->symref);
+       free(ref);
+}
+
 void free_refs(struct ref *ref)
 {
        struct ref *next;
        while (ref) {
                next = ref->next;
                free(ref->peer_ref);
-               free(ref);
+               free_ref(ref);
                ref = next;
        }
 }
@@ -793,7 +814,6 @@ static struct ref *try_explicit_object_name(const char *name)
 {
        unsigned char sha1[20];
        struct ref *ref;
-       int len;
 
        if (!*name) {
                ref = alloc_ref(20);
@@ -803,21 +823,14 @@ static struct ref *try_explicit_object_name(const char *name)
        }
        if (get_sha1(name, sha1))
                return NULL;
-       len = strlen(name) + 1;
-       ref = alloc_ref(len);
-       memcpy(ref->name, name, len);
+       ref = alloc_ref_from_str(name);
        hashcpy(ref->new_sha1, sha1);
        return ref;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       struct ref *ret;
-       size_t len;
-
-       len = strlen(name) + 1;
-       ret = alloc_ref(len);
-       memcpy(ret->name, name, len);
+       struct ref *ret = alloc_ref_from_str(name);
        tail_link_ref(ret, tail);
        return ret;
 }
@@ -1138,9 +1151,7 @@ static struct ref *get_local_ref(const char *name)
                return NULL;
 
        if (!prefixcmp(name, "refs/")) {
-               ret = alloc_ref(strlen(name) + 1);
-               strcpy(ret->name, name);
-               return ret;
+               return alloc_ref_from_str(name);
        }
 
        if (!prefixcmp(name, "heads/") ||
@@ -1199,3 +1210,15 @@ int get_fetch_map(const struct ref *remote_refs,
 
        return 0;
 }
+
+int resolve_remote_symref(struct ref *ref, struct ref *list)
+{
+       if (!ref->symref)
+               return 0;
+       for (; list; list = list->next)
+               if (!strcmp(ref->symref, list->name)) {
+                       hashcpy(ref->old_sha1, list->old_sha1);
+                       return 0;
+               }
+       return 1;
+}
index f7b7be84b440bee840fafd583d7b4d7a8757b7d2..c2f557357fd4a0eb247f0e6f688efd10f946a08a 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -26,6 +26,7 @@ struct remote {
         */
        int fetch_tags;
        int skip_default_update;
+       int mirror;
 
        const char *receivepack;
        const char *uploadpack;
@@ -54,6 +55,8 @@ struct refspec {
 
 struct ref *alloc_ref(unsigned namelen);
 
+struct ref *alloc_ref_from_str(const char* str);
+
 struct ref *copy_ref_list(const struct ref *ref);
 
 int check_ref_type(const struct ref *ref, int flags);
@@ -63,6 +66,8 @@ int check_ref_type(const struct ref *ref, int flags);
  */
 void free_refs(struct ref *ref);
 
+int resolve_remote_symref(struct ref *ref, struct ref *list);
+
 /*
  * Removes and frees any duplicate refs in the map.
  */
index 4231ea2cce57c74a4110c9c69459a4caf67dc15c..7142cf96cde81377522e49de5f102a25eaec2d54 100644 (file)
@@ -6,6 +6,7 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
+#include "graph.h"
 #include "grep.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
@@ -415,7 +416,6 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
 {
        struct commit_list *parent = commit->parents;
        unsigned left_flag;
-       int add, rest;
 
        if (commit->object.flags & ADDED)
                return 0;
@@ -462,19 +462,18 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
 
        left_flag = (commit->object.flags & SYMMETRIC_LEFT);
 
-       rest = !revs->first_parent_only;
-       for (parent = commit->parents, add = 1; parent; add = rest) {
+       for (parent = commit->parents; parent; parent = parent->next) {
                struct commit *p = parent->item;
 
-               parent = parent->next;
                if (parse_commit(p) < 0)
                        return -1;
                p->object.flags |= left_flag;
-               if (p->object.flags & SEEN)
-                       continue;
-               p->object.flags |= SEEN;
-               if (add)
+               if (!(p->object.flags & SEEN)) {
+                       p->object.flags |= SEEN;
                        insert_by_date(p, list);
+               }
+               if(revs->first_parent_only)
+                       break;
        }
        return 0;
 }
@@ -1105,7 +1104,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                }
                        }
                        if (!strcmp(arg, "--parents")) {
-                               revs->parents = 1;
+                               revs->rewrite_parents = 1;
+                               revs->print_parents = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--dense")) {
@@ -1202,6 +1202,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                get_commit_format(arg+8, revs);
                                continue;
                        }
+                       if (!prefixcmp(arg, "--graph")) {
+                               revs->topo_order = 1;
+                               revs->rewrite_parents = 1;
+                               revs->graph = graph_init();
+                               continue;
+                       }
                        if (!strcmp(arg, "--root")) {
                                revs->show_root_diff = 1;
                                continue;
@@ -1396,6 +1402,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (revs->reverse && revs->reflog_info)
                die("cannot combine --reverse with --walk-reflogs");
 
+       /*
+        * Limitations on the graph functionality
+        */
+       if (revs->reverse && revs->graph)
+               die("cannot combine --reverse with --graph");
+
+       if (revs->reflog_info && revs->graph)
+               die("cannot combine --walk-reflogs with --graph");
+
        return left;
 }
 
@@ -1524,13 +1539,13 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
                /* Commit without changes? */
                if (commit->object.flags & TREESAME) {
                        /* drop merges unless we want parenthood */
-                       if (!revs->parents)
+                       if (!revs->rewrite_parents)
                                return commit_ignore;
                        /* non-merge - always ignore it */
                        if (!commit->parents || !commit->parents->next)
                                return commit_ignore;
                }
-               if (revs->parents && rewrite_parents(revs, commit) < 0)
+               if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)
                        return commit_error;
        }
        return commit_show;
@@ -1597,7 +1612,7 @@ static void gc_boundary(struct object_array *array)
        }
 }
 
-struct commit *get_revision(struct rev_info *revs)
+static struct commit *get_revision_internal(struct rev_info *revs)
 {
        struct commit *c = NULL;
        struct commit_list *l;
@@ -1704,3 +1719,11 @@ struct commit *get_revision(struct rev_info *revs)
 
        return c;
 }
+
+struct commit *get_revision(struct rev_info *revs)
+{
+       struct commit *c = get_revision_internal(revs);
+       if (c && revs->graph)
+               graph_update(revs->graph, c);
+       return c;
+}
index 31217f8c67307aa09c2f470ccdbdebd5c2445890..abce5001f19a60bb15b519b26773b57c83563021 100644 (file)
@@ -46,7 +46,8 @@ struct rev_info {
                        unpacked:1, /* see also ignore_packed below */
                        boundary:2,
                        left_right:1,
-                       parents:1,
+                       rewrite_parents:1,
+                       print_parents:1,
                        reverse:1,
                        cherry_pick:1,
                        first_parent_only:1;
@@ -65,7 +66,8 @@ struct rev_info {
        /* Format info */
        unsigned int    shown_one:1,
                        abbrev_commit:1,
-                       use_terminator:1;
+                       use_terminator:1,
+                       missing_newline:1;
        enum date_mode date_mode;
 
        const char **ignore_packed; /* pretend objects in these are unpacked */
@@ -88,6 +90,9 @@ struct rev_info {
        /* Filter by commit log message */
        struct grep_opt *grep_filter;
 
+       /* Display history graph */
+       struct git_graph *graph;
+
        /* special limits */
        int skip_count;
        int max_count;
diff --git a/setup.c b/setup.c
index 1b4fa6a8c4289bedf10f57d9aa19db4c7f186b01..b8fd476395db782bf942a548b87242b3a9f990f4 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -314,6 +314,44 @@ static int check_repository_format_gently(int *nongit_ok)
        return 0;
 }
 
+/*
+ * Try to read the location of the git directory from the .git file,
+ * return path to git directory if found.
+ */
+const char *read_gitfile_gently(const char *path)
+{
+       char *buf;
+       struct stat st;
+       int fd;
+       size_t len;
+
+       if (stat(path, &st))
+               return NULL;
+       if (!S_ISREG(st.st_mode))
+               return NULL;
+       fd = open(path, O_RDONLY);
+       if (fd < 0)
+               die("Error opening %s: %s", path, strerror(errno));
+       buf = xmalloc(st.st_size + 1);
+       len = read_in_full(fd, buf, st.st_size);
+       close(fd);
+       if (len != st.st_size)
+               die("Error reading %s", path);
+       buf[len] = '\0';
+       if (prefixcmp(buf, "gitdir: "))
+               die("Invalid gitfile format: %s", path);
+       while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
+               len--;
+       if (len < 9)
+               die("No path in gitfile: %s", path);
+       buf[len] = '\0';
+       if (!is_git_directory(buf + 8))
+               die("Not a git repository: %s", buf + 8);
+       path = make_absolute_path(buf + 8);
+       free(buf);
+       return path;
+}
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
@@ -323,6 +361,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
+       const char *gitfile_dir;
        int len, offset;
 
        /*
@@ -377,8 +416,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
 
        /*
         * Test in the following order (relative to the cwd):
+        * - .git (file containing "gitdir: <path>")
         * - .git/
         * - ./ (bare)
+        * - ../.git
         * - ../.git/
         * - ../ (bare)
         * - ../../.git/
@@ -386,6 +427,12 @@ const char *setup_git_directory_gently(int *nongit_ok)
         */
        offset = len = strlen(cwd);
        for (;;) {
+               gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+               if (gitfile_dir) {
+                       if (set_git_dir(gitfile_dir))
+                               die("Repository setup failed");
+                       break;
+               }
                if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
                        break;
                if (is_git_directory(".")) {
index 3516777bc7614c23da02dd7a9aa28a48294d56cb..141f5a713a2e542c0bcccbfa57b0dccc3142f362 100644 (file)
@@ -2102,26 +2102,16 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
        return 0;
 }
 
-int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
+                             void *buf, unsigned long len, time_t mtime)
 {
-       int size, ret;
+       int fd, size, ret;
        unsigned char *compressed;
        z_stream stream;
-       unsigned char sha1[20];
        char *filename;
        static char tmpfile[PATH_MAX];
-       char hdr[32];
-       int fd, hdrlen;
 
-       /* Normally if we have it in the pack then we do not bother writing
-        * it out into .git/objects/??/?{38} file.
-        */
-       write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
        filename = sha1_file_name(sha1);
-       if (returnsha1)
-               hashcpy(returnsha1, sha1);
-       if (has_sha1_file(sha1))
-               return 0;
        fd = open(filename, O_RDONLY);
        if (fd >= 0) {
                /*
@@ -2182,9 +2172,53 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
                die("unable to write sha1 file");
        free(compressed);
 
+       if (mtime) {
+               struct utimbuf utb;
+               utb.actime = mtime;
+               utb.modtime = mtime;
+               if (utime(tmpfile, &utb) < 0)
+                       warning("failed utime() on %s: %s",
+                               tmpfile, strerror(errno));
+       }
+
        return move_temp_to_file(tmpfile, filename);
 }
 
+int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+{
+       unsigned char sha1[20];
+       char hdr[32];
+       int hdrlen;
+
+       /* Normally if we have it in the pack then we do not bother writing
+        * it out into .git/objects/??/?{38} file.
+        */
+       write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+       if (returnsha1)
+               hashcpy(returnsha1, sha1);
+       if (has_sha1_file(sha1))
+               return 0;
+       return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
+}
+
+int force_object_loose(const unsigned char *sha1, time_t mtime)
+{
+       struct stat st;
+       void *buf;
+       unsigned long len;
+       enum object_type type;
+       char hdr[32];
+       int hdrlen;
+
+       if (find_sha1_file(sha1, &st))
+               return 0;
+       buf = read_packed_sha1(sha1, &type, &len);
+       if (!buf)
+               return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
+       hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
+       return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
+}
+
 /*
  * We need to unpack and recompress the object for writing
  * it out to a different file.
index be9ace6c04ce1fe7d53d85f30adab2218ec1ec65..5a5e781a15d7d9cb60797958433eca896b31ec85 100644 (file)
@@ -1,48 +1,64 @@
 #include "cache.h"
 
-int has_symlink_leading_path(const char *name, char *last_symlink)
-{
+struct pathname {
+       int len;
        char path[PATH_MAX];
-       const char *sp, *ep;
-       char *dp;
-
-       sp = name;
-       dp = path;
-
-       if (last_symlink && *last_symlink) {
-               size_t last_len = strlen(last_symlink);
-               size_t len = strlen(name);
-               if (last_len < len &&
-                   !strncmp(name, last_symlink, last_len) &&
-                   name[last_len] == '/')
-                       return 1;
-               *last_symlink = '\0';
+};
+
+/* Return matching pathname prefix length, or zero if not matching */
+static inline int match_pathname(int len, const char *name, struct pathname *match)
+{
+       int match_len = match->len;
+       return (len > match_len &&
+               name[match_len] == '/' &&
+               !memcmp(name, match->path, match_len)) ? match_len : 0;
+}
+
+static inline void set_pathname(int len, const char *name, struct pathname *match)
+{
+       if (len < PATH_MAX) {
+               match->len = len;
+               memcpy(match->path, name, len);
+               match->path[len] = 0;
        }
+}
+
+int has_symlink_leading_path(int len, const char *name)
+{
+       static struct pathname link, nonlink;
+       char path[PATH_MAX];
+       struct stat st;
+       char *sp;
+       int known_dir;
 
-       while (1) {
-               size_t len;
-               struct stat st;
+       /*
+        * See if the last known symlink cache matches.
+        */
+       if (match_pathname(len, name, &link))
+               return 1;
 
-               ep = strchr(sp, '/');
-               if (!ep)
-                       break;
-               len = ep - sp;
-               if (PATH_MAX <= dp + len - path + 2)
-                       return 0; /* new name is longer than that??? */
-               memcpy(dp, sp, len);
-               dp[len] = 0;
+       /*
+        * Get rid of the last known directory part
+        */
+       known_dir = match_pathname(len, name, &nonlink);
+
+       while ((sp = strchr(name + known_dir + 1, '/')) != NULL) {
+               int thislen = sp - name ;
+               memcpy(path, name, thislen);
+               path[thislen] = 0;
 
                if (lstat(path, &st))
                        return 0;
+               if (S_ISDIR(st.st_mode)) {
+                       set_pathname(thislen, path, &nonlink);
+                       known_dir = thislen;
+                       continue;
+               }
                if (S_ISLNK(st.st_mode)) {
-                       if (last_symlink)
-                               strcpy(last_symlink, path);
+                       set_pathname(thislen, path, &link);
                        return 1;
                }
-
-               dp[len++] = '/';
-               dp = dp + len;
-               sp = ep + 1;
+               break;
        }
        return 0;
 }
index fad67c097b08c695c57c376946f3093f487d4358..11ffd910c1b5022e0bd3aa217147269c8e9ac29e 100644 (file)
@@ -1 +1 @@
-trash
+/trash directory
index 72d7884232fbb4ce04082a778f9f952395764bf6..c6a60ab165b504393d3ecd5db1ea9d241687be05 100644 (file)
@@ -20,7 +20,7 @@ $(T):
        @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 clean:
-       $(RM) -r trash
+       $(RM) -r 'trash directory'
 
 # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
 full-svn-test:
index 73ed11bfe24edf56879d90a43c8df0813332fb08..70841a4645b2469d8263553a7ba41d4f953774f7 100644 (file)
--- a/t/README
+++ b/t/README
@@ -123,7 +123,7 @@ This test harness library does the following things:
    (or -h), it shows the test_description and exits.
 
  - Creates an empty test directory with an empty .git/objects
-   database and chdir(2) into it.  This directory is 't/trash'
+   database and chdir(2) into it.  This directory is 't/trash directory'
    if you must know, but I do not think you care.
 
  - Defines standard test helper functions for your scripts to
index 9decd2e1e81cfcba9c2b407abb176eea73d37ed9..5d3bd9dda916d454997da0015c35d0fa7d9f72c0 100644 (file)
@@ -20,12 +20,13 @@ then
 fi
 
 svnrepo=$PWD/svnrepo
+export svnrepo
 
 perl -w -e "
 use SVN::Core;
 use SVN::Repos;
 \$SVN::Core::VERSION gt '1.1.0' or exit(42);
-system(qw/svnadmin create --fs-type fsfs/, '$svnrepo') == 0 or exit(41);
+system(qw/svnadmin create --fs-type fsfs/, \$ENV{svnrepo}) == 0 or exit(41);
 " >&3 2>&4
 x=$?
 if test $x -ne 0
@@ -73,11 +74,16 @@ for d in \
 done
 
 start_httpd () {
+       repo_base_path="$1"
        if test -z "$SVN_HTTPD_PORT"
        then
                echo >&2 'SVN_HTTPD_PORT is not defined!'
                return
        fi
+       if test -z "$repo_base_path"
+       then
+               repo_base_path=svn
+       fi
 
        mkdir "$GIT_DIR"/logs
 
@@ -90,13 +96,13 @@ LockFile logs/accept.lock
 Listen 127.0.0.1:$SVN_HTTPD_PORT
 LoadModule dav_module $SVN_HTTPD_MODULE_PATH/mod_dav.so
 LoadModule dav_svn_module $SVN_HTTPD_MODULE_PATH/mod_dav_svn.so
-<Location /svn>
+<Location /$repo_base_path>
        DAV svn
        SVNPath $rawsvnrepo
 </Location>
 EOF
        "$SVN_HTTPD_PATH" -f "$GIT_DIR"/httpd.conf -k start
-       svnrepo=http://127.0.0.1:$SVN_HTTPD_PORT/svn
+       svnrepo="http://127.0.0.1:$SVN_HTTPD_PORT/$repo_base_path"
 }
 
 stop_httpd () {
index 7f206c56cf89451d09f777bcbd181fefedaabff2..a5c4436fd104e93ca74b11e11bc98238aaf83b29 100644 (file)
@@ -61,7 +61,8 @@ prepare_httpd() {
                        -new -x509 -nodes \
                        -out $HTTPD_ROOT_PATH/httpd.pem \
                        -keyout $HTTPD_ROOT_PATH/httpd.pem
-               export GIT_SSL_NO_VERIFY=t
+               GIT_SSL_NO_VERIFY=t
+               export GIT_SSL_NO_VERIFY
                HTTPD_PARA="$HTTPD_PARA -DSSL"
        else
                HTTPD_URL=http://127.0.0.1:$LIB_HTTPD_PORT
index 27b54cbb1216beef8b5f315708574825a1c81da3..690f80ab27fd9ff6d7e145a86eb22c6f87e3cd76 100755 (executable)
@@ -305,10 +305,10 @@ test_expect_success 'absolute path works as expected' '
        file="$dir"/index &&
        test "$file" = "$(test-absolute-path $dir2/index)" &&
        basename=blub &&
-       test "$dir/$basename" = $(cd .git && test-absolute-path $basename) &&
+       test "$dir/$basename" = "$(cd .git && test-absolute-path "$basename")" &&
        ln -s ../first/file .git/syml &&
        sym="$(cd first; pwd -P)"/file &&
-       test "$sym" = "$(test-absolute-path $dir2/syml)"
+       test "$sym" = "$(test-absolute-path "$dir2/syml")"
 '
 
 test_expect_success 'very long name in the index handled sanely' '
diff --git a/t/t0002-gitfile.sh b/t/t0002-gitfile.sh
new file mode 100755 (executable)
index 0000000..c5dbc72
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+test_description='.git file
+
+Verify that plumbing commands work when .git is a file
+'
+. ./test-lib.sh
+
+objpath() {
+    echo "$1" | sed -e 's|\(..\)|\1/|'
+}
+
+objck() {
+       p=$(objpath "$1")
+       if test ! -f "$REAL/objects/$p"
+       then
+               echo "Object not found: $REAL/objects/$p"
+               false
+       fi
+}
+
+
+test_expect_success 'initial setup' '
+       REAL="$(pwd)/.real" &&
+       mv .git "$REAL"
+'
+
+test_expect_success 'bad setup: invalid .git file format' '
+       echo "gitdir $REAL" >.git &&
+       if git rev-parse 2>.err
+       then
+               echo "git rev-parse accepted an invalid .git file"
+               false
+       fi &&
+       if ! grep -qe "Invalid gitfile format" .err
+       then
+               echo "git rev-parse returned wrong error"
+               false
+       fi
+'
+
+test_expect_success 'bad setup: invalid .git file path' '
+       echo "gitdir: $REAL.not" >.git &&
+       if git rev-parse 2>.err
+       then
+               echo "git rev-parse accepted an invalid .git file path"
+               false
+       fi &&
+       if ! grep -qe "Not a git repository" .err
+       then
+               echo "git rev-parse returned wrong error"
+               false
+       fi
+'
+
+test_expect_success 'final setup + check rev-parse --git-dir' '
+       echo "gitdir: $REAL" >.git &&
+       test "$REAL" = "$(git rev-parse --git-dir)"
+'
+
+test_expect_success 'check hash-object' '
+       echo "foo" >bar &&
+       SHA=$(cat bar | git hash-object -w --stdin) &&
+       objck $SHA
+'
+
+test_expect_success 'check cat-file' '
+       git cat-file blob $SHA >actual &&
+       diff -u bar actual
+'
+
+test_expect_success 'check update-index' '
+       if test -f "$REAL/index"
+       then
+               echo "Hmm, $REAL/index exists?"
+               false
+       fi &&
+       rm -f "$REAL/objects/$(objpath $SHA)" &&
+       git update-index --add bar &&
+       if ! test -f "$REAL/index"
+       then
+               echo "$REAL/index not found"
+               false
+       fi &&
+       objck $SHA
+'
+
+test_expect_success 'check write-tree' '
+       SHA=$(git write-tree) &&
+       objck $SHA
+'
+
+test_expect_success 'check commit-tree' '
+       SHA=$(echo "commit bar" | git commit-tree $SHA) &&
+       objck $SHA
+'
+
+test_expect_success 'check rev-list' '
+       echo $SHA >"$REAL/HEAD" &&
+       test "$SHA" = "$(git rev-list HEAD)"
+'
+
+test_done
index 3fbad77811e3782523e83dfdd58fe5308f237771..c5360e23d9ff1a5f31d42919eabdb2561f0d2e3d 100755 (executable)
@@ -7,6 +7,7 @@ test_description='Various filesystem issues'
 auml=`printf '\xc3\xa4'`
 aumlcdiar=`printf '\x61\xcc\x88'`
 
+case_insensitive=
 test_expect_success 'see if we expect ' '
 
        test_case=test_expect_success
@@ -17,6 +18,7 @@ test_expect_success 'see if we expect ' '
        if test "$(cat junk/CamelCase)" != good
        then
                test_case=test_expect_failure
+               case_insensitive=t
                say "will test on a case insensitive filesystem"
        fi &&
        rm -fr junk &&
@@ -32,8 +34,23 @@ test_expect_success 'see if we expect ' '
        rm -fr junk
 '
 
+if test "$case_insensitive"
+then
+test_expect_success "detection of case insensitive filesystem during repo init" '
+
+       test $(git config --bool core.ignorecase) = true
+'
+else
+test_expect_success "detection of case insensitive filesystem during repo init" '
+
+       ! git config --bool core.ignorecase >/dev/null ||
+       test $(git config --bool core.ignorecase) = false
+'
+fi
+
 test_expect_success "setup case tests" '
 
+       git config core.ignorecase true &&
        touch camelcase &&
        git add camelcase &&
        git commit -m "initial" &&
@@ -55,11 +72,23 @@ $test_case 'rename (case change)' '
 
 $test_case 'merge (case change)' '
 
+       rm -f CamelCase &&
+       rm -f camelcase &&
        git reset --hard initial &&
        git merge topic
 
 '
 
+$test_case 'add (with different case)' '
+
+       git reset --hard initial &&
+       rm camelcase &&
+       echo 1 >CamelCase &&
+       git add CamelCase &&
+       test $(git-ls-files | grep -i camelcase | wc -l) = 1
+
+'
+
 test_expect_success "setup unicode normalization tests" '
 
   test_create_repo unicode &&
index b9cef3422c3ba1392b32ec66f72dc4b7c34f4a43..fc386ba033ac165a5f4a9fca0c6c6f5db49a314e 100755 (executable)
@@ -21,7 +21,7 @@ LF='
 '
 
 test_expect_success 'update-index and ls-files' '
-       cd $HERE &&
+       cd "$HERE" &&
        git update-index --add one &&
        case "`git ls-files`" in
        one) echo ok one ;;
@@ -41,7 +41,7 @@ test_expect_success 'update-index and ls-files' '
 '
 
 test_expect_success 'cat-file' '
-       cd $HERE &&
+       cd "$HERE" &&
        two=`git ls-files -s dir/two` &&
        two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` &&
        echo "$two" &&
@@ -54,7 +54,7 @@ test_expect_success 'cat-file' '
 rm -f actual dir/actual
 
 test_expect_success 'diff-files' '
-       cd $HERE &&
+       cd "$HERE" &&
        echo a >>one &&
        echo d >>dir/two &&
        case "`git diff-files --name-only`" in
@@ -74,7 +74,7 @@ test_expect_success 'diff-files' '
 '
 
 test_expect_success 'write-tree' '
-       cd $HERE &&
+       cd "$HERE" &&
        top=`git write-tree` &&
        echo $top &&
        cd dir &&
@@ -84,7 +84,7 @@ test_expect_success 'write-tree' '
 '
 
 test_expect_success 'checkout-index' '
-       cd $HERE &&
+       cd "$HERE" &&
        git checkout-index -f -u one &&
        cmp one original.one &&
        cd dir &&
@@ -93,7 +93,7 @@ test_expect_success 'checkout-index' '
 '
 
 test_expect_success 'read-tree' '
-       cd $HERE &&
+       cd "$HERE" &&
        rm -f one dir/two &&
        tree=`git write-tree` &&
        git read-tree --reset -u "$tree" &&
@@ -107,27 +107,27 @@ test_expect_success 'read-tree' '
 '
 
 test_expect_success 'no file/rev ambiguity check inside .git' '
-       cd $HERE &&
+       cd "$HERE" &&
        git commit -a -m 1 &&
-       cd $HERE/.git &&
+       cd "$HERE"/.git &&
        git show -s HEAD
 '
 
 test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-       cd $HERE &&
+       cd "$HERE" &&
        git clone -s --bare .git foo.git &&
        cd foo.git && GIT_DIR=. git show -s HEAD
 '
 
 # This still does not work as it should...
 : test_expect_success 'no file/rev ambiguity check inside a bare repo' '
-       cd $HERE &&
+       cd "$HERE" &&
        git clone -s --bare .git foo.git &&
        cd foo.git && git show -s HEAD
 '
 
 test_expect_success 'detection should not be fooled by a symlink' '
-       cd $HERE &&
+       cd "$HERE" &&
        rm -fr foo.git &&
        git clone -s .git another &&
        ln -s another yetanother &&
index 99985dcd79f12d680c51e8f64d61c47a70b43f36..f366b53fb671a5ef52c34516bc4ab3869a142d8e 100755 (executable)
@@ -34,4 +34,10 @@ test_expect_success 'add key in different section' '
        check section2.key bar
 '
 
+SECTION="test.q\"s\\sq'sp e.key"
+test_expect_success 'make sure git-config escapes section names properly' '
+       git config "$SECTION" bar &&
+       check "$SECTION" bar
+'
+
 test_done
index 38a2bf09af286bf681deadb9cdaec80175d13e0c..85da4caa7ed1b8bcaca7b21e218f2d1839d2db82 100755 (executable)
@@ -51,8 +51,9 @@ test_rev_parse 'core.bare undefined' false false true
 
 mkdir work || exit 1
 cd work || exit 1
-export GIT_DIR=../.git
-export GIT_CONFIG="$(pwd)"/../.git/config
+GIT_DIR=../.git
+GIT_CONFIG="$(pwd)"/../.git/config
+export GIT_DIR GIT_CONFIG
 
 git config core.bare false
 test_rev_parse 'GIT_DIR=../.git, core.bare = false' false false true ''
@@ -64,8 +65,8 @@ git config --unset core.bare
 test_rev_parse 'GIT_DIR=../.git, core.bare undefined' false false true ''
 
 mv ../.git ../repo.git || exit 1
-export GIT_DIR=../repo.git
-export GIT_CONFIG="$(pwd)"/../repo.git/config
+GIT_DIR=../repo.git
+GIT_CONFIG="$(pwd)"/../repo.git/config
 
 git config core.bare false
 test_rev_parse 'GIT_DIR=../repo.git, core.bare = false' false false true ''
index 7ee3820ce97b6c26c9465685a7bc64a962aad3cb..2ee88d8a069288d0d9f6931231162e04d6b0917a 100755 (executable)
@@ -32,24 +32,25 @@ mkdir -p work/sub/dir || exit 1
 mv .git repo.git || exit 1
 
 say "core.worktree = relative path"
-export GIT_DIR=repo.git
-export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+GIT_DIR=repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+export GIT_DIR GIT_CONFIG
 unset GIT_WORK_TREE
 git config core.worktree ../work
 test_rev_parse 'outside'      false false false
 cd work || exit 1
-export GIT_DIR=../repo.git
-export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+GIT_DIR=../repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
 test_rev_parse 'inside'       false false true ''
 cd sub/dir || exit 1
-export GIT_DIR=../../../repo.git
-export GIT_CONFIG="$(pwd)"/$GIT_DIR/config
+GIT_DIR=../../../repo.git
+GIT_CONFIG="$(pwd)"/$GIT_DIR/config
 test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
 say "core.worktree = absolute path"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
 git config core.worktree "$(pwd)/work"
 test_rev_parse 'outside'      false false false
 cd work || exit 1
@@ -59,25 +60,26 @@ test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
 say "GIT_WORK_TREE=relative path (override core.worktree)"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
 git config core.worktree non-existent
-export GIT_WORK_TREE=work
+GIT_WORK_TREE=work
+export GIT_WORK_TREE
 test_rev_parse 'outside'      false false false
 cd work || exit 1
-export GIT_WORK_TREE=.
+GIT_WORK_TREE=.
 test_rev_parse 'inside'       false false true ''
 cd sub/dir || exit 1
-export GIT_WORK_TREE=../..
+GIT_WORK_TREE=../..
 test_rev_parse 'subdirectory' false false true sub/dir/
 cd ../../.. || exit 1
 
 mv work repo.git/work
 
 say "GIT_WORK_TREE=absolute path, work tree below git dir"
-export GIT_DIR=$(pwd)/repo.git
-export GIT_CONFIG=$GIT_DIR/config
-export GIT_WORK_TREE=$(pwd)/repo.git/work
+GIT_DIR=$(pwd)/repo.git
+GIT_CONFIG=$GIT_DIR/config
+GIT_WORK_TREE=$(pwd)/repo.git/work
 test_rev_parse 'outside'              false false false
 cd repo.git || exit 1
 test_rev_parse 'in repo.git'              false true  false
diff --git a/t/t1503-rev-parse-verify.sh b/t/t1503-rev-parse-verify.sh
new file mode 100755 (executable)
index 0000000..95244c9
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='test git rev-parse --verify'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_line_into_file()
+{
+    _line=$1
+    _file=$2
+
+    if [ -f "$_file" ]; then
+        echo "$_line" >> $_file || return $?
+        MSG="Add <$_line> into <$_file>."
+    else
+        echo "$_line" > $_file || return $?
+        git add $_file || return $?
+        MSG="Create file <$_file> with <$_line> inside."
+    fi
+
+    test_tick
+    git-commit --quiet -m "$MSG" $_file
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+
+test_expect_success 'set up basic repo with 1 file (hello) and 4 commits' '
+       add_line_into_file "1: Hello World" hello &&
+       HASH1=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "2: A new day for git" hello &&
+       HASH2=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "3: Another new day for git" hello &&
+       HASH3=$(git rev-parse --verify HEAD) &&
+       add_line_into_file "4: Ciao for now" hello &&
+       HASH4=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'works with one good rev' '
+       rev_hash1=$(git rev-parse --verify $HASH1) &&
+       test "$rev_hash1" = "$HASH1" &&
+       rev_hash2=$(git rev-parse --verify $HASH2) &&
+       test "$rev_hash2" = "$HASH2" &&
+       rev_hash3=$(git rev-parse --verify $HASH3) &&
+       test "$rev_hash3" = "$HASH3" &&
+       rev_hash4=$(git rev-parse --verify $HASH4) &&
+       test "$rev_hash4" = "$HASH4" &&
+       rev_master=$(git rev-parse --verify master) &&
+       test "$rev_master" = "$HASH4" &&
+       rev_head=$(git rev-parse --verify HEAD) &&
+       test "$rev_head" = "$HASH4"
+'
+
+test_expect_success 'fails with any bad rev or many good revs' '
+       test_must_fail git rev-parse --verify 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify foo 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify HEAD bar 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify baz HEAD 2>error &&
+       grep "single revision" error &&
+       test_must_fail git rev-parse --verify $HASH2 HEAD 2>error &&
+       grep "single revision" error
+'
+
+test_expect_success 'fails silently when using -q' '
+       test_must_fail git rev-parse --verify --quiet 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse -q --verify foo 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse --verify -q HEAD bar 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse --quiet --verify baz HEAD 2>error &&
+       test -z "$(cat error)" &&
+       test_must_fail git rev-parse -q --verify $HASH2 HEAD 2>error &&
+       test -z "$(cat error)"
+'
+
+test_expect_success 'no stdout output on error' '
+       test -z "$(git rev-parse --verify)" &&
+       test -z "$(git rev-parse --verify foo)" &&
+       test -z "$(git rev-parse --verify baz HEAD)" &&
+       test -z "$(git rev-parse --verify HEAD bar)" &&
+       test -z "$(git rev-parse --verify $HASH2 HEAD)"
+'
+
+test_expect_success 'use --default' '
+       git rev-parse --verify --default master &&
+       git rev-parse --verify --default master HEAD &&
+       git rev-parse --default master --verify &&
+       git rev-parse --default master --verify HEAD &&
+       git rev-parse --verify HEAD --default master &&
+       test_must_fail git rev-parse --verify foo --default master &&
+       test_must_fail git rev-parse --default HEAD --verify bar &&
+       test_must_fail git rev-parse --verify --default HEAD baz &&
+       test_must_fail git rev-parse --default foo --verify &&
+       test_must_fail git rev-parse --verify --default bar
+'
+
+test_done
index 2b21b1070d6b8f864aee0049d5cb2dccef0daa46..4261e9641e00fb3b543384b6a8dbbcc1a214b598 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success setup '
 '
 
 test_expect_success clone '
-       git clone file://`pwd`/.git cloned &&
+       git clone "file://$(pwd)/.git" cloned &&
        (git rev-parse HEAD; git ls-files -s) >expected &&
        (
                cd cloned &&
index cb5f7a444175938c8a9cc4c029b41b7325b91618..8d8768688d8dde3637a50ece28fb8720b7612ddd 100755 (executable)
@@ -224,4 +224,238 @@ test_expect_success 'avoid ambiguous track' '
        test -z "$(git config branch.all1.merge)"
 '
 
+test_expect_success 'autosetuprebase local on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase local &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase &&
+       git branch --track myr1 mybase &&
+       test "$(git config branch.myr1.remote)" = . &&
+       test "$(git config branch.myr1.merge)" = refs/heads/mybase &&
+       test "$(git config branch.myr1.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase always on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase always &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase2 &&
+       git branch --track myr2 mybase &&
+       test "$(git config branch.myr2.remote)" = . &&
+       test "$(git config branch.myr2.merge)" = refs/heads/mybase &&
+       test "$(git config branch.myr2.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase remote on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase remote &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase3 &&
+       git branch --track myr3 mybase2 &&
+       test "$(git config branch.myr3.remote)" = . &&
+       test "$(git config branch.myr3.merge)" = refs/heads/mybase2 &&
+       ! test "$(git config branch.myr3.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase never on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase never &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase4 &&
+       git branch --track myr4 mybase2 &&
+       test "$(git config branch.myr4.remote)" = . &&
+       test "$(git config branch.myr4.merge)" = refs/heads/mybase2 &&
+       ! test "$(git config branch.myr4.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase local on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase local &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr5 local/master &&
+       test "$(git config branch.myr5.remote)" = local &&
+       test "$(git config branch.myr5.merge)" = refs/heads/master &&
+       ! test "$(git config branch.myr5.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase never on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase never &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr6 local/master &&
+       test "$(git config branch.myr6.remote)" = local &&
+       test "$(git config branch.myr6.merge)" = refs/heads/master &&
+       ! test "$(git config branch.myr6.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase remote on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase remote &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr7 local/master &&
+       test "$(git config branch.myr7.remote)" = local &&
+       test "$(git config branch.myr7.merge)" = refs/heads/master &&
+       test "$(git config branch.myr7.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase always on a tracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       git config branch.autosetuprebase remote &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr8 local/master &&
+       test "$(git config branch.myr8.remote)" = local &&
+       test "$(git config branch.myr8.merge)" = refs/heads/master &&
+       test "$(git config branch.myr8.rebase)" = true
+'
+
+test_expect_success 'autosetuprebase unconfigured on a tracked remote branch' '
+       git config --unset branch.autosetuprebase &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --track myr9 local/master &&
+       test "$(git config branch.myr9.remote)" = local &&
+       test "$(git config branch.myr9.merge)" = refs/heads/master &&
+       test "z$(git config branch.myr9.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on a tracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/o || git-fetch local) &&
+       git branch mybase10 &&
+       git branch --track myr10 mybase2 &&
+       test "$(git config branch.myr10.remote)" = . &&
+       test "$(git config branch.myr10.merge)" = refs/heads/mybase2 &&
+       test "z$(git config branch.myr10.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on untracked local branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr11 mybase2 &&
+       test "z$(git config branch.myr11.remote)" = z &&
+       test "z$(git config branch.myr11.merge)" = z &&
+       test "z$(git config branch.myr11.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase unconfigured on untracked remote branch' '
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr12 local/master &&
+       test "z$(git config branch.myr12.remote)" = z &&
+       test "z$(git config branch.myr12.merge)" = z &&
+       test "z$(git config branch.myr12.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase never on an untracked local branch' '
+       git config branch.autosetuprebase never &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr13 mybase2 &&
+       test "z$(git config branch.myr13.remote)" = z &&
+       test "z$(git config branch.myr13.merge)" = z &&
+       test "z$(git config branch.myr13.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase local on an untracked local branch' '
+       git config branch.autosetuprebase local &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr14 mybase2 &&
+       test "z$(git config branch.myr14.remote)" = z &&
+       test "z$(git config branch.myr14.merge)" = z &&
+       test "z$(git config branch.myr14.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase remote on an untracked local branch' '
+       git config branch.autosetuprebase remote &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr15 mybase2 &&
+       test "z$(git config branch.myr15.remote)" = z &&
+       test "z$(git config branch.myr15.merge)" = z &&
+       test "z$(git config branch.myr15.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase always on an untracked local branch' '
+       git config branch.autosetuprebase always &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr16 mybase2 &&
+       test "z$(git config branch.myr16.remote)" = z &&
+       test "z$(git config branch.myr16.merge)" = z &&
+       test "z$(git config branch.myr16.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase never on an untracked remote branch' '
+       git config branch.autosetuprebase never &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr17 local/master &&
+       test "z$(git config branch.myr17.remote)" = z &&
+       test "z$(git config branch.myr17.merge)" = z &&
+       test "z$(git config branch.myr17.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase local on an untracked remote branch' '
+       git config branch.autosetuprebase local &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr18 local/master &&
+       test "z$(git config branch.myr18.remote)" = z &&
+       test "z$(git config branch.myr18.merge)" = z &&
+       test "z$(git config branch.myr18.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase remote on an untracked remote branch' '
+       git config branch.autosetuprebase remote &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr19 local/master &&
+       test "z$(git config branch.myr19.remote)" = z &&
+       test "z$(git config branch.myr19.merge)" = z &&
+       test "z$(git config branch.myr19.rebase)" = z
+'
+
+test_expect_success 'autosetuprebase always on an untracked remote branch' '
+       git config branch.autosetuprebase always &&
+       git config remote.local.url . &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
+       (git show-ref -q refs/remotes/local/master || git-fetch local) &&
+       git branch --no-track myr20 local/master &&
+       test "z$(git config branch.myr20.remote)" = z &&
+       test "z$(git config branch.myr20.merge)" = z &&
+       test "z$(git config branch.myr20.rebase)" = z
+'
+
+test_expect_success 'detect misconfigured autosetuprebase (bad value)' '
+       git config branch.autosetuprebase garbage &&
+       test_must_fail git branch
+'
+
+test_expect_success 'detect misconfigured autosetuprebase (no value)' '
+       git config --unset branch.autosetuprebase &&
+       echo "[branch] autosetuprebase" >> .git/config &&
+       test_must_fail git branch &&
+       git config --unset branch.autosetuprebase
+'
+
 test_done
index b4cf628d225d380d6c0bf73dee4c3e9df0cadb41..f86f4bc5ebcc0e36ddb4071a6aeb855e1039faa6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='branch --contains <commit>'
+test_description='branch --contains <commit>, --merged, and --no-merged'
 
 . ./test-lib.sh
 
@@ -55,4 +55,44 @@ test_expect_success 'branch --contains=side' '
 
 '
 
+test_expect_success 'side: branch --merged' '
+
+       git branch --merged >actual &&
+       {
+               echo "  master" &&
+               echo "* side"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'side: branch --no-merged' '
+
+       git branch --no-merged >actual &&
+       >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'master: branch --merged' '
+
+       git checkout master &&
+       git branch --merged >actual &&
+       {
+               echo "* master"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'master: branch --no-merged' '
+
+       git branch --no-merged >actual &&
+       {
+               echo "  side"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
 test_done
index 496f4ec17217769228954116528b0fc0c1ef2a62..91bb5e1d9eea0b2f1ff7a1120d97ca2876a8f277 100755 (executable)
@@ -9,7 +9,8 @@ This test runs git rebase and checks that the author information is not lost.
 '
 . ./test-lib.sh
 
-export GIT_AUTHOR_EMAIL=bogus_email_address
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
 
 test_expect_success \
     'prepare repository with topic branches' \
@@ -44,13 +45,13 @@ test_expect_success 'rebase against master' '
 
 test_expect_success \
     'the rebase operation should not have destroyed author information' \
-    '! git log | grep "Author:" | grep "<>"'
+    '! (git log | grep "Author:" | grep "<>")'
 
 test_expect_success 'rebase after merge master' '
      git reset --hard topic &&
      git merge master &&
      git rebase master &&
-     ! git show | grep "^Merge:"
+     ! (git show | grep "^Merge:")
 '
 
 test_expect_success 'rebase of history with merges is linearized' '
index 9cf873f7ebfa8f8a4fd215cbbb468f955d6a47e3..b9e3dbd242cc92710d1c689ca5412619e46ee63f 100755 (executable)
@@ -91,9 +91,8 @@ for line in $FAKE_LINES; do
 done
 EOF
 
+test_set_editor "$(pwd)/fake-editor.sh"
 chmod a+x fake-editor.sh
-VISUAL="$(pwd)/fake-editor.sh"
-export VISUAL
 
 test_expect_success 'no changes are a nop' '
        git rebase -i F &&
index 37944c39a3be5e8d2db59faa5609d857fe902199..1777ffe8a22d8f9dd28efadd3cb9ea65be9c2178 100755 (executable)
@@ -4,7 +4,13 @@ test_description='git rebase --abort tests'
 
 . ./test-lib.sh
 
+### Test that we handle space characters properly
+work_dir="$(pwd)/test dir"
+
 test_expect_success setup '
+       mkdir -p "$work_dir" &&
+       cd "$work_dir" &&
+       git init &&
        echo a > a &&
        git add a &&
        git commit -m a &&
@@ -28,32 +34,35 @@ testrebase() {
        dotest=$2
 
        test_expect_success "rebase$type --abort" '
+               cd "$work_dir" &&
                # Clean up the state from the previous one
-               git reset --hard pre-rebase
-               test_must_fail git rebase'"$type"' master &&
-               test -d '$dotest' &&
+               git reset --hard pre-rebase &&
+               test_must_fail git rebase$type master &&
+               test -d "$dotest" &&
                git rebase --abort &&
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-               test ! -d '$dotest'
+               test ! -d "$dotest"
        '
 
        test_expect_success "rebase$type --abort after --skip" '
+               cd "$work_dir" &&
                # Clean up the state from the previous one
-               git reset --hard pre-rebase
-               test_must_fail git rebase'"$type"' master &&
-               test -d '$dotest' &&
+               git reset --hard pre-rebase &&
+               test_must_fail git rebase$type master &&
+               test -d "$dotest" &&
                test_must_fail git rebase --skip &&
                test $(git rev-parse HEAD) = $(git rev-parse master) &&
                git-rebase --abort &&
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-               test ! -d '$dotest'
+               test ! -d "$dotest"
        '
 
        test_expect_success "rebase$type --abort after --continue" '
+               cd "$work_dir" &&
                # Clean up the state from the previous one
-               git reset --hard pre-rebase
-               test_must_fail git rebase'"$type"' master &&
-               test -d '$dotest' &&
+               git reset --hard pre-rebase &&
+               test_must_fail git rebase$type master &&
+               test -d "$dotest" &&
                echo c > a &&
                echo d >> a &&
                git add a &&
@@ -61,7 +70,7 @@ testrebase() {
                test $(git rev-parse HEAD) != $(git rev-parse master) &&
                git rebase --abort &&
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
-               test ! -d '$dotest'
+               test ! -d "$dotest"
        '
 }
 
index d0a440feba5f501a2320d52b36d1bb51ca8944dd..4911c48378a137471d2ad56747ceed11d0115be5 100755 (executable)
@@ -10,7 +10,8 @@ checks that git cherry only returns the second patch in the local branch
 '
 . ./test-lib.sh
 
-export GIT_AUTHOR_EMAIL=bogus_email_address
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
 
 test_expect_success \
     'prepare repository with topic branch, and check cherry finds the 2 patches from there' \
index 287e058e3766df129dcde82aeddecac59b46e2a6..e83fa1f6894dd13eb11cae6afb8ecbd3dcb999db 100755 (executable)
@@ -81,17 +81,17 @@ test_expect_success '.gitignore test setup' '
 
 test_expect_success '.gitignore is honored' '
        git add . &&
-       ! git ls-files | grep "\\.ig"
+       ! (git ls-files | grep "\\.ig")
 '
 
 test_expect_success 'error out when attempting to add ignored ones without -f' '
        ! git add a.?? &&
-       ! git ls-files | grep "\\.ig"
+       ! (git ls-files | grep "\\.ig")
 '
 
 test_expect_success 'error out when attempting to add ignored ones without -f' '
        ! git add d.?? &&
-       ! git ls-files | grep "\\.ig"
+       ! (git ls-files | grep "\\.ig")
 '
 
 test_expect_success 'add ignored ones with -f' '
@@ -179,4 +179,47 @@ test_expect_success 'git add --refresh' '
        test -z "`git diff-index HEAD -- foo`"
 '
 
+test_expect_success 'git add should fail atomically upon an unreadable file' '
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose . &&
+       ! ( git ls-files foo1 | grep foo1 )
+'
+
+rm -f foo2
+
+test_expect_success 'git add --ignore-errors' '
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose --ignore-errors . &&
+       git ls-files foo1 | grep foo1
+'
+
+rm -f foo2
+
+test_expect_success 'git add (add.ignore-errors)' '
+       git config add.ignore-errors 1 &&
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose . &&
+       git ls-files foo1 | grep foo1
+'
+rm -f foo2
+
+test_expect_success 'git add (add.ignore-errors = false)' '
+       git config add.ignore-errors 0 &&
+       git reset --hard &&
+       date >foo1 &&
+       date >foo2 &&
+       chmod 0 foo2 &&
+       test_must_fail git add --verbose . &&
+       ! ( git ls-files foo1 | grep foo1 )
+'
+
 test_done
index f15be93e7709acbb517bb85020d4d6b94a50f725..fae64eae9f9ff9b34b935e8556df61d2093425cf 100755 (executable)
@@ -66,6 +66,11 @@ test_expect_success 'revert works (commit)' '
        grep "unchanged *+3/-0 file" output
 '
 
+if test "$(git config --bool core.filemode)" = false
+then
+    say 'skipping filemode tests (filesystem does not properly support modes)'
+else
+
 test_expect_success 'patch does not affect mode' '
        git reset --hard &&
        echo content >>file &&
@@ -84,5 +89,7 @@ test_expect_success 'stage mode but not hunk' '
        git diff          file | grep "+content"
 '
 
+fi
+# end of tests disabled when filemode is not usable
 
 test_done
index 1fd3fb74d763026d4e20a38fe9d2ed48dda74fed..ba6679c6e4032bb12e4206226f95770946ece8cc 100755 (executable)
@@ -50,4 +50,11 @@ test_expect_success 'git diff-files --raw' '
        test_cmp expect actual.files
 '
 
+test_expect_success 'git diff (empty submodule dir)' '
+       : >empty &&
+       rm -rf sub/* sub/.git &&
+       git diff > actual.empty &&
+       test_cmp empty actual.empty
+'
+
 test_done
index fa62b6aa21f5d8f8774b7f2af3a4cd60b8c0d761..9b0baac8db4b342206d37ab5eaff0b7a09d33967 100755 (executable)
@@ -67,10 +67,10 @@ test_expect_success \
 
 test_expect_success \
     'validate file modification time' \
-    'TZ=GMT $TAR tvf b.tar a/a |
-     awk \{print\ \$4,\ \(length\(\$5\)\<7\)\ ?\ \$5\":00\"\ :\ \$5\} \
-     >b.mtime &&
-     echo "2005-05-27 22:00:00" >expected.mtime &&
+    'mkdir extract &&
+     $TAR xf b.tar -C extract a/a &&
+     perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime &&
+     echo "1117231200" >expected.mtime &&
      diff expected.mtime b.mtime'
 
 test_expect_success \
index b88b5bbd022bb3dd836d00f88a8e486c00285103..09fd91767297a8c59edb08944740bbcf08f8a7c4 100755 (executable)
@@ -65,7 +65,7 @@ test_expect_success \
 
 have_64bits=
 if msg=$(git verify-pack -v "test-3-${pack3}.pack" 2>&1) ||
-       ! echo "$msg" | grep "pack too large .* off_t"
+       ! (echo "$msg" | grep "pack too large .* off_t")
 then
        have_64bits=t
 else
index 788b4a5aae17d33ad688446bb60973952c3ca918..140e8745811b9f25abfa2c9c24a0569f8dd39c66 100755 (executable)
@@ -31,7 +31,7 @@ add () {
        sec=$(($sec+1))
        commit=$(echo "$text" | GIT_AUTHOR_DATE=$sec \
                git commit-tree $tree $parents 2>>log2.txt)
-       export $name=$commit
+       eval "$name=$commit; export $name"
        echo $commit > .git/refs/heads/$branch
        eval ${branch}TIP=$commit
 }
@@ -129,7 +129,7 @@ pull_to_client 2nd "B" $((64*3))
 
 pull_to_client 3rd "A" $((1*3)) # old fails
 
-test_expect_success "clone shallow" "git-clone --depth 2 file://`pwd`/. shallow"
+test_expect_success "clone shallow" 'git-clone --depth 2 "file://$(pwd)/." shallow'
 
 (cd shallow; git count-objects -v) > count.shallow
 
index c0dc94909b694439e389ceeeecec82ed36864ec0..1dd8eed5bb3cb0f320a8f0780452e52fa7d8da16 100755 (executable)
@@ -17,7 +17,7 @@ test_expect_success setup '
                git show-ref -d | sed -e "s/ /  /"
        ) >expected.all &&
 
-       git remote add self $(pwd)/.git
+       git remote add self "$(pwd)/.git"
 
 '
 
index 53e47e1198763b878d629acb71df768ea479eb3c..c5c59335f36d0d8f6a26ee606fd605e74e2d1e33 100755 (executable)
@@ -105,7 +105,7 @@ test_expect_success 'fetch with insteadOf' '
        (
                TRASH=$(pwd)/ &&
                cd testrepo &&
-               git config url.$TRASH.insteadOf trash/
+               git config "url.$TRASH.insteadOf" trash/ &&
                git config remote.up.url trash/. &&
                git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
                git fetch up &&
@@ -145,8 +145,8 @@ test_expect_success 'push with wildcard' '
 
 test_expect_success 'push with insteadOf' '
        mk_empty &&
-       TRASH=$(pwd)/ &&
-       git config url.$TRASH.insteadOf trash/ &&
+       TRASH="$(pwd)/" &&
+       git config "url./$TRASH/.insteadOf" trash/ &&
        git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
        (
                cd testrepo &&
index ed3fec192a8da73da269af03746fa7e9eda65d52..ea49dedbf8867694d83cd550c8212ff107361920 100755 (executable)
@@ -25,7 +25,7 @@ mk_repo_pair () {
        (
                cd master &&
                git init &&
-               git config remote.up.url ../mirror
+               git remote add $1 up ../mirror
        )
 }
 
@@ -225,4 +225,43 @@ test_expect_success 'push mirror adds, updates and removes tags together' '
 
 '
 
+test_expect_success 'remote.foo.mirror adds and removes branches' '
+
+       mk_repo_pair --mirror &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git branch keep master &&
+               git branch remove master &&
+               git push up &&
+               git branch -D remove
+               git push up
+       ) &&
+       (
+               cd mirror &&
+               git show-ref -s --verify refs/heads/keep &&
+               invert git show-ref -s --verify refs/heads/remove
+       )
+
+'
+
+test_expect_success 'remote.foo.mirror=no has no effect' '
+
+       mk_repo_pair &&
+       (
+               cd master &&
+               echo one >foo && git add foo && git commit -m one &&
+               git config --add remote.up.mirror no &&
+               git branch keep master &&
+               git push --mirror up &&
+               git branch -D keep &&
+               git push up
+       ) &&
+       (
+               cd mirror &&
+               git show-ref -s --verify refs/heads/keep
+       )
+
+'
+
 test_done
index 9484129ca5aafab369a6ee1f7a1264d568f8cb44..997b2db827c4f37512c6b5d2f861e12105e2a32d 100755 (executable)
@@ -92,4 +92,22 @@ test_expect_success '--rebase with rebased upstream' '
 
 '
 
+test_expect_success 'pull --rebase dies early with dirty working directory' '
+
+       git update-ref refs/remotes/me/copy copy^ &&
+       COPY=$(git rev-parse --verify me/copy) &&
+       git rebase --onto $COPY copy &&
+       git config branch.to-rebase.remote me &&
+       git config branch.to-rebase.merge refs/heads/copy &&
+       git config branch.to-rebase.rebase true &&
+       echo dirty >> file &&
+       git add file &&
+       test_must_fail git pull &&
+       test $COPY = $(git rev-parse --verify me/copy) &&
+       git checkout HEAD -- file &&
+       git pull &&
+       test $COPY != $(git rev-parse --verify me/copy)
+
+'
+
 test_done
index b6a54867b491ba67e4813fd492a1a8cc16959a21..e5619a9f5c9aae8c9565dd6937c20ce6401d26d4 100755 (executable)
@@ -51,7 +51,7 @@ diff expected current'
 cd "$base_dir"
 
 test_expect_success 'cloning with reference (no -l -s)' \
-'git clone --reference B file://`pwd`/A D'
+'git clone --reference B "file://$(pwd)/A" D'
 
 cd "$base_dir"
 
index 910ccb4fff561360ba7060ab8df71c0349c4207a..ef7127c1b3943a494692ac8027ec321608a31b9c 100755 (executable)
@@ -81,9 +81,9 @@ test_valid_repo'
 cd "$base_dir"
 
 test_expect_success 'breaking of loops' \
-"echo '$base_dir/B/.git/objects' >> '$base_dir'/A/.git/objects/info/alternates&&
+'echo "$base_dir"/B/.git/objects >> "$base_dir"/A/.git/objects/info/alternates&&
 cd C &&
-test_valid_repo"
+test_valid_repo'
 
 cd "$base_dir"
 
index c0baaa536079b1213d83bbf234ac688567c95237..f55627b641682e72d58a2282639ca589b38fa744 100755 (executable)
@@ -49,13 +49,15 @@ as_author()
        shift 1
         _save=$GIT_AUTHOR_EMAIL
 
-       export GIT_AUTHOR_EMAIL="$_author"
+       GIT_AUTHOR_EMAIL="$_author"
+       export GIT_AUTHOR_EMAIL
        "$@"
        if test -z "$_save"
        then
                unset GIT_AUTHOR_EMAIL
        else
-               export GIT_AUTHOR_EMAIL="$_save"
+               GIT_AUTHOR_EMAIL="$_save"
+               export GIT_AUTHOR_EMAIL
        fi
 }
 
@@ -69,7 +71,8 @@ on_committer_date()
 {
     _date=$1
     shift 1
-    export GIT_COMMITTER_DATE="$_date"
+    GIT_COMMITTER_DATE="$_date"
+    export GIT_COMMITTER_DATE
     "$@"
     unset GIT_COMMITTER_DATE
 }
index 96f3d355301cf9e2daf58d219c3cf6cbdd118d82..b6e57b2426728cce308a57315247cd2a66cabf4a 100755 (executable)
@@ -13,10 +13,11 @@ T=$(git write-tree)
 M=1130000000
 Z=+0000
 
-export GIT_COMMITTER_EMAIL=git@comm.iter.xz
-export GIT_COMMITTER_NAME='C O Mmiter'
-export GIT_AUTHOR_NAME='A U Thor'
-export GIT_AUTHOR_EMAIL=git@au.thor.xz
+GIT_COMMITTER_EMAIL=git@comm.iter.xz
+GIT_COMMITTER_NAME='C O Mmiter'
+GIT_AUTHOR_NAME='A U Thor'
+GIT_AUTHOR_EMAIL=git@au.thor.xz
+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 
 doit() {
        OFFSET=$1; shift
index 5e3e5445c730140ad6c2fa9cc5bda4a7029e7fbf..933f5679831b1df3baac8164094b204a09fd7f16 100755 (executable)
@@ -284,6 +284,31 @@ test_expect_success 'bisect starting with a detached HEAD' '
 
 '
 
+test_expect_success 'bisect refuses to start if branch bisect exists' '
+       git bisect reset &&
+       git branch bisect &&
+       test_must_fail git bisect start &&
+       git branch -d bisect &&
+       git checkout -b bisect &&
+       test_must_fail git bisect start &&
+       git checkout master &&
+       git branch -d bisect
+'
+
+test_expect_success 'bisect refuses to start if branch new-bisect exists' '
+       git bisect reset &&
+       git branch new-bisect &&
+       test_must_fail git bisect start &&
+       git branch -d new-bisect
+'
+
+test_expect_success 'bisect errors out if bad and good are mistaken' '
+       git bisect reset &&
+       test_must_fail git bisect start $HASH2 $HASH4 2> rev_list_error &&
+       grep "mistake good and bad" rev_list_error &&
+       git bisect reset
+'
+
 #
 #
 test_done
diff --git a/t/t6032-merge-large-rename.sh b/t/t6032-merge-large-rename.sh
new file mode 100755 (executable)
index 0000000..eac5eba
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='merging with large rename matrix'
+. ./test-lib.sh
+
+count() {
+       i=1
+       while test $i -le $1; do
+               echo $i
+               i=$(($i + 1))
+       done
+}
+
+test_expect_success 'setup (initial)' '
+       touch file &&
+       git add . &&
+       git commit -m initial &&
+       git tag initial
+'
+
+make_text() {
+       echo $1: $2
+       for i in `count 20`; do
+               echo $1: $i
+       done
+       echo $1: $3
+}
+
+test_rename() {
+       test_expect_success "rename ($1, $2)" '
+       n='$1'
+       expect='$2'
+       git checkout -f master &&
+       git branch -D test$n || true &&
+       git reset --hard initial &&
+       for i in $(count $n); do
+               make_text $i initial initial >$i
+       done &&
+       git add . &&
+       git commit -m add=$n &&
+       for i in $(count $n); do
+               make_text $i changed initial >$i
+       done &&
+       git commit -a -m change=$n &&
+       git checkout -b test$n HEAD^ &&
+       for i in $(count $n); do
+               git rm $i
+               make_text $i initial changed >$i.moved
+       done &&
+       git add . &&
+       git commit -m change+rename=$n &&
+       case "$expect" in
+               ok) git merge master ;;
+                *) test_must_fail git merge master ;;
+       esac
+       '
+}
+
+test_rename 5 ok
+
+test_expect_success 'set diff.renamelimit to 4' '
+       git config diff.renamelimit 4
+'
+test_rename 4 ok
+test_rename 5 fail
+
+test_expect_success 'set merge.renamelimit to 5' '
+       git config merge.renamelimit 5
+'
+test_rename 5 ok
+test_rename 6 fail
+
+test_done
index 526d7d1c4422e342c7257e260626dac3dda36a3a..c9bf6fdba3c3495b5215394d568fd8ac5b4f6daf 100755 (executable)
@@ -82,14 +82,14 @@ test_expect_success 'merge-msg test #1' '
        git diff actual expected
 '
 
-cat >expected <<\EOF
-Merge branch 'left' of ../trash
+cat >expected <<EOF
+Merge branch 'left' of ../$test
 EOF
 
 test_expect_success 'merge-msg test #2' '
 
        git checkout master &&
-       git fetch ../trash left &&
+       git fetch ../"$test" left &&
 
        git fmt-merge-msg <.git/FETCH_HEAD >actual &&
        git diff actual expected
@@ -106,8 +106,24 @@ Merge branch 'left'
   Common #1
 EOF
 
-test_expect_success 'merge-msg test #3' '
+test_expect_success 'merge-msg test #3-1' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.log true &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left &&
+
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git diff actual expected
+'
+
+test_expect_success 'merge-msg test #3-2' '
 
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
        git config merge.summary true &&
 
        git checkout master &&
@@ -136,8 +152,24 @@ Merge branches 'left' and 'right'
   Common #1
 EOF
 
-test_expect_success 'merge-msg test #4' '
+test_expect_success 'merge-msg test #4-1' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.log true &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
 
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git diff actual expected
+'
+
+test_expect_success 'merge-msg test #4-2' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
        git config merge.summary true &&
 
        git checkout master &&
@@ -148,8 +180,24 @@ test_expect_success 'merge-msg test #4' '
        git diff actual expected
 '
 
-test_expect_success 'merge-msg test #5' '
+test_expect_success 'merge-msg test #5-1' '
+
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
+       git config merge.log yes &&
+
+       git checkout master &&
+       setdate &&
+       git fetch . left right &&
+
+       git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+       git diff actual expected
+'
+
+test_expect_success 'merge-msg test #5-2' '
 
+       git config --unset-all merge.log
+       git config --unset-all merge.summary
        git config merge.summary yes &&
 
        git checkout master &&
index efd658adb6d6327863f430b10ab65aa3f112f996..1639c7aa9624bf3219afd021daf14c6f07dcfc32 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success 'use index-filter to move into a subdirectory' '
                 "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
                  GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
                        git update-index --index-info &&
-                 mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE" directorymoved &&
+                 mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
        test -z "$(git diff HEAD directorymoved:newsubdir)"'
 
 test_expect_success 'stops when msg filter fails' '
@@ -219,4 +219,36 @@ test_expect_success 'Subdirectory filter with disappearing trees' '
        test $(git rev-list master | wc -l) = 3
 '
 
+test_expect_success 'Tag name filtering retains tag message' '
+       git tag -m atag T &&
+       git cat-file tag T > expect &&
+       git filter-branch -f --tag-name-filter cat &&
+       git cat-file tag T > actual &&
+       git diff expect actual
+'
+
+faux_gpg_tag='object XXXXXX
+type commit
+tag S
+tagger T A Gger <tagger@example.com> 1206026339 -0500
+
+This is a faux gpg signed tag.
+-----BEGIN PGP SIGNATURE-----
+Version: FauxGPG v0.0.0 (FAUX/Linux)
+
+gdsfoewhxu/6l06f1kxyxhKdZkrcbaiOMtkJUA9ITAc1mlamh0ooasxkH1XwMbYQ
+acmwXaWET20H0GeAGP+7vow=
+=agpO
+-----END PGP SIGNATURE-----
+'
+test_expect_success 'Tag name filtering strips gpg signature' '
+       sha1=$(git rev-parse HEAD) &&
+       sha1t=$(echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | git mktag) &&
+       git update-ref "refs/tags/S" "$sha1t" &&
+       echo "$faux_gpg_tag" | sed -e s/XXXXXX/$sha1/ | head -n 6 > expect &&
+       git filter-branch -f --tag-name-filter cat &&
+       git cat-file tag S > actual &&
+       git diff expect actual
+'
+
 test_done
index 1a7141ecd7ad1ffcd5081181548920b6a199ab2c..2dcee7ccc572bbc9b239ff805aede076ff926f49 100755 (executable)
@@ -626,7 +626,8 @@ esac
 
 cp -R ../t7004 ./gpghome
 chmod 0700 gpghome
-export GNUPGHOME="$(pwd)/gpghome"
+GNUPGHOME="$(pwd)/gpghome"
+export GNUPGHOME
 
 get_tag_header signed-tag $commit commit $time >expect
 echo 'A signed tag message' >>expect
index 02cf7c5c9dc5ba9ea66d9dc109a11d300c7bd817..d8a7c798525728ddc8fc5fa9bd8335d8d1f0a710 100755 (executable)
@@ -122,7 +122,7 @@ test_expect_success 'commit using absolute path names' '
 
 test_expect_success 'log using absolute path names' '
        echo bb >>a/b/c/d &&
-       git commit -m "bb" $(pwd)/a/b/c/d &&
+       git commit -m "bb" "$(pwd)/a/b/c/d" &&
 
        git log a/b/c/d >f1.txt &&
        git log "$(pwd)/a/b/c/d" >f2.txt &&
index a50492f7c065d034cb344eac965d51a82cb5a599..bd7723970869aa0ce8f48b83fdc568821fdf737c 100755 (executable)
@@ -112,7 +112,7 @@ test_expect_success 'git-clean with absolute path' '
        touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
        would_clean=$(
                cd docs &&
-               git clean -n $(pwd)/../src |
+               git clean -n "$(pwd)/../src" |
                sed -n -e "s|^Would remove ||p"
        ) &&
        test "$would_clean" = ../src/part3.c || {
index 2ef85a869d515b557815476f126386dc19f755c5..6c7b9024822024ca24f7d1ddea63c94d3aa016b0 100755 (executable)
@@ -196,4 +196,17 @@ test_expect_success 'apply submodule diff' '
        test -z "$D"
 '
 
+test_expect_success 'update --init' '
+
+       mv init init2 &&
+       git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+       git config --remove-section submodule.example
+       git submodule update init > update.out &&
+       grep "not initialized" update.out &&
+       test ! -d init/.git &&
+       git submodule update --init init &&
+       test -d init/.git
+
+'
+
 test_done
index c0288f345fb5809d8606f4d3cfaf73d24d6c7281..d3370ff7ff66240f14b8231de7ba0198e73e8deb 100755 (executable)
@@ -41,7 +41,7 @@ test_expect_success \
 test_expect_success \
        "using paths with --interactive" \
        "echo bong-o-bong >file &&
-       ! echo 7 | git-commit -m foo --interactive file"
+       ! (echo 7 | git-commit -m foo --interactive file)"
 
 test_expect_success \
        "using invalid commit with -C" \
@@ -79,8 +79,8 @@ test_expect_success \
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -e "s/a file/an amend commit/g" < $1 > $1-
-mv $1- $1
+sed -e "s/a file/an amend commit/g" < "$1" > "$1-"
+mv "$1-" "$1"
 EOF
 chmod 755 editor
 
@@ -99,8 +99,8 @@ test_expect_success \
 
 cat >editor <<\EOF
 #!/bin/sh
-sed -e "s/amend/older/g"  < $1 > $1-
-mv $1- $1
+sed -e "s/amend/older/g"  < "$1" > "$1-"
+mv "$1-" "$1"
 EOF
 chmod 755 editor
 
index 284c9412474754be649ccc51df996e3e370ac6ec..3531a992a9a50e25a1585435e024097c7028af27 100755 (executable)
@@ -154,6 +154,35 @@ test_expect_success 'cleanup commit messages (strip,-F,-e)' '
 
 '
 
+echo "#
+# Author:    $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>
+#" >> expect
+
+test_expect_success 'author different from committer' '
+
+       echo >>negative &&
+       git commit -e -m "sample"
+       head -n 7 .git/COMMIT_EDITMSG >actual &&
+       test_cmp expect actual
+'
+
+mv expect expect.tmp
+sed '$d' < expect.tmp > expect
+rm -f expect.tmp
+echo "# Committer:
+#" >> expect
+unset GIT_COMMITTER_EMAIL
+unset GIT_COMMITTER_NAME
+
+test_expect_success 'committer is automatic' '
+
+       echo >>negative &&
+       git commit -e -m "sample"
+       head -n 8 .git/COMMIT_EDITMSG | \
+       sed "s/^# Committer: .*/# Committer:/" >actual &&
+       test_cmp expect actual
+'
+
 pwd=`pwd`
 cat >> .git/FAKE_EDITOR << EOF
 #! /bin/sh
index eff36aaee32200075f815de43af781cb115ea38e..88577af9535b5ed7027ec2b6b752df3b78d7bab4 100755 (executable)
@@ -19,6 +19,9 @@ cp FAKE_MSG "$1"
 exit 0
 EOF
 chmod +x fake-editor
+
+## Not using test_set_editor here so we can easily ensure the editor variable
+## is only set for the editor tests
 FAKE_EDITOR="$(pwd)/fake-editor"
 export FAKE_EDITOR
 
@@ -27,7 +30,7 @@ test_expect_success 'with no hook (editor)' '
        echo "more foo" >> file &&
        git add file &&
        echo "more foo" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit
 
 '
 
@@ -44,7 +47,7 @@ test_expect_success '--no-verify with no hook (editor)' '
        echo "more bar" > file &&
        git add file &&
        echo "more bar" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -71,7 +74,7 @@ test_expect_success 'with succeeding hook (editor)' '
        echo "more more" >> file &&
        git add file &&
        echo "more more" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit
 
 '
 
@@ -88,7 +91,7 @@ test_expect_success '--no-verify with succeeding hook (editor)' '
        echo "even more more" >> file &&
        git add file &&
        echo "even more more" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -111,7 +114,7 @@ test_expect_success 'with failing hook (editor)' '
        echo "more another" >> file &&
        git add file &&
        echo "more another" > FAKE_MSG &&
-       ! (GIT_EDITOR="$FAKE_EDITOR" git commit)
+       ! (GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit)
 
 '
 
@@ -128,7 +131,7 @@ test_expect_success '--no-verify with failing hook (editor)' '
        echo "more stuff" >> file &&
        git add file &&
        echo "more stuff" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -146,7 +149,7 @@ test_expect_success 'with non-executable hook (editor)' '
        echo "content again" >> file &&
        git add file &&
        echo "content again" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit -m "content again"
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -m "content again"
 
 '
 
@@ -163,7 +166,7 @@ test_expect_success '--no-verify with non-executable hook (editor)' '
        echo "even more content" >> file &&
        git add file &&
        echo "even more content" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify
 
 '
 
@@ -193,7 +196,7 @@ test_expect_success 'hook edits commit message (editor)' '
        echo "additional content" >> file &&
        git add file &&
        echo "additional content" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
        commit_msg_is "new message"
 
 '
@@ -212,7 +215,7 @@ test_expect_success "hook doesn't edit commit message (editor)" '
        echo "more plus" >> file &&
        git add file &&
        echo "more plus" > FAKE_MSG &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify &&
        commit_msg_is "more plus"
 
 '
index 802aa624d0ff6a1add2e1c7e40a651336b450053..cd6c7c834218fd4c46c49396b79da1ddeef42772 100755 (executable)
@@ -18,6 +18,9 @@ cat > fake-editor <<'EOF'
 exit 0
 EOF
 chmod +x fake-editor
+
+## Not using test_set_editor here so we can easily ensure the editor variable
+## is only set for the editor tests
 FAKE_EDITOR="$(pwd)/fake-editor"
 export FAKE_EDITOR
 
@@ -58,7 +61,7 @@ test_expect_success 'with hook (-m editor)' '
 
        echo "more" >> file &&
        git add file &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit -e -m "more more" &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -m "more more" &&
        test "`git log -1 --pretty=format:%s`" = message
 
 '
@@ -85,7 +88,7 @@ test_expect_success 'with hook (-F editor)' '
 
        echo "more" >> file &&
        git add file &&
-       (echo more more | GIT_EDITOR="$FAKE_EDITOR" git commit -e -F -) &&
+       (echo more more | GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -e -F -) &&
        test "`git log -1 --pretty=format:%s`" = message
 
 '
@@ -104,7 +107,7 @@ test_expect_success 'with hook (editor)' '
 
        echo "more more" >> file &&
        git add file &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit &&
        test "`git log -1 --pretty=format:%s`" = default
 
 '
@@ -114,7 +117,7 @@ test_expect_success 'with hook (--amend)' '
        head=`git rev-parse HEAD` &&
        echo "more" >> file &&
        git add file &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit --amend &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --amend &&
        test "`git log -1 --pretty=format:%s`" = "$head"
 
 '
@@ -124,7 +127,7 @@ test_expect_success 'with hook (-c)' '
        head=`git rev-parse HEAD` &&
        echo "more" >> file &&
        git add file &&
-       GIT_EDITOR="$FAKE_EDITOR" git commit -c $head &&
+       GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head &&
        test "`git log -1 --pretty=format:%s`" = "$head"
 
 '
@@ -139,7 +142,7 @@ test_expect_success 'with failing hook' '
        head=`git rev-parse HEAD` &&
        echo "more" >> file &&
        git add file &&
-       ! GIT_EDITOR="$FAKE_EDITOR" git commit -c $head
+       ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit -c $head
 
 '
 
@@ -148,7 +151,7 @@ test_expect_success 'with failing hook (--no-verify)' '
        head=`git rev-parse HEAD` &&
        echo "more" >> file &&
        git add file &&
-       ! GIT_EDITOR="$FAKE_EDITOR" git commit --no-verify -c $head
+       ! GIT_EDITOR="\"\$FAKE_EDITOR\"" git commit --no-verify -c $head
 
 '
 
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
new file mode 100755 (executable)
index 0000000..a75130c
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+test_description='git-status for submodule'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_create_repo sub
+       cd sub &&
+       : >bar &&
+       git add bar &&
+       git commit -m " Add bar" &&
+       cd .. &&
+       git add sub &&
+       git commit -m "Add submodule sub"
+'
+
+test_expect_success 'status clean' '
+       git status |
+       grep "nothing to commit"
+'
+test_expect_success 'status -a clean' '
+       git status -a |
+       grep "nothing to commit"
+'
+test_expect_success 'rm submodule contents' '
+       rm -rf sub/* sub/.git
+'
+test_expect_success 'status clean (empty submodule dir)' '
+       git status |
+       grep "nothing to commit"
+'
+test_expect_success 'status -a clean (empty submodule dir)' '
+       git status -a |
+       grep "nothing to commit"
+'
+
+test_done
index 56869aceeda90281a9fed992d0b202ec03fcb9f9..d21cd290d3d8e3d912b94045f3d3d9db8bdd7e7d 100755 (executable)
@@ -104,7 +104,11 @@ create_merge_msgs() {
        git log --no-merges ^HEAD c2 >>squash.1-5 &&
        echo "Squashed commit of the following:" >squash.1-5-9 &&
        echo >>squash.1-5-9 &&
-       git log --no-merges ^HEAD c2 c3 >>squash.1-5-9
+       git log --no-merges ^HEAD c2 c3 >>squash.1-5-9 &&
+       echo > msg.nolog &&
+       echo "* commit 'c3':" >msg.log &&
+       echo "  commit 3" >>msg.log &&
+       echo >>msg.log
 }
 
 verify_diff() {
@@ -364,7 +368,7 @@ test_expect_success 'merge c1 with c2 (squash in config)' '
 
 test_debug 'gitk --all'
 
-test_expect_success 'override config option -n' '
+test_expect_success 'override config option -n with --summary' '
        git reset --hard c1 &&
        git config branch.master.mergeoptions "-n" &&
        test_tick &&
@@ -373,15 +377,30 @@ test_expect_success 'override config option -n' '
        verify_parents $c1 $c2 &&
        if ! grep "^ file |  *2 +-$" diffstat.txt
        then
-               echo "[OOPS] diffstat was not generated"
+               echo "[OOPS] diffstat was not generated with --summary"
+               false
+       fi
+'
+
+test_expect_success 'override config option -n with --stat' '
+       git reset --hard c1 &&
+       git config branch.master.mergeoptions "-n" &&
+       test_tick &&
+       git merge --stat c2 >diffstat.txt &&
+       verify_merge file result.1-5 msg.1-5 &&
+       verify_parents $c1 $c2 &&
+       if ! grep "^ file |  *2 +-$" diffstat.txt
+       then
+               echo "[OOPS] diffstat was not generated with --stat"
+               false
        fi
 '
 
 test_debug 'gitk --all'
 
-test_expect_success 'override config option --summary' '
+test_expect_success 'override config option --stat' '
        git reset --hard c1 &&
-       git config branch.master.mergeoptions "--summary" &&
+       git config branch.master.mergeoptions "--stat" &&
        test_tick &&
        git merge -n c2 >diffstat.txt &&
        verify_merge file result.1-5 msg.1-5 &&
@@ -441,6 +460,16 @@ test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
        verify_head $c1
 '
 
+test_expect_success 'merge log message' '
+       git reset --hard c0 &&
+       git merge --no-log c2 &&
+       git show -s --pretty=format:%b HEAD >msg.act &&
+       verify_diff msg.nolog msg.act "[OOPS] bad merge log message" &&
+       git merge --log c3 &&
+       git show -s --pretty=format:%b HEAD >msg.act &&
+       verify_diff msg.log msg.act "[OOPS] bad merge log message"
+'
+
 test_debug 'gitk --all'
 
 test_done
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
new file mode 100755 (executable)
index 0000000..6a5211f
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='git-repack works correctly'
+
+. ./test-lib.sh
+
+test_expect_success '-A option leaves unreachable objects unpacked' '
+       echo content > file1 &&
+       git add . &&
+       git commit -m initial_commit &&
+       # create a transient branch with unique content
+       git checkout -b transient_branch &&
+       echo more content >> file1 &&
+       # record the objects created in the database for file, commit, tree
+       fsha1=$(git hash-object file1) &&
+       git commit -a -m more_content &&
+       csha1=$(git rev-parse HEAD^{commit}) &&
+       tsha1=$(git rev-parse HEAD^{tree}) &&
+       git checkout master &&
+       echo even more content >> file1 &&
+       git commit -a -m even_more_content &&
+       # delete the transient branch
+       git branch -D transient_branch &&
+       # pack the repo
+       git repack -A -d -l &&
+       # verify objects are packed in repository
+       test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+                  grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+                  sort | uniq | wc -l) &&
+       git show $fsha1 &&
+       git show $csha1 &&
+       git show $tsha1 &&
+       # now expire the reflog
+       sleep 1 &&
+       git reflog expire --expire-unreachable=now --all &&
+       # and repack
+       git repack -A -d -l &&
+       # verify objects are retained unpacked
+       test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+                  grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+                  sort | uniq | wc -l) &&
+       git show $fsha1 &&
+       git show $csha1 &&
+       git show $tsha1
+'
+
+test_done
index c0973b4e6ee2c6a55d1573e2341607c4adfcbb10..3e4eb63f1c4d63d6ff38c79ff1f7a47fa3aa1597 100755 (executable)
@@ -139,15 +139,16 @@ test_expect_success 'Valid In-Reply-To when prompting' '
 
 test_expect_success 'setup fake editor' '
        (echo "#!/bin/sh" &&
-        echo "echo fake edit >>\$1"
+        echo "echo fake edit >>\"\$1\""
        ) >fake-editor &&
        chmod +x fake-editor
 '
 
+test_set_editor "$(pwd)/fake-editor"
+
 test_expect_success '--compose works' '
        clean_fake_sendmail &&
        echo y | \
-               GIT_EDITOR=$(pwd)/fake-editor \
                GIT_SEND_EMAIL_NOTTY=1 \
                git send-email \
                --compose --subject foo \
@@ -166,4 +167,129 @@ test_expect_success 'second message is patch' '
        grep "Subject:.*Second" msgtxt2
 '
 
+cat >expected-show-all-headers <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: cc@example.com, A <author@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cc set' '
+       git config sendemail.cc cc@example.com &&
+       git send-email \
+               --dry-run \
+               --from="Example <from@example.com>" \
+               --to=to@example.com \
+               --smtp-server relay.example.com \
+               $patches |
+       sed     -e "s/^\(Date:\).*/\1 DATE-STRING/" \
+               -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+               -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+               >actual-show-all-headers &&
+       test_cmp expected-show-all-headers actual-show-all-headers
+'
+
+cat >expected-show-all-headers <<\EOF
+0001-Second.patch
+(mbox) Adding cc: A <author@example.com> from line 'From: A <author@example.com>'
+Dry-OK. Log says:
+Server: relay.example.com
+MAIL FROM:<from@example.com>
+RCPT TO:<to@example.com>,<author@example.com>
+From: Example <from@example.com>
+To: to@example.com
+Cc: A <author@example.com>
+Subject: [PATCH 1/1] Second.
+Date: DATE-STRING
+Message-Id: MESSAGE-ID-STRING
+X-Mailer: X-MAILER-STRING
+
+Result: OK
+EOF
+
+test_expect_success 'sendemail.cc unset' '
+       git config --unset sendemail.cc &&
+       git send-email \
+               --dry-run \
+               --from="Example <from@example.com>" \
+               --to=to@example.com \
+               --smtp-server relay.example.com \
+               $patches |
+       sed     -e "s/^\(Date:\).*/\1 DATE-STRING/" \
+               -e "s/^\(Message-Id:\).*/\1 MESSAGE-ID-STRING/" \
+               -e "s/^\(X-Mailer:\).*/\1 X-MAILER-STRING/" \
+               >actual-show-all-headers &&
+       test_cmp expected-show-all-headers actual-show-all-headers
+'
+
+test_expect_success '--compose adds MIME for utf8 body' '
+       clean_fake_sendmail &&
+       (echo "#!/bin/sh" &&
+        echo "echo utf8 body: àéìöú >>\"\$1\""
+       ) >fake-editor-utf8 &&
+       chmod +x fake-editor-utf8 &&
+       echo y | \
+         GIT_EDITOR="\"$(pwd)/fake-editor-utf8\"" \
+         GIT_SEND_EMAIL_NOTTY=1 \
+         git send-email \
+         --compose --subject foo \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         $patches &&
+       grep "^utf8 body" msgtxt1 &&
+       grep "^Content-Type: text/plain; charset=utf-8" msgtxt1
+'
+
+test_expect_success '--compose respects user mime type' '
+       clean_fake_sendmail &&
+       (echo "#!/bin/sh" &&
+        echo "(echo MIME-Version: 1.0"
+        echo " echo Content-Type: text/plain\\; charset=iso-8859-1"
+        echo " echo Content-Transfer-Encoding: 8bit"
+        echo " echo Subject: foo"
+        echo " echo "
+        echo " echo utf8 body: àéìöú) >\"\$1\""
+       ) >fake-editor-utf8-mime &&
+       chmod +x fake-editor-utf8-mime &&
+       echo y | \
+         GIT_EDITOR="\"$(pwd)/fake-editor-utf8-mime\"" \
+         GIT_SEND_EMAIL_NOTTY=1 \
+         git send-email \
+         --compose --subject foo \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         $patches &&
+       grep "^utf8 body" msgtxt1 &&
+       grep "^Content-Type: text/plain; charset=iso-8859-1" msgtxt1 &&
+       ! grep "^Content-Type: text/plain; charset=utf-8" msgtxt1
+'
+
+test_expect_success '--compose adds MIME for utf8 subject' '
+       clean_fake_sendmail &&
+       echo y | \
+         GIT_EDITOR="\"$(pwd)/fake-editor\"" \
+         GIT_SEND_EMAIL_NOTTY=1 \
+         git send-email \
+         --compose --subject utf8-sübjëct \
+         --from="Example <nobody@example.com>" \
+         --to=nobody@example.com \
+         --smtp-server="$(pwd)/fake.sendmail" \
+         $patches &&
+       grep "^fake edit" msgtxt1 &&
+       grep "^Subject: =?utf-8?q?utf8-s=C3=BCbj=C3=ABct?=" msgtxt1
+'
+
 test_done
index 4e24ab3a7db96d396c108dd0cbe677aa067c9460..bdf29c17344051e5c65d61994744f5432ddf8ae3 100755 (executable)
@@ -20,39 +20,39 @@ esac
 echo '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 &&
        ln -s foo foo.link
        mkdir -p dir/a/b/c/d/e &&
-       echo 'deep dir' > dir/a/b/c/d/e/file &&
+       echo "deep dir" > dir/a/b/c/d/e/file &&
        mkdir bar &&
-       echo 'zzz' > bar/zzz &&
-       echo '#!/bin/sh' > exec.sh &&
+       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'
 
-test_expect_success "checkout from svn" "svn co $svnrepo '$SVN_TREE'"
+test_expect_success "checkout from svn" 'svn co "$svnrepo" "$SVN_TREE"'
 
 name='try a deep --rmdir with a commit'
-test_expect_success "$name" "
+test_expect_success "$name" '
        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 commit -m "$name" &&
        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"
+       svn up "$SVN_TREE" &&
+       test -d "$SVN_TREE"/dir && test ! -d "$SVN_TREE"/dir/a'
 
 
 name='detect node change from file to directory #1'
@@ -68,108 +68,108 @@ test_expect_success "$name" "
 
 
 name='detect node change from directory to file #1'
-test_expect_success "$name" "
-       rm -rf dir '$GIT_DIR'/index &&
+test_expect_success "$name" '
+       rm -rf dir "$GIT_DIR"/index &&
        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' &&
+       git commit -m "$name" &&
        ! git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch2" || true
+               remotes/git-svn..mybranch2' || true
 
 
 name='detect node change from file to directory #2'
-test_expect_success "$name" "
-       rm -f '$GIT_DIR'/index &&
+test_expect_success "$name" '
+       rm -f "$GIT_DIR"/index &&
        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' &&
+       git commit -m "$name" &&
        ! git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch3" || true
+               remotes/git-svn..mybranch3' || true
 
 
 name='detect node change from directory to file #2'
-test_expect_success "$name" "
-       rm -f '$GIT_DIR'/index &&
+test_expect_success "$name" '
+       rm -f "$GIT_DIR"/index &&
        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' &&
+       git commit -m "$name" &&
        ! git-svn set-tree --find-copies-harder --rmdir \
-               remotes/git-svn..mybranch4" || true
+               remotes/git-svn..mybranch4' || true
 
 
 name='remove executable bit from a file'
-test_expect_success "$name" "
-       rm -f '$GIT_DIR'/index &&
+test_expect_success "$name" '
+       rm -f "$GIT_DIR"/index &&
        git checkout -f -b mybranch5 remotes/git-svn &&
        chmod -x exec.sh &&
        git update-index exec.sh &&
-       git commit -m '$name' &&
+       git commit -m "$name" &&
        git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch5 &&
-       svn up '$SVN_TREE' &&
-       test ! -x '$SVN_TREE'/exec.sh"
+       svn up "$SVN_TREE" &&
+       test ! -x "$SVN_TREE"/exec.sh'
 
 
 name='add executable bit back file'
-test_expect_success "$name" "
+test_expect_success "$name" '
        chmod +x exec.sh &&
        git update-index exec.sh &&
-       git commit -m '$name' &&
+       git commit -m "$name" &&
        git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch5 &&
-       svn up '$SVN_TREE' &&
-       test -x '$SVN_TREE'/exec.sh"
+       svn up "$SVN_TREE" &&
+       test -x "$SVN_TREE"/exec.sh'
 
 
 name='executable file becomes a symlink to bar/zzz (file)'
-test_expect_success "$name" "
+test_expect_success "$name" '
        rm exec.sh &&
        ln -s bar/zzz exec.sh &&
        git update-index exec.sh &&
-       git commit -m '$name' &&
+       git commit -m "$name" &&
        git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch5 &&
-       svn up '$SVN_TREE' &&
-       test -L '$SVN_TREE'/exec.sh"
+       svn up "$SVN_TREE" &&
+       test -L "$SVN_TREE"/exec.sh'
 
 name='new symlink is added to a file that was also just made executable'
 
-test_expect_success "$name" "
+test_expect_success "$name" '
        chmod +x bar/zzz &&
        ln -s bar/zzz exec-2.sh &&
        git update-index --add bar/zzz exec-2.sh &&
-       git commit -m '$name' &&
+       git commit -m "$name" &&
        git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch5 &&
-       svn up '$SVN_TREE' &&
-       test -x '$SVN_TREE'/bar/zzz &&
-       test -L '$SVN_TREE'/exec-2.sh"
+       svn up "$SVN_TREE" &&
+       test -x "$SVN_TREE"/bar/zzz &&
+       test -L "$SVN_TREE"/exec-2.sh'
 
 name='modify a symlink to become a file'
-test_expect_success "$name" "
+test_expect_success "$name" '
        echo git help > help || true &&
        rm exec-2.sh &&
        cp help exec-2.sh &&
        git update-index exec-2.sh &&
-       git commit -m '$name' &&
+       git commit -m "$name" &&
        git-svn set-tree --find-copies-harder --rmdir \
                remotes/git-svn..mybranch5 &&
-       svn up '$SVN_TREE' &&
-       test -f '$SVN_TREE'/exec-2.sh &&
-       test ! -L '$SVN_TREE'/exec-2.sh &&
-       git diff help $SVN_TREE/exec-2.sh"
+       svn up "$SVN_TREE" &&
+       test -f "$SVN_TREE"/exec-2.sh &&
+       test ! -L "$SVN_TREE"/exec-2.sh &&
+       git diff help "$SVN_TREE"/exec-2.sh'
 
 if test "$have_utf8" = t
 then
@@ -190,10 +190,10 @@ name='test fetch functionality (svn => git) with alternate GIT_SVN_ID'
 GIT_SVN_ID=alt
 export GIT_SVN_ID
 test_expect_success "$name" \
-    "git-svn init $svnrepo && git-svn fetch &&
+    'git-svn init "$svnrepo" && git-svn fetch &&
      git rev-list --pretty=raw remotes/git-svn | grep ^tree | uniq > a &&
      git rev-list --pretty=raw remotes/alt | grep ^tree | uniq > b &&
-     git diff a b"
+     git diff a b'
 
 name='check imported tree checksums expected tree checksums'
 rm -f expected
@@ -219,22 +219,22 @@ test_expect_success 'exit if remote refs are ambigious' "
        ! git-svn migrate
 "
 
-test_expect_success 'exit if init-ing a would clobber a URL' "
-        svnadmin create ${PWD}/svnrepo2 &&
-        svn mkdir -m 'mkdir bar' ${svnrepo}2/bar &&
+test_expect_success 'exit if init-ing a would clobber a URL' '
+        svnadmin create "${PWD}/svnrepo2" &&
+        svn mkdir -m "mkdir bar" "${svnrepo}2/bar" &&
         git config --unset svn-remote.svn.fetch \
-                                '^bar:refs/remotes/git-svn$' &&
-       ! git-svn init ${svnrepo}2/bar
-        "
+                                "^bar:refs/remotes/git-svn$" &&
+       ! git-svn init "${svnrepo}2/bar"
+        '
 
 test_expect_success \
-  'init allows us to connect to another directory in the same repo' "
-        git-svn init --minimize-url -i bar $svnrepo/bar &&
+  'init allows us to connect to another directory in the same repo' '
+        git-svn init --minimize-url -i bar "$svnrepo/bar" &&
         git config --get svn-remote.svn.fetch \
-                              '^bar:refs/remotes/bar$' &&
+                              "^bar:refs/remotes/bar$" &&
         git config --get svn-remote.svn.fetch \
-                              '^:refs/remotes/git-svn$'
-        "
+                              "^:refs/remotes/git-svn$"
+        '
 
 test_expect_success 'able to dcommit to a subdirectory' "
        git-svn fetch -i bar &&
index d7a704754ea13f17c098e56a7b068cb4f44c1fd0..f420796c31db2746b71ba9d7090f37363eba214a 100755 (executable)
@@ -52,7 +52,7 @@ EOF
 cd ..
 
 rm -rf import
-test_expect_success 'checkout working copy from svn' "svn co $svnrepo test_wc"
+test_expect_success 'checkout working copy from svn' 'svn co "$svnrepo" test_wc'
 test_expect_success 'setup some commits to svn' \
        'cd test_wc &&
                echo Greetings >> kw.c &&
@@ -66,7 +66,7 @@ test_expect_success 'setup some commits to svn' \
                svn commit -m "Propset Id" &&
        cd ..'
 
-test_expect_success 'initialize git-svn' "git-svn init $svnrepo"
+test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"'
 test_expect_success 'fetch revisions from svn' 'git-svn fetch'
 
 name='test svn:keywords ignoring'
@@ -90,9 +90,9 @@ test_expect_success "propset CR on crlf files" \
         cd ..'
 
 test_expect_success 'fetch and pull latest from svn and checkout a new wc' \
-       "git-svn fetch &&
+       'git-svn fetch &&
         git pull . remotes/git-svn &&
-        svn co $svnrepo new_wc"
+        svn co "$svnrepo" new_wc'
 
 for i in crlf ne_crlf lf ne_lf cr ne_cr empty_cr empty_lf empty empty_crlf
 do
index 4e0808380fea78061e37bc4308f0d7ffeb1cbf5f..0e7ce34b9b1e254873a2700cf58095318b49b15c 100755 (executable)
@@ -2,29 +2,29 @@
 test_description='git-svn rmdir'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        mkdir -p deeply/nested/directory/number/1 &&
        mkdir -p deeply/nested/directory/number/2 &&
        echo foo > deeply/nested/directory/number/1/file &&
        echo foo > deeply/nested/directory/number/2/another &&
-       svn import -m 'import for git-svn' . $svnrepo &&
+       svn import -m "import for git-svn" . "$svnrepo" &&
        cd ..
-       "
+       '
 
-test_expect_success 'mirror via git-svn' "
-       git-svn init $svnrepo &&
+test_expect_success 'mirror via git-svn' '
+       git-svn init "$svnrepo" &&
        git-svn fetch &&
        git checkout -f -b test-rmdir remotes/git-svn
-       "
+       '
 
-test_expect_success 'Try a commit on rmdir' "
+test_expect_success 'Try a commit on rmdir' '
        git rm -f deeply/nested/directory/number/2/another &&
-       git commit -a -m 'remove another' &&
+       git commit -a -m "remove another" &&
        git-svn set-tree --rmdir HEAD &&
-       svn ls -R $svnrepo | grep ^deeply/nested/directory/number/1
-       "
+       svn ls -R "$svnrepo" | grep ^deeply/nested/directory/number/1
+       '
 
 
 test_done
index 0f0b0fd2c69addc33b91a114a7e294097a494cd5..9ffd8458ef9d58fa5d3c42fd61f4629219b4d80a 100755 (executable)
@@ -10,30 +10,30 @@ test_expect_success 'make history for tracking' '
        mkdir import &&
        mkdir import/trunk &&
        echo hello >> import/trunk/README &&
-       svn import -m initial import $svnrepo &&
+       svn import -m initial import "$svnrepo" &&
        rm -rf import &&
-       svn co $svnrepo/trunk trunk &&
+       svn co "$svnrepo"/trunk trunk &&
        echo bye bye >> trunk/README &&
-       svn rm -m "gone" $svnrepo/trunk &&
+       svn rm -m "gone" "$svnrepo"/trunk &&
        rm -rf trunk &&
        mkdir trunk &&
        echo "new" > trunk/FOLLOWME &&
-       svn import -m "new trunk" trunk $svnrepo/trunk
+       svn import -m "new trunk" trunk "$svnrepo"/trunk
 '
 
 test_expect_success 'clone repo with git' '
-       git svn clone -s $svnrepo x &&
+       git svn clone -s "$svnrepo" x &&
        test -f x/FOLLOWME &&
        test ! -f x/README
 '
 
-test_expect_success 'make sure r2 still has old file' '
+test_expect_success 'make sure r2 still has old file' "
        cd x &&
-               test -n "$(git svn find-rev r1)" &&
-               git reset --hard $(git svn find-rev r1) &&
+               test -n \"\$(git svn find-rev r1)\" &&
+               git reset --hard \$(git svn find-rev r1) &&
                test -f README &&
                test ! -f FOLLOWME &&
-               test x$(git svn find-rev r2) = x
-'
+               test x\$(git svn find-rev r2) = x
+"
 
 test_done
index 7ba76309ac9e57f9e5379bf93ecac4e6a4e4ad96..4d964e2db7cc3c96fc64911bd58c4f2f9679a6cd 100755 (executable)
 test_description='git-svn fetching'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        mkdir -p trunk &&
        echo hello > trunk/readme &&
-       svn import -m 'initial' . $svnrepo &&
+       svn import -m "initial" . "$svnrepo" &&
        cd .. &&
-       svn co $svnrepo wc &&
+       svn co "$svnrepo" wc &&
        cd wc &&
        echo world >> trunk/readme &&
        poke trunk/readme &&
-       svn commit -m 'another commit' &&
+       svn commit -m "another commit" &&
        svn up &&
        svn mv trunk thunk &&
        echo goodbye >> thunk/readme &&
        poke thunk/readme &&
-       svn commit -m 'bye now' &&
+       svn commit -m "bye now" &&
        cd ..
-       "
+       '
 
-test_expect_success 'init and fetch a moved directory' "
-       git-svn init --minimize-url -i thunk $svnrepo/thunk &&
+test_expect_success 'init and fetch a moved directory' '
+       git-svn init --minimize-url -i thunk "$svnrepo"/thunk &&
        git-svn fetch -i thunk &&
-       test \"\`git rev-parse --verify refs/remotes/thunk@2\`\" \
-           = \"\`git rev-parse --verify refs/remotes/thunk~1\`\" &&
-        test \"\`git cat-file blob refs/remotes/thunk:readme |\
-                 sed -n -e '3p'\`\" = goodbye &&
-       test -z \"\`git config --get svn-remote.svn.fetch \
-                '^trunk:refs/remotes/thunk@2$'\`\"
-       "
+       test "`git rev-parse --verify refs/remotes/thunk@2`" \
+           = "`git rev-parse --verify refs/remotes/thunk~1`" &&
+        test "`git cat-file blob refs/remotes/thunk:readme |\
+                 sed -n -e "3p"`" = goodbye &&
+       test -z "`git config --get svn-remote.svn.fetch \
+                "^trunk:refs/remotes/thunk@2$"`"
+       '
 
-test_expect_success 'init and fetch from one svn-remote' "
-        git config svn-remote.svn.url $svnrepo &&
+test_expect_success 'init and fetch from one svn-remote' '
+        git config svn-remote.svn.url "$svnrepo" &&
         git config --add svn-remote.svn.fetch \
           trunk:refs/remotes/svn/trunk &&
         git config --add svn-remote.svn.fetch \
           thunk:refs/remotes/svn/thunk &&
         git-svn fetch -i svn/thunk &&
-       test \"\`git rev-parse --verify refs/remotes/svn/trunk\`\" \
-           = \"\`git rev-parse --verify refs/remotes/svn/thunk~1\`\" &&
-        test \"\`git cat-file blob refs/remotes/svn/thunk:readme |\
-                 sed -n -e '3p'\`\" = goodbye
-        "
+       test "`git rev-parse --verify refs/remotes/svn/trunk`" \
+           = "`git rev-parse --verify refs/remotes/svn/thunk~1`" &&
+        test "`git cat-file blob refs/remotes/svn/thunk:readme |\
+                 sed -n -e "3p"`" = goodbye
+        '
 
-test_expect_success 'follow deleted parent' "
-        (svn cp -m 'resurrecting trunk as junk' \
-               $svnrepo/trunk@2 $svnrepo/junk ||
-         svn cp -m 'resurrecting trunk as junk' \
-               -r2 $svnrepo/trunk $svnrepo/junk) &&
+test_expect_success 'follow deleted parent' '
+        (svn cp -m "resurrecting trunk as junk" \
+               "$svnrepo"/trunk@2 "$svnrepo"/junk ||
+         svn cp -m "resurrecting trunk as junk" \
+               -r2 "$svnrepo"/trunk "$svnrepo"/junk) &&
         git config --add svn-remote.svn.fetch \
           junk:refs/remotes/svn/junk &&
         git-svn fetch -i svn/thunk &&
         git-svn fetch -i svn/junk &&
-        test -z \"\`git diff svn/junk svn/trunk\`\" &&
-        test \"\`git merge-base svn/junk svn/trunk\`\" \
-           = \"\`git rev-parse svn/trunk\`\"
-        "
+        test -z "`git diff svn/junk svn/trunk`" &&
+        test "`git merge-base svn/junk svn/trunk`" \
+           = "`git rev-parse svn/trunk`"
+        '
 
-test_expect_success 'follow larger parent' "
+test_expect_success 'follow larger parent' '
         mkdir -p import/trunk/thunk/bump/thud &&
         echo hi > import/trunk/thunk/bump/thud/file &&
-        svn import -m 'import a larger parent' import $svnrepo/larger-parent &&
-        svn cp -m 'hi' $svnrepo/larger-parent $svnrepo/another-larger &&
+        svn import -m "import a larger parent" import "$svnrepo"/larger-parent &&
+        svn cp -m "hi" "$svnrepo"/larger-parent "$svnrepo"/another-larger &&
         git-svn init --minimize-url -i larger \
-          $svnrepo/another-larger/trunk/thunk/bump/thud &&
+          "$svnrepo"/another-larger/trunk/thunk/bump/thud &&
         git-svn fetch -i larger &&
         git rev-parse --verify refs/remotes/larger &&
         git rev-parse --verify \
            refs/remotes/larger-parent/trunk/thunk/bump/thud &&
-        test \"\`git merge-base \
+        test "`git merge-base \
                  refs/remotes/larger-parent/trunk/thunk/bump/thud \
-                 refs/remotes/larger\`\" = \
-             \"\`git rev-parse refs/remotes/larger\`\"
+                 refs/remotes/larger`" = \
+             "`git rev-parse refs/remotes/larger`"
         true
-        "
+        '
 
-test_expect_success 'follow higher-level parent' "
-        svn mkdir -m 'follow higher-level parent' $svnrepo/blob &&
-        svn co $svnrepo/blob blob &&
+test_expect_success 'follow higher-level parent' '
+        svn mkdir -m "follow higher-level parent" "$svnrepo"/blob &&
+        svn co "$svnrepo"/blob blob &&
         cd blob &&
                 echo hi > hi &&
                 svn add hi &&
-                svn commit -m 'hihi' &&
+                svn commit -m "hihi" &&
                 cd ..
-        svn mkdir -m 'new glob at top level' $svnrepo/glob &&
-        svn mv -m 'move blob down a level' $svnrepo/blob $svnrepo/glob/blob &&
-        git-svn init --minimize-url -i blob $svnrepo/glob/blob &&
+        svn mkdir -m "new glob at top level" "$svnrepo"/glob &&
+        svn mv -m "move blob down a level" "$svnrepo"/blob "$svnrepo"/glob/blob &&
+        git-svn init --minimize-url -i blob "$svnrepo"/glob/blob &&
         git-svn fetch -i blob
-        "
+        '
 
-test_expect_success 'follow deleted directory' "
-       svn mv -m 'bye!' $svnrepo/glob/blob/hi $svnrepo/glob/blob/bye &&
-       svn rm -m 'remove glob' $svnrepo/glob &&
-       git-svn init --minimize-url -i glob $svnrepo/glob &&
+test_expect_success 'follow deleted directory' '
+       svn mv -m "bye!" "$svnrepo"/glob/blob/hi "$svnrepo"/glob/blob/bye &&
+       svn rm -m "remove glob" "$svnrepo"/glob &&
+       git-svn init --minimize-url -i glob "$svnrepo"/glob &&
        git-svn fetch -i glob &&
-       test \"\`git cat-file blob refs/remotes/glob:blob/bye\`\" = hi &&
-       test \"\`git ls-tree refs/remotes/glob | wc -l \`\" -eq 1
-       "
+       test "`git cat-file blob refs/remotes/glob:blob/bye`" = hi &&
+       test "`git ls-tree refs/remotes/glob | wc -l `" -eq 1
+       '
 
 # ref: r9270 of the Subversion repository: (http://svn.collab.net/repos/svn)
 # in trunk/subversion/bindings/swig/perl
-test_expect_success 'follow-parent avoids deleting relevant info' "
+test_expect_success 'follow-parent avoids deleting relevant info' '
        mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
        for i in a b c ; do \
-         echo \$i > import/trunk/subversion/bindings/swig/perl/\$i.pm &&
-         echo _\$i > import/trunk/subversion/bindings/swig/perl/t/\$i.t; \
+         echo $i > import/trunk/subversion/bindings/swig/perl/$i.pm &&
+         echo _$i > import/trunk/subversion/bindings/swig/perl/t/$i.t; \
        done &&
-         echo 'bad delete test' > \
+         echo "bad delete test" > \
           import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
-         echo 'bad delete test 2' > \
+         echo "bad delete test 2" > \
           import/trunk/subversion/bindings/swig/perl/another-larger &&
        cd import &&
-         svn import -m 'r9270 test' . $svnrepo/r9270 &&
+         svn import -m "r9270 test" . "$svnrepo"/r9270 &&
        cd .. &&
-       svn co $svnrepo/r9270/trunk/subversion/bindings/swig/perl r9270 &&
+       svn co "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl r9270 &&
        cd r9270 &&
          svn mkdir native &&
          svn mv t native/t &&
-         for i in a b c; do svn mv \$i.pm native/\$i.pm; done &&
+         for i in a b c; do svn mv $i.pm native/$i.pm; done &&
          echo z >> native/t/c.t &&
          poke native/t/c.t &&
-         svn commit -m 'reorg test' &&
+         svn commit -m "reorg test" &&
        cd .. &&
        git-svn init --minimize-url -i r9270-t \
-         $svnrepo/r9270/trunk/subversion/bindings/swig/perl/native/t &&
+         "$svnrepo"/r9270/trunk/subversion/bindings/swig/perl/native/t &&
        git-svn fetch -i r9270-t &&
-       test \`git rev-list r9270-t | wc -l\` -eq 2 &&
-       test \"\`git ls-tree --name-only r9270-t~1\`\" = \
-            \"\`git ls-tree --name-only r9270-t\`\"
-       "
+       test `git rev-list r9270-t | wc -l` -eq 2 &&
+       test "`git ls-tree --name-only r9270-t~1`" = \
+            "`git ls-tree --name-only r9270-t`"
+       '
 
-test_expect_success "track initial change if it was only made to parent" "
-       svn cp -m 'wheee!' $svnrepo/r9270/trunk $svnrepo/r9270/drunk &&
+test_expect_success "track initial change if it was only made to parent" '
+       svn cp -m "wheee!" "$svnrepo"/r9270/trunk "$svnrepo"/r9270/drunk &&
        git-svn init --minimize-url -i r9270-d \
-         $svnrepo/r9270/drunk/subversion/bindings/swig/perl/native/t &&
+         "$svnrepo"/r9270/drunk/subversion/bindings/swig/perl/native/t &&
        git-svn fetch -i r9270-d &&
-       test \`git rev-list r9270-d | wc -l\` -eq 3 &&
-       test \"\`git ls-tree --name-only r9270-t\`\" = \
-            \"\`git ls-tree --name-only r9270-d\`\" &&
-       test \"\`git rev-parse r9270-t\`\" = \
-            \"\`git rev-parse r9270-d~1\`\"
-       "
+       test `git rev-list r9270-d | wc -l` -eq 3 &&
+       test "`git ls-tree --name-only r9270-t`" = \
+            "`git ls-tree --name-only r9270-d`" &&
+       test "`git rev-parse r9270-t`" = \
+            "`git rev-parse r9270-d~1`"
+       '
 
-test_expect_success "track multi-parent paths" "
-       svn cp -m 'resurrect /glob' $svnrepo/r9270 $svnrepo/glob &&
+test_expect_success "track multi-parent paths" '
+       svn cp -m "resurrect /glob" "$svnrepo"/r9270 "$svnrepo"/glob &&
        git-svn multi-fetch &&
-       test \`git cat-file commit refs/remotes/glob | \
-              grep '^parent ' | wc -l\` -eq 2
-       "
+       test `git cat-file commit refs/remotes/glob | \
+              grep "^parent " | wc -l` -eq 2
+       '
 
 test_expect_success "multi-fetch continues to work" "
        git-svn multi-fetch
        "
 
-test_expect_success "multi-fetch works off a 'clean' repository" "
-       rm -r $GIT_DIR/svn $GIT_DIR/refs/remotes $GIT_DIR/logs &&
-       mkdir $GIT_DIR/svn &&
+test_expect_success "multi-fetch works off a 'clean' repository" '
+       rm -r "$GIT_DIR/svn" "$GIT_DIR/refs/remotes" "$GIT_DIR/logs" &&
+       mkdir "$GIT_DIR/svn" &&
        git-svn multi-fetch
-       "
+       '
 
 test_debug 'gitk --all &'
 
index 318e172ef5e8f20d3552f272c6c256f5c2677c68..63230367bb1566384e66e1b5ddd6a68e1ae98c8f 100755 (executable)
@@ -4,18 +4,18 @@
 test_description='git-svn commit-diff'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        echo hello > readme &&
-       svn import -m 'initial' . $svnrepo &&
+       svn import -m "initial" . "$svnrepo" &&
        cd .. &&
        echo hello > readme &&
        git update-index --add readme &&
-       git commit -a -m 'initial' &&
+       git commit -a -m "initial" &&
        echo world >> readme &&
-       git commit -a -m 'another'
-       "
+       git commit -a -m "another"
+       '
 
 head=`git rev-parse --verify HEAD^0`
 prev=`git rev-parse --verify HEAD^1`
@@ -24,20 +24,20 @@ prev=`git rev-parse --verify HEAD^1`
 # commit, so only a basic test of functionality is needed since we've
 # already tested commit extensively elsewhere
 
-test_expect_success 'test the commit-diff command' "
-       test -n '$prev' && test -n '$head' &&
-       git-svn commit-diff -r1 '$prev' '$head' '$svnrepo' &&
-       svn co $svnrepo wc &&
+test_expect_success 'test the commit-diff command' '
+       test -n "$prev" && test -n "$head" &&
+       git-svn commit-diff -r1 "$prev" "$head" "$svnrepo" &&
+       svn co "$svnrepo" wc &&
        cmp readme wc/readme
-       "
+       '
 
-test_expect_success 'commit-diff to a sub-directory (with git-svn config)' "
-       svn import -m 'sub-directory' import $svnrepo/subdir &&
-       git-svn init --minimize-url $svnrepo/subdir &&
+test_expect_success 'commit-diff to a sub-directory (with git-svn config)' '
+       svn import -m "sub-directory" import "$svnrepo"/subdir &&
+       git-svn init --minimize-url "$svnrepo"/subdir &&
        git-svn fetch &&
-       git-svn commit-diff -r3 '$prev' '$head' &&
-       svn cat $svnrepo/subdir/readme > readme.2 &&
+       git-svn commit-diff -r3 "$prev" "$head" &&
+       svn cat "$svnrepo"/subdir/readme > readme.2 &&
        cmp readme readme.2
-       "
+       '
 
 test_done
index f74ab1269e3fa79a34aa9f9a3a64df08728e6354..58a3a7b1c333131051282acbae71dc6a1cb16b13 100755 (executable)
@@ -4,56 +4,56 @@
 test_description='git-svn commit-diff clobber'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
        echo initial > file &&
-       svn import -m 'initial' . $svnrepo &&
+       svn import -m "initial" . "$svnrepo" &&
        cd .. &&
        echo initial > file &&
        git update-index --add file &&
-       git commit -a -m 'initial'
-       "
-test_expect_success 'commit change from svn side' "
-       svn co $svnrepo t.svn &&
+       git commit -a -m "initial"
+       '
+test_expect_success 'commit change from svn side' '
+       svn co "$svnrepo" t.svn &&
        cd t.svn &&
        echo second line from svn >> file &&
        poke file &&
-       svn commit -m 'second line from svn' &&
+       svn commit -m "second line from svn" &&
        cd .. &&
        rm -rf t.svn
-       "
+       '
 
-test_expect_success 'commit conflicting change from git' "
+test_expect_success 'commit conflicting change from git' '
        echo second line from git >> file &&
-       git commit -a -m 'second line from git' &&
-       ! git-svn commit-diff -r1 HEAD~1 HEAD $svnrepo
-"
+       git commit -a -m "second line from git" &&
+       ! git-svn commit-diff -r1 HEAD~1 HEAD "$svnrepo"
+'
 
-test_expect_success 'commit complementing change from git' "
+test_expect_success 'commit complementing change from git' '
        git reset --hard HEAD~1 &&
        echo second line from svn >> file &&
-       git commit -a -m 'second line from svn' &&
+       git commit -a -m "second line from svn" &&
        echo third line from git >> file &&
-       git commit -a -m 'third line from git' &&
-       git-svn commit-diff -r2 HEAD~1 HEAD $svnrepo
-       "
+       git commit -a -m "third line from git" &&
+       git-svn commit-diff -r2 HEAD~1 HEAD "$svnrepo"
+       '
 
-test_expect_success 'dcommit fails to commit because of conflict' "
-       git-svn init $svnrepo &&
+test_expect_success 'dcommit fails to commit because of conflict' '
+       git-svn init "$svnrepo" &&
        git-svn fetch &&
        git reset --hard refs/remotes/git-svn &&
-       svn co $svnrepo t.svn &&
+       svn co "$svnrepo" t.svn &&
        cd t.svn &&
        echo fourth line from svn >> file &&
        poke file &&
-       svn commit -m 'fourth line from svn' &&
+       svn commit -m "fourth line from svn" &&
        cd .. &&
        rm -rf t.svn &&
-       echo 'fourth line from git' >> file &&
-       git commit -a -m 'fourth line from git' &&
+       echo "fourth line from git" >> file &&
+       git commit -a -m "fourth line from git" &&
        ! git-svn dcommit
-       "
+       '
 
 test_expect_success 'dcommit does the svn equivalent of an index merge' "
        git reset --hard refs/remotes/git-svn &&
@@ -66,15 +66,15 @@ test_expect_success 'dcommit does the svn equivalent of an index merge' "
        git-svn dcommit
        "
 
-test_expect_success 'commit another change from svn side' "
-       svn co $svnrepo t.svn &&
+test_expect_success 'commit another change from svn side' '
+       svn co "$svnrepo" t.svn &&
        cd t.svn &&
                echo third line from svn >> file &&
                poke file &&
-               svn commit -m 'third line from svn' &&
+               svn commit -m "third line from svn" &&
        cd .. &&
        rm -rf t.svn
-       "
+       '
 
 test_expect_success 'multiple dcommit from git-svn will not clobber svn' "
        git reset --hard refs/remotes/git-svn &&
index ca8a00ed0af0382c20a4883e5aa5f1914b6ecd3f..a400dc796666e820c27dd6989ef9ee1b9922fad3 100755 (executable)
@@ -4,30 +4,30 @@
 test_description='git-svn dcommit clobber series'
 . ./lib-git-svn.sh
 
-test_expect_success 'initialize repo' "
+test_expect_success 'initialize repo' '
        mkdir import &&
        cd import &&
-       awk 'BEGIN { for (i = 1; i < 64; i++) { print i } }' > file
-       svn import -m 'initial' . $svnrepo &&
+       awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+       svn import -m "initial" . "$svnrepo" &&
        cd .. &&
-       git svn init $svnrepo &&
+       git svn init "$svnrepo" &&
        git svn fetch &&
        test -e file
-       "
+       '
 
-test_expect_success '(supposedly) non-conflicting change from SVN' "
-       test x\"\`sed -n -e 58p < file\`\" = x58 &&
-       test x\"\`sed -n -e 61p < file\`\" = x61 &&
-       svn co $svnrepo tmp &&
+test_expect_success '(supposedly) non-conflicting change from SVN' '
+       test x"`sed -n -e 58p < file`" = x58 &&
+       test x"`sed -n -e 61p < file`" = x61 &&
+       svn co "$svnrepo" tmp &&
        cd tmp &&
-               perl -i -p -e 's/^58\$/5588/' file &&
-               perl -i -p -e 's/^61\$/6611/' file &&
+               perl -i -p -e "s/^58$/5588/" file &&
+               perl -i -p -e "s/^61$/6611/" file &&
                poke file &&
-               test x\"\`sed -n -e 58p < file\`\" = x5588 &&
-               test x\"\`sed -n -e 61p < file\`\" = x6611 &&
-               svn commit -m '58 => 5588, 61 => 6611' &&
+               test x"`sed -n -e 58p < file`" = x5588 &&
+               test x"`sed -n -e 61p < file`" = x6611 &&
+               svn commit -m "58 => 5588, 61 => 6611" &&
                cd ..
-       "
+       '
 
 test_expect_success 'some unrelated changes to git' "
        echo hi > life &&
index 0a41d52c7a734c530cd93e8f536aa9a0a8b5a3f6..d9b553ad55b1f7024af0689a450a9c6c65dcb034 100755 (executable)
@@ -3,61 +3,61 @@
 test_description='git-svn metadata migrations from previous versions'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup old-looking metadata' "
-       cp $GIT_DIR/config $GIT_DIR/config-old-git-svn &&
+test_expect_success 'setup old-looking metadata' '
+       cp "$GIT_DIR"/config "$GIT_DIR"/config-old-git-svn &&
        mkdir import &&
        cd import &&
                for i in trunk branches/a branches/b \
                         tags/0.1 tags/0.2 tags/0.3; do
-                       mkdir -p \$i && \
-                       echo hello >> \$i/README || exit 1
+                       mkdir -p $i && \
+                       echo hello >> $i/README || exit 1
                done && \
-               svn import -m test . $svnrepo
+               svn import -m test . "$svnrepo"
                cd .. &&
-       git-svn init $svnrepo &&
+       git-svn init "$svnrepo" &&
        git-svn fetch &&
-       mv $GIT_DIR/svn/* $GIT_DIR/ &&
-       mv $GIT_DIR/svn/.metadata $GIT_DIR/ &&
-       rmdir $GIT_DIR/svn &&
+       mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
+       mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
+       rmdir "$GIT_DIR"/svn &&
        git update-ref refs/heads/git-svn-HEAD refs/remotes/git-svn &&
        git update-ref refs/heads/svn-HEAD refs/remotes/git-svn &&
        git update-ref -d refs/remotes/git-svn refs/remotes/git-svn
-       "
+       '
 
 head=`git rev-parse --verify refs/heads/git-svn-HEAD^0`
 test_expect_success 'git-svn-HEAD is a real HEAD' "test -n '$head'"
 
-test_expect_success 'initialize old-style (v0) git-svn layout' "
-       mkdir -p $GIT_DIR/git-svn/info $GIT_DIR/svn/info &&
-       echo $svnrepo > $GIT_DIR/git-svn/info/url &&
-       echo $svnrepo > $GIT_DIR/svn/info/url &&
+test_expect_success 'initialize old-style (v0) git-svn layout' '
+       mkdir -p "$GIT_DIR"/git-svn/info "$GIT_DIR"/svn/info &&
+       echo "$svnrepo" > "$GIT_DIR"/git-svn/info/url &&
+       echo "$svnrepo" > "$GIT_DIR"/svn/info/url &&
        git-svn migrate &&
-       ! test -d $GIT_DIR/git-svn &&
+       ! test -d "$GIT_DIR"/git-svn &&
        git rev-parse --verify refs/remotes/git-svn^0 &&
        git rev-parse --verify refs/remotes/svn^0 &&
-       test \`git config --get svn-remote.svn.url\` = '$svnrepo' &&
-       test \`git config --get svn-remote.svn.fetch\` = \
-             ':refs/remotes/git-svn'
-       "
+       test "$(git config --get svn-remote.svn.url)" = "$svnrepo" &&
+       test `git config --get svn-remote.svn.fetch` = \
+             ":refs/remotes/git-svn"
+       '
 
-test_expect_success 'initialize a multi-repository repo' "
-       git-svn init $svnrepo -T trunk -t tags -b branches &&
+test_expect_success 'initialize a multi-repository repo' '
+       git-svn init "$svnrepo" -T trunk -t tags -b branches &&
        git config --get-all svn-remote.svn.fetch > fetch.out &&
-       grep '^trunk:refs/remotes/trunk$' fetch.out &&
-       test -n \"\`git config --get svn-remote.svn.branches \
-                   '^branches/\*:refs/remotes/\*$'\`\" &&
-       test -n \"\`git config --get svn-remote.svn.tags \
-                   '^tags/\*:refs/remotes/tags/\*$'\`\" &&
+       grep "^trunk:refs/remotes/trunk$" fetch.out &&
+       test -n "`git config --get svn-remote.svn.branches \
+                   "^branches/\*:refs/remotes/\*$"`" &&
+       test -n "`git config --get svn-remote.svn.tags \
+                   "^tags/\*:refs/remotes/tags/\*$"`" &&
        git config --unset svn-remote.svn.branches \
-                               '^branches/\*:refs/remotes/\*$' &&
+                               "^branches/\*:refs/remotes/\*$" &&
        git config --unset svn-remote.svn.tags \
-                               '^tags/\*:refs/remotes/tags/\*$' &&
-       git config --add svn-remote.svn.fetch 'branches/a:refs/remotes/a' &&
-       git config --add svn-remote.svn.fetch 'branches/b:refs/remotes/b' &&
+                               "^tags/\*:refs/remotes/tags/\*$" &&
+       git config --add svn-remote.svn.fetch "branches/a:refs/remotes/a" &&
+       git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
        for i in tags/0.1 tags/0.2 tags/0.3; do
                git config --add svn-remote.svn.fetch \
-                                \$i:refs/remotes/\$i || exit 1; done
-       "
+                                $i:refs/remotes/$i || exit 1; done
+       '
 
 # refs should all be different, but the trees should all be the same:
 test_expect_success 'multi-fetch works on partial urls + paths' "
@@ -73,43 +73,43 @@ test_expect_success 'multi-fetch works on partial urls + paths' "
                                 refs/remotes/\$j\`\" ||exit 1; done; done
        "
 
-test_expect_success 'migrate --minimize on old inited layout' "
+test_expect_success 'migrate --minimize on old inited layout' '
        git config --unset-all svn-remote.svn.fetch &&
        git config --unset-all svn-remote.svn.url &&
-       rm -rf $GIT_DIR/svn &&
-       for i in \`cat fetch.out\`; do
-               path=\`expr \$i : '\\([^:]*\\):.*$'\`
-               ref=\`expr \$i : '[^:]*:refs/remotes/\\(.*\\)$'\`
-               if test -z \"\$ref\"; then continue; fi
-               if test -n \"\$path\"; then path=\"/\$path\"; fi
-               ( mkdir -p $GIT_DIR/svn/\$ref/info/ &&
-               echo $svnrepo\$path > $GIT_DIR/svn/\$ref/info/url ) || exit 1;
+       rm -rf "$GIT_DIR"/svn &&
+       for i in `cat fetch.out`; do
+               path=`expr $i : "\([^:]*\):.*$"`
+               ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
+               if test -z "$ref"; then continue; fi
+               if test -n "$path"; then path="/$path"; fi
+               ( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
+               echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
        done &&
        git-svn migrate --minimize &&
-       test -z \"\`git config -l |grep -v '^svn-remote\.git-svn\.'\`\" &&
+       test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
        git config --get-all svn-remote.svn.fetch > fetch.out &&
-       grep '^trunk:refs/remotes/trunk$' fetch.out &&
-       grep '^branches/a:refs/remotes/a$' fetch.out &&
-       grep '^branches/b:refs/remotes/b$' fetch.out &&
-       grep '^tags/0\.1:refs/remotes/tags/0\.1$' fetch.out &&
-       grep '^tags/0\.2:refs/remotes/tags/0\.2$' fetch.out &&
-       grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out
-       grep '^:refs/remotes/git-svn' fetch.out
-       "
+       grep "^trunk:refs/remotes/trunk$" fetch.out &&
+       grep "^branches/a:refs/remotes/a$" fetch.out &&
+       grep "^branches/b:refs/remotes/b$" fetch.out &&
+       grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+       grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+       grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
+       grep "^:refs/remotes/git-svn" fetch.out
+       '
 
-test_expect_success  ".rev_db auto-converted to .rev_map.UUID" "
+test_expect_success  ".rev_db auto-converted to .rev_map.UUID" '
        git-svn fetch -i trunk &&
-       test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
-       expect=\"\$(ls $GIT_DIR/svn/trunk/.rev_map.*)\" &&
-       test -n \"\$expect\" &&
-       rev_db=\$(echo \$expect | sed -e 's,_map,_db,') &&
-       convert_to_rev_db \$expect \$rev_db &&
-       rm -f \$expect &&
-       test -f \$rev_db &&
+       test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
+       expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
+       test -n "$expect" &&
+       rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
+       convert_to_rev_db "$expect" "$rev_db" &&
+       rm -f "$expect" &&
+       test -f "$rev_db" &&
        git-svn fetch -i trunk &&
-       test -z \"\$(ls $GIT_DIR/svn/trunk/.rev_db.* 2>/dev/null)\" &&
-       test ! -e $GIT_DIR/svn/trunk/.rev_db &&
-       test -f \$expect
-       "
+       test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
+       test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
+       test -f "$expect"
+       '
 
 test_done
index db4344cc84395fcecee2010320f7fc46101d75a6..f6f71d0545c869a7216eb0e81f260085f6ffdec1 100755 (executable)
@@ -10,77 +10,77 @@ start a new branch
 initial
 EOF
 
-test_expect_success 'test refspec globbing' "
+test_expect_success 'test refspec globbing' '
        mkdir -p trunk/src/a trunk/src/b trunk/doc &&
-       echo 'hello world' > trunk/src/a/readme &&
-       echo 'goodbye world' > trunk/src/b/readme &&
-       svn import -m 'initial' trunk $svnrepo/trunk &&
-       svn co $svnrepo tmp &&
+       echo "hello world" > trunk/src/a/readme &&
+       echo "goodbye world" > trunk/src/b/readme &&
+       svn import -m "initial" trunk "$svnrepo"/trunk &&
+       svn co "$svnrepo" tmp &&
        cd tmp &&
                mkdir branches tags &&
                svn add branches tags &&
                svn cp trunk branches/start &&
-               svn commit -m 'start a new branch' &&
+               svn commit -m "start a new branch" &&
                svn up &&
-               echo 'hi' >> branches/start/src/b/readme &&
+               echo "hi" >> branches/start/src/b/readme &&
                poke branches/start/src/b/readme &&
-               echo 'hey' >> branches/start/src/a/readme &&
+               echo "hey" >> branches/start/src/a/readme &&
                poke branches/start/src/a/readme &&
-               svn commit -m 'hi' &&
+               svn commit -m "hi" &&
                svn up &&
                svn cp branches/start tags/end &&
-               echo 'bye' >> tags/end/src/b/readme &&
+               echo "bye" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               echo 'aye' >> tags/end/src/a/readme &&
+               echo "aye" >> tags/end/src/a/readme &&
                poke tags/end/src/a/readme &&
-               svn commit -m 'the end' &&
-               echo 'byebye' >> tags/end/src/b/readme &&
+               svn commit -m "the end" &&
+               echo "byebye" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m 'nothing to see here'
+               svn commit -m "nothing to see here"
                cd .. &&
-       git config --add svn-remote.svn.url $svnrepo &&
+       git config --add svn-remote.svn.url "$svnrepo" &&
        git config --add svn-remote.svn.fetch \
-                        'trunk/src/a:refs/remotes/trunk' &&
+                        "trunk/src/a:refs/remotes/trunk" &&
        git config --add svn-remote.svn.branches \
-                        'branches/*/src/a:refs/remotes/branches/*' &&
+                        "branches/*/src/a:refs/remotes/branches/*" &&
        git config --add svn-remote.svn.tags\
-                        'tags/*/src/a:refs/remotes/tags/*' &&
+                        "tags/*/src/a:refs/remotes/tags/*" &&
        git-svn multi-fetch &&
        git log --pretty=oneline refs/remotes/tags/end | \
-           sed -e 's/^.\{41\}//' > output.end &&
+           sed -e "s/^.\{41\}//" > output.end &&
        cmp expect.end output.end &&
-       test \"\`git rev-parse refs/remotes/tags/end~1\`\" = \
-               \"\`git rev-parse refs/remotes/branches/start\`\" &&
-       test \"\`git rev-parse refs/remotes/branches/start~2\`\" = \
-               \"\`git rev-parse refs/remotes/trunk\`\"
-       "
+       test "`git rev-parse refs/remotes/tags/end~1`" = \
+               "`git rev-parse refs/remotes/branches/start`" &&
+       test "`git rev-parse refs/remotes/branches/start~2`" = \
+               "`git rev-parse refs/remotes/trunk`"
+       '
 
 echo try to try > expect.two
 echo nothing to see here >> expect.two
 cat expect.end >> expect.two
 
-test_expect_success 'test left-hand-side only globbing' "
-       git config --add svn-remote.two.url $svnrepo &&
+test_expect_success 'test left-hand-side only globbing' '
+       git config --add svn-remote.two.url "$svnrepo" &&
        git config --add svn-remote.two.fetch trunk:refs/remotes/two/trunk &&
        git config --add svn-remote.two.branches \
-                        'branches/*:refs/remotes/two/branches/*' &&
+                        "branches/*:refs/remotes/two/branches/*" &&
        git config --add svn-remote.two.tags \
-                        'tags/*:refs/remotes/two/tags/*' &&
+                        "tags/*:refs/remotes/two/tags/*" &&
        cd tmp &&
-               echo 'try try' >> tags/end/src/b/readme &&
+               echo "try try" >> tags/end/src/b/readme &&
                poke tags/end/src/b/readme &&
-               svn commit -m 'try to try'
+               svn commit -m "try to try"
                cd .. &&
        git-svn fetch two &&
-       test \`git rev-list refs/remotes/two/tags/end | wc -l\` -eq 6 &&
-       test \`git rev-list refs/remotes/two/branches/start | wc -l\` -eq 3 &&
-       test \`git rev-parse refs/remotes/two/branches/start~2\` = \
-            \`git rev-parse refs/remotes/two/trunk\` &&
-       test \`git rev-parse refs/remotes/two/tags/end~3\` = \
-            \`git rev-parse refs/remotes/two/branches/start\` &&
+       test `git rev-list refs/remotes/two/tags/end | wc -l` -eq 6 &&
+       test `git rev-list refs/remotes/two/branches/start | wc -l` -eq 3 &&
+       test `git rev-parse refs/remotes/two/branches/start~2` = \
+            `git rev-parse refs/remotes/two/trunk` &&
+       test `git rev-parse refs/remotes/two/tags/end~3` = \
+            `git rev-parse refs/remotes/two/branches/start` &&
        git log --pretty=oneline refs/remotes/two/tags/end | \
-           sed -e 's/^.\{41\}//' > output.two &&
+           sed -e "s/^.\{41\}//" > output.two &&
        cmp expect.two output.two
-       "
+       '
 
 test_done
index 6235af4db8b0b744d40156f90a0afed469952441..047659fde1049777e7fe12113ecb305377b1f8f9 100755 (executable)
@@ -7,15 +7,15 @@ test_description='git-svn useSvmProps test'
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load svm repo' "
-       svnadmin load -q $rawsvnrepo < ../t9110/svm.dump &&
-       git-svn init --minimize-url -R arr -i bar $svnrepo/mirror/arr &&
-       git-svn init --minimize-url -R argh -i dir $svnrepo/mirror/argh &&
+test_expect_success 'load svm repo' '
+       svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump &&
+       git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
+       git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
        git-svn init --minimize-url -R argh -i e \
-         $svnrepo/mirror/argh/a/b/c/d/e &&
+         "$svnrepo"/mirror/argh/a/b/c/d/e &&
        git config svn.useSvmProps true &&
        git-svn fetch --all
-       "
+       '
 
 uuid=161ce429-a9dd-4828-af4a-52023f968c89
 
index ec7dedd48b0e45278b33e3c5d4f97d8df5f85ddf..a8d74dcd3aba7c462d46ea33c722d4307d24bded 100755 (executable)
@@ -7,14 +7,14 @@ test_description='git-svn useSvnsyncProps test'
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load svnsync repo' "
-       svnadmin load -q $rawsvnrepo < ../t9111/svnsync.dump &&
-       git-svn init --minimize-url -R arr -i bar $svnrepo/bar &&
-       git-svn init --minimize-url -R argh -i dir $svnrepo/dir &&
-       git-svn init --minimize-url -R argh -i e $svnrepo/dir/a/b/c/d/e &&
+test_expect_success 'load svnsync repo' '
+       svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump &&
+       git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
+       git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
+       git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
        git config svn.useSvnsyncProps true &&
        git-svn fetch --all
-       "
+       '
 
 uuid=161ce429-a9dd-4828-af4a-52023f968c89
 
index 646a5f0cdb78a960faac59d202b12531542d7715..d470a920e4864ab0c494da1261fe835ff80474eb 100755 (executable)
@@ -40,8 +40,8 @@ PROPS-END
 
 EOF
 
-test_expect_success 'load svn dumpfile' "svnadmin load $rawsvnrepo < dumpfile.svn"
+test_expect_success 'load svn dumpfile' 'svnadmin load "$rawsvnrepo" < dumpfile.svn'
 
-test_expect_success 'initialize git-svn' "git-svn init $svnrepo"
+test_expect_success 'initialize git-svn' 'git-svn init "$svnrepo"'
 test_expect_success 'fetch revisions from svn' 'git-svn fetch'
 test_done
index 9ef0db9044dcd8f01baafd8c141a51a981df27d4..31c929b57368fe08f35301a75bff48e19b86a06d 100755 (executable)
@@ -15,18 +15,18 @@ test_description='git-svn dcommit new files over svn:// test'
 
 start_svnserve () {
        svnserve --listen-port $SVNSERVE_PORT \
-                --root $rawsvnrepo \
+                --root "$rawsvnrepo" \
                 --listen-once \
                 --listen-host 127.0.0.1 &
 }
 
-test_expect_success 'start tracking an empty repo' "
-       svn mkdir -m 'empty dir' $svnrepo/empty-dir &&
-       echo anon-access = write >> $rawsvnrepo/conf/svnserve.conf &&
+test_expect_success 'start tracking an empty repo' '
+       svn mkdir -m "empty dir" "$svnrepo"/empty-dir &&
+       echo anon-access = write >> "$rawsvnrepo"/conf/svnserve.conf &&
        start_svnserve &&
        git svn init svn://127.0.0.1:$SVNSERVE_PORT &&
        git svn fetch
-       "
+       '
 
 test_expect_success 'create files in new directory with dcommit' "
        mkdir git-new-dir &&
index 225060b88bc7533c02d9acffbe9d4b01f848050e..61d7781616eed4374c014cabd75a297c2baa348d 100755 (executable)
@@ -34,35 +34,35 @@ cat << EOF
 EOF
 }
 
-test_expect_success 'setup svn repository' "
-       svn co $svnrepo mysvnwork &&
+test_expect_success 'setup svn repository' '
+       svn co "$svnrepo" mysvnwork &&
        mkdir -p mysvnwork/trunk &&
        cd mysvnwork &&
                big_text_block >> trunk/README &&
                svn add trunk &&
-               svn ci -m 'first commit' trunk &&
+               svn ci -m "first commit" trunk &&
                cd ..
-       "
+       '
 
-test_expect_success 'setup git mirror and merge' "
-       git svn init $svnrepo -t tags -T trunk -b branches &&
+test_expect_success 'setup git mirror and merge' '
+       git svn init "$svnrepo" -t tags -T trunk -b branches &&
        git svn fetch &&
        git checkout --track -b svn remotes/trunk &&
        git checkout -b merge &&
        echo new file > new_file &&
        git add new_file &&
-       git commit -a -m 'New file' &&
+       git commit -a -m "New file" &&
        echo hello >> README &&
-       git commit -a -m 'hello' &&
+       git commit -a -m "hello" &&
        echo add some stuff >> new_file &&
-       git commit -a -m 'add some stuff' &&
+       git commit -a -m "add some stuff" &&
        git checkout svn &&
        mv -f README tmp &&
        echo friend > README &&
        cat tmp >> README &&
-       git commit -a -m 'friend' &&
+       git commit -a -m "friend" &&
        git pull . merge
-       "
+       '
 
 test_debug 'gitk --all & sleep 1'
 
index 182299cbb53a8bae9e3cd51f9aff9073e08559c1..f0fbd3aff7e63f64f8ba388db805013c43b4b22c 100755 (executable)
@@ -7,16 +7,16 @@ test_description='git-svn dcommit can commit renames of files with ugly names'
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load repository with strange names' "
-       svnadmin load -q $rawsvnrepo < ../t9115/funky-names.dump &&
-       start_httpd
-       "
+test_expect_success 'load repository with strange names' '
+       svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump &&
+       start_httpd gtk+
+       '
 
-test_expect_success 'init and fetch repository' "
-       git svn init $svnrepo &&
+test_expect_success 'init and fetch repository' '
+       git svn init "$svnrepo" &&
        git svn fetch &&
        git reset --hard git-svn
-       "
+       '
 
 test_expect_success 'create file in existing ugly and empty dir' '
        mkdir "#{bad_directory_name}" &&
@@ -49,6 +49,39 @@ test_expect_success 'rename pretty file into ugly one' '
        git svn dcommit
        '
 
+test_expect_success 'add a file with plus signs' '
+       echo .. > +_+ &&
+       git update-index --add +_+ &&
+       git commit -m plus &&
+       mkdir gtk+ &&
+       git mv +_+ gtk+/_+_ &&
+       git commit -m plus_dir &&
+       git svn dcommit
+       '
+
+test_expect_success 'clone the repository to test rebase' '
+       git svn clone "$svnrepo" test-rebase &&
+       cd test-rebase &&
+               echo test-rebase > test-rebase &&
+               git add test-rebase &&
+               git commit -m test-rebase &&
+               cd ..
+       '
+
+test_expect_success 'make a commit to test rebase' '
+               echo test-rebase-main > test-rebase-main &&
+               git add test-rebase-main &&
+               git commit -m test-rebase-main &&
+               git svn dcommit
+       '
+
+test_expect_success 'git-svn rebase works inside a fresh-cloned repository' '
+       cd test-rebase &&
+               git svn rebase &&
+               test -e test-rebase-main &&
+               test -e test-rebase
+       '
+
 stop_httpd
 
 test_done
index e1e8bdf0e37928c20e6bf24c08aaf574268cb037..4b2cc878f685e65b2ccd5d8153efb533320d6ee9 100755 (executable)
@@ -6,17 +6,17 @@
 test_description='git-svn log tests'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup repository and import' "
+test_expect_success 'setup repository and import' '
        mkdir import &&
        cd import &&
                for i in trunk branches/a branches/b \
                         tags/0.1 tags/0.2 tags/0.3; do
-                       mkdir -p \$i && \
-                       echo hello >> \$i/README || exit 1
+                       mkdir -p $i && \
+                       echo hello >> $i/README || exit 1
                done && \
-               svn import -m test . $svnrepo
+               svn import -m test . "$svnrepo"
                cd .. &&
-       git-svn init $svnrepo -T trunk -b branches -t tags &&
+       git-svn init "$svnrepo" -T trunk -b branches -t tags &&
        git-svn fetch &&
        git reset --hard trunk &&
        echo bye >> README &&
@@ -37,7 +37,7 @@ test_expect_success 'setup repository and import' "
        echo try >> README &&
        git commit -a -m try &&
        git svn dcommit
-       "
+       '
 
 test_expect_success 'run log' "
        git reset --hard a &&
index d482b407f2ae17591a1a345f9f1ffd73cc9c5a97..7a689bb1cd1d9daa1f17c0dcaaafa4d68ebd78fa 100755 (executable)
@@ -13,43 +13,43 @@ rm -r .git
 mkdir tmp
 cd tmp
 
-test_expect_success 'setup svnrepo' "
+test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m '$test_description' project $svnrepo/project &&
+       svn import -m "$test_description" project "$svnrepo"/project &&
        rm -rf project
-       "
+       '
 
-test_expect_success 'basic clone' "
+test_expect_success 'basic clone' '
        test ! -d trunk &&
-       git svn clone $svnrepo/project/trunk &&
+       git svn clone "$svnrepo"/project/trunk &&
        test -d trunk/.git/svn &&
        test -e trunk/foo &&
        rm -rf trunk
-       "
+       '
 
-test_expect_success 'clone to target directory' "
+test_expect_success 'clone to target directory' '
        test ! -d target &&
-       git svn clone $svnrepo/project/trunk target &&
+       git svn clone "$svnrepo"/project/trunk target &&
        test -d target/.git/svn &&
        test -e target/foo &&
        rm -rf target
-       "
+       '
 
-test_expect_success 'clone with --stdlayout' "
+test_expect_success 'clone with --stdlayout' '
        test ! -d project &&
-       git svn clone -s $svnrepo/project &&
+       git svn clone -s "$svnrepo"/project &&
        test -d project/.git/svn &&
        test -e project/foo &&
        rm -rf project
-       "
+       '
 
-test_expect_success 'clone to target directory with --stdlayout' "
+test_expect_success 'clone to target directory with --stdlayout' '
        test ! -d target &&
-       git svn clone -s $svnrepo/project target &&
+       git svn clone -s "$svnrepo"/project target &&
        test -d target/.git/svn &&
        test -e target/foo &&
        rm -rf target
-       "
+       '
 
 test_done
index 640bb066f380d6306ebb76e3dedf38c60cfab0b5..3281cbd3472a8da58c4f6f0f3965b5810705b0e9 100755 (executable)
@@ -6,25 +6,25 @@
 test_description='git-svn funky branch names'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup svnrepo' "
+test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m '$test_description' project \"$svnrepo/pr ject\" &&
+       svn import -m "$test_description" project "$svnrepo/pr ject" &&
        rm -rf project &&
-       svn cp -m 'fun' \"$svnrepo/pr ject/trunk\" \
-                       \"$svnrepo/pr ject/branches/fun plugin\" &&
-       svn cp -m 'more fun!' \"$svnrepo/pr ject/branches/fun plugin\" \
-                             \"$svnrepo/pr ject/branches/more fun plugin!\" &&
+       svn cp -m "fun" "$svnrepo/pr ject/trunk" \
+                       "$svnrepo/pr ject/branches/fun plugin" &&
+       svn cp -m "more fun!" "$svnrepo/pr ject/branches/fun plugin" \
+                             "$svnrepo/pr ject/branches/more fun plugin!" &&
        start_httpd
-       "
+       '
 
-test_expect_success 'test clone with funky branch names' "
-       git svn clone -s \"$svnrepo/pr ject\" project &&
+test_expect_success 'test clone with funky branch names' '
+       git svn clone -s "$svnrepo/pr ject" project &&
        cd project &&
-               git rev-parse 'refs/remotes/fun%20plugin' &&
-               git rev-parse 'refs/remotes/more%20fun%20plugin!' &&
+               git rev-parse "refs/remotes/fun%20plugin" &&
+               git rev-parse "refs/remotes/more%20fun%20plugin!" &&
        cd ..
-       "
+       '
 
 test_expect_success 'test dcommit to funky branch' "
        cd project &&
index 9a4eabe523c8560da9559b5c0ca6a951a027ca5d..5979e133b9d5b9d85ddca31a40763ed4fb6feba3 100755 (executable)
@@ -6,13 +6,13 @@
 test_description='git-svn clone with percent escapes'
 . ./lib-git-svn.sh
 
-test_expect_success 'setup svnrepo' "
+test_expect_success 'setup svnrepo' '
        mkdir project project/trunk project/branches project/tags &&
        echo foo > project/trunk/foo &&
-       svn import -m '$test_description' project '$svnrepo/pr ject' &&
+       svn import -m "$test_description" project "$svnrepo/pr ject" &&
        rm -rf project &&
        start_httpd
-"
+'
 
 if test "$SVN_HTTPD_PORT" = ""
 then
index 5143ed606612a0a66e382f799e64f3ff48f16b9e..99230b08107102836f752c14e1b0a67804b35ea3 100755 (executable)
@@ -7,14 +7,14 @@ test_description='git-svn can fetch renamed directories'
 
 . ./lib-git-svn.sh
 
-test_expect_success 'load repository with renamed directory' "
-       svnadmin load -q $rawsvnrepo < ../t9121/renamed-dir.dump
-       "
+test_expect_success 'load repository with renamed directory' '
+       svnadmin load -q "$rawsvnrepo" < ../t9121/renamed-dir.dump
+       '
 
-test_expect_success 'init and fetch repository' "
-       git svn init $svnrepo/newname &&
+test_expect_success 'init and fetch repository' '
+       git svn init "$svnrepo/newname" &&
        git svn fetch
-       "
+       '
 
 test_done
 
index 166b43f783517debf1de716aed4c1952ab1fea4b..e97aaa6c2a2fdcc476e538ffe903cef65274c280 100755 (executable)
@@ -153,21 +153,21 @@ test_expect_success 'req_Root failure (conflicting roots)' \
    tail log | grep "^error 1 Conflicting roots specified$"'
 
 test_expect_success 'req_Root (strict paths)' \
-  'cat request-anonymous | git-cvsserver --strict-paths pserver $SERVERDIR >log 2>&1 &&
+  'cat request-anonymous | git-cvsserver --strict-paths pserver "$SERVERDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (strict-paths)' '
     ! cat request-anonymous |
-    git-cvsserver --strict-paths pserver $WORKDIR >log 2>&1
+    git-cvsserver --strict-paths pserver "$WORKDIR" >log 2>&1
 '
 
 test_expect_success 'req_Root (w/o strict-paths)' \
-  'cat request-anonymous | git-cvsserver pserver $WORKDIR/ >log 2>&1 &&
+  'cat request-anonymous | git-cvsserver pserver "$WORKDIR/" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (w/o strict-paths)' '
     ! cat request-anonymous |
-    git-cvsserver pserver $WORKDIR/gitcvs >log 2>&1
+    git-cvsserver pserver "$WORKDIR/gitcvs" >log 2>&1
 '
 
 cat >request-base  <<EOF
@@ -180,25 +180,25 @@ Root /gitcvs.git
 EOF
 
 test_expect_success 'req_Root (base-path)' \
-  'cat request-base | git-cvsserver --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
+  'cat request-base | git-cvsserver --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (base-path)' '
     ! cat request-anonymous |
-    git-cvsserver --strict-paths --base-path $WORKDIR pserver $SERVERDIR >log 2>&1
+    git-cvsserver --strict-paths --base-path "$WORKDIR" pserver "$SERVERDIR" >log 2>&1
 '
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled false || exit 1
 
 test_expect_success 'req_Root (export-all)' \
-  'cat request-anonymous | git-cvsserver --export-all pserver $WORKDIR >log 2>&1 &&
+  'cat request-anonymous | git-cvsserver --export-all pserver "$WORKDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 test_expect_success 'req_Root failure (export-all w/o whitelist)' \
   '! (cat request-anonymous | git-cvsserver --export-all pserver >log 2>&1 || false)'
 
 test_expect_success 'req_Root (everything together)' \
-  'cat request-base | git-cvsserver --export-all --strict-paths --base-path $WORKDIR/ pserver $SERVERDIR >log 2>&1 &&
+  'cat request-base | git-cvsserver --export-all --strict-paths --base-path "$WORKDIR/" pserver "$SERVERDIR" >log 2>&1 &&
    sed -ne \$p log | grep "^I LOVE YOU$"'
 
 GIT_DIR="$SERVERDIR" git config --bool gitcvs.enabled true || exit 1
index 061a2596d3b63e7d737d735c4a6cb097bfcf387f..ae7082be1d903e1f4d5758610d5166152f2847cc 100755 (executable)
@@ -10,6 +10,7 @@ commandline, and checks that it would not write any errors
 or warnings to log.'
 
 gitweb_init () {
+       safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
        cat >gitweb_config.perl <<EOF
 #!/usr/bin/perl
 
@@ -17,16 +18,16 @@ gitweb_init () {
 
 our \$version = "current";
 our \$GIT = "git";
-our \$projectroot = "$(pwd)";
+our \$projectroot = "$safe_pwd";
 our \$project_maxdepth = 8;
 our \$home_link_str = "projects";
 our \$site_name = "[localhost]";
 our \$site_header = "";
 our \$site_footer = "";
 our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$(pwd)/../../gitweb/gitweb.css");
-our \$logo = "file:///$(pwd)/../../gitweb/git-logo.png";
-our \$favicon = "file:///$(pwd)/../../gitweb/git-favicon.png";
+our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
+our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
+our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
 our \$projects_list = "";
 our \$export_ok = "";
 our \$strict_export = "";
@@ -39,19 +40,21 @@ EOF
 }
 
 gitweb_run () {
-       export GATEWAY_INTERFACE="CGI/1.1"
-       export HTTP_ACCEPT="*/*"
-       export REQUEST_METHOD="GET"
-       export QUERY_STRING=""$1""
-       export PATH_INFO=""$2""
+       GATEWAY_INTERFACE="CGI/1.1"
+       HTTP_ACCEPT="*/*"
+       REQUEST_METHOD="GET"
+       QUERY_STRING=""$1""
+       PATH_INFO=""$2""
+       export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD QUERY_STRING PATH_INFO
 
-       export GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+       GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+       export GITWEB_CONFIG
 
        # some of git commands write to STDERR on error, but this is not
        # written to web server logs, so we are not interested in that:
        # we are interested only in properly formatted errors/warnings
        rm -f gitweb.log &&
-       perl -- $(pwd)/../../gitweb/gitweb.perl \
+       perl -- "$(pwd)/../../gitweb/gitweb.perl" \
                >/dev/null 2>gitweb.log &&
        if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
 
index 00a74ee7384e4b824c9faf9e6d7891e4f28c775a..0b115a17ab46e02ca7ec67f08feada0c48c8628a 100755 (executable)
@@ -36,7 +36,7 @@ test_expect_success 'setup cvsroot' 'cvs init'
 
 test_expect_success 'setup a cvs module' '
 
-       mkdir $CVSROOT/module &&
+       mkdir "$CVSROOT/module" &&
        cvs co -d module-cvs module &&
        cd module-cvs &&
        cat <<EOF >o_fortuna &&
index 7c2a8ba77d312c3196767969a39687bfb12f74c9..3bf570b068dd56c802e7bdcef0384aa8e1209985 100644 (file)
@@ -160,6 +160,22 @@ die () {
 
 trap 'die' exit
 
+# The semantics of the editor variables are that of invoking
+# sh -c "$EDITOR \"$@\"" files ...
+#
+# If our trash directory contains shell metacharacters, they will be
+# interpreted if we just set $EDITOR directly, so do a little dance with
+# environment variables to work around this.
+#
+# In particular, quoting isn't enough, as the path may contain the same quote
+# that we're using. 
+test_set_editor () {
+       FAKE_EDITOR="$1"
+       export FAKE_EDITOR
+       VISUAL='"$FAKE_EDITOR"'
+       export VISUAL
+}
+
 test_tick () {
        if test -z "${test_tick+set}"
        then
@@ -329,7 +345,7 @@ test_create_repo () {
        repo="$1"
        mkdir "$repo"
        cd "$repo" || error "Cannot setup test environment"
-       "$GIT_EXEC_PATH/git" init --template=$GIT_EXEC_PATH/templates/blt/ >/dev/null 2>&1 ||
+       "$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >/dev/null 2>&1 ||
        error "cannot run git init -- have you built things yet?"
        mv .git/hooks .git/hooks-disabled
        cd "$owd"
@@ -352,7 +368,7 @@ test_done () {
        case "$test_failure" in
        0)
                # We could:
-               # cd .. && rm -fr trash
+               # cd .. && rm -fr 'trash directory'
                # but that means we forbid any tests that use their own
                # subdirectory from calling test_done without coming back
                # to where they started from.
@@ -395,15 +411,15 @@ fi
 . ../GIT-BUILD-OPTIONS
 
 # Test repository
-test=trash
+test="trash directory"
 rm -fr "$test" || {
        trap - exit
        echo >&5 "FATAL: Cannot prepare test area"
        exit 1
 }
 
-test_create_repo $test
-cd "$test"
+test_create_repo "$test"
+cd "$test" || exit 1
 
 this_test=$(expr "./$0" : '.*/\(t[0-9]*\)-[^/]*$')
 for skp in $GIT_SKIP_TESTS
index ff0f42a1d984cdcd4b647e713eaec668177fb1f6..d3c1da34d2c7cbdd66ca142b5bf0ec5b877e1baa 100644 (file)
 # The third example adds a Signed-off-by line to the message, that can
 # still be edited.  This is rarely a good idea.
 
-case "$2 $3" in
-  merge)
-    sed -i '/^Conflicts:/,/#/!b;s/^/# &/;s/^# #/#/' "$1" ;;
+case "$2,$3" in
+  merge,)
+    perl -i -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
 
-# ""|template)
+# ,|template,)
 #   perl -i -pe '
 #      print "\n" . `git diff --cached --name-status -r`
 #       if /^#/ && $first++ == 0' "$1" ;;
index 393e0e8fe233ca51dc4c5db94c55e81ee53baa52..1bc16f2b65f46650c53227758df8431cf5a77598 100644 (file)
@@ -441,10 +441,14 @@ static struct ref *get_refs_via_curl(struct transport *transport)
        struct ref *ref = NULL;
        struct ref *last_ref = NULL;
 
+       struct walker *walker;
+
        if (!transport->data)
                transport->data = get_http_walker(transport->url,
                                                transport->remote);
 
+       walker = transport->data;
+
        refs_url = xmalloc(strlen(transport->url) + 11);
        sprintf(refs_url, "%s/info/refs", transport->url);
 
@@ -500,6 +504,15 @@ static struct ref *get_refs_via_curl(struct transport *transport)
 
        strbuf_release(&buffer);
 
+       ref = alloc_ref_from_str("HEAD");
+       if (!walker->fetch_ref(walker, ref) &&
+           !resolve_remote_symref(ref, refs)) {
+               ref->next = refs;
+               refs = ref;
+       } else {
+               free(ref);
+       }
+
        return refs;
 }
 
@@ -532,9 +545,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport)
                die ("Could not read bundle '%s'.", transport->url);
        for (i = 0; i < data->header.references.nr; i++) {
                struct ref_list_entry *e = data->header.references.list + i;
-               struct ref *ref = alloc_ref(strlen(e->name) + 1);
+               struct ref *ref = alloc_ref_from_str(e->name);
                hashcpy(ref->old_sha1, e->sha1);
-               strcpy(ref->name, e->name);
                ref->next = result;
                result = ref;
        }
index a59f47557a2b3760c27b93fa678697c35211f952..1ab28fda45be940b300b504ae029f3fba2c28fa9 100644 (file)
@@ -26,11 +26,12 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
  * directories, in case this unlink is the removal of the
  * last entry in the directory -- empty directories are removed.
  */
-static void unlink_entry(char *name, char *last_symlink)
+static void unlink_entry(struct cache_entry *ce)
 {
        char *cp, *prev;
+       char *name = ce->name;
 
-       if (has_symlink_leading_path(name, last_symlink))
+       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
                return;
        if (unlink(name))
                return;
@@ -58,7 +59,6 @@ static int check_updates(struct unpack_trees_options *o)
 {
        unsigned cnt = 0, total = 0;
        struct progress *progress = NULL;
-       char last_symlink[PATH_MAX];
        struct index_state *index = &o->result;
        int i;
        int errs = 0;
@@ -75,24 +75,27 @@ static int check_updates(struct unpack_trees_options *o)
                cnt = 0;
        }
 
-       *last_symlink = '\0';
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
 
-               if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
-                       display_progress(progress, ++cnt);
                if (ce->ce_flags & CE_REMOVE) {
+                       display_progress(progress, ++cnt);
                        if (o->update)
-                               unlink_entry(ce->name, last_symlink);
+                               unlink_entry(ce);
                        remove_index_entry_at(&o->result, i);
                        i--;
                        continue;
                }
+       }
+
+       for (i = 0; i < index->cache_nr; i++) {
+               struct cache_entry *ce = index->cache[i];
+
                if (ce->ce_flags & CE_UPDATE) {
+                       display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
                        if (o->update) {
                                errs |= checkout_entry(ce, &state, NULL);
-                               *last_symlink = '\0';
                        }
                }
        }
@@ -520,6 +523,22 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        return cnt;
 }
 
+/*
+ * This gets called when there was no index entry for the tree entry 'dst',
+ * but we found a file in the working tree that 'lstat()' said was fine,
+ * and we're on a case-insensitive filesystem.
+ *
+ * See if we can find a case-insensitive match in the index that also
+ * matches the stat information, and assume it's that other file!
+ */
+static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst, struct stat *st)
+{
+       struct cache_entry *src;
+
+       src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
+       return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID);
+}
+
 /*
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
@@ -532,12 +551,23 @@ static int verify_absent(struct cache_entry *ce, const char *action,
        if (o->index_only || o->reset || !o->update)
                return 0;
 
-       if (has_symlink_leading_path(ce->name, NULL))
+       if (has_symlink_leading_path(ce_namelen(ce), ce->name))
                return 0;
 
        if (!lstat(ce->name, &st)) {
                int cnt;
                int dtype = ce_to_dtype(ce);
+               struct cache_entry *result;
+
+               /*
+                * It may be that the 'lstat()' succeeded even though
+                * target 'ce' was absent, because there is an old
+                * entry that is different only in case..
+                *
+                * Ignore that lstat() if it matches.
+                */
+               if (ignore_case && icase_exists(o, ce, &st))
+                       return 0;
 
                if (o->dir && excluded(o->dir, ce->name, &dtype))
                        /*
@@ -581,10 +611,9 @@ static int verify_absent(struct cache_entry *ce, const char *action,
                 * delete this path, which is in a subdirectory that
                 * is being replaced with a blob.
                 */
-               cnt = index_name_pos(&o->result, ce->name, strlen(ce->name));
-               if (0 <= cnt) {
-                       struct cache_entry *ce = o->result.cache[cnt];
-                       if (ce->ce_flags & CE_REMOVE)
+               result = index_name_exists(&o->result, ce->name, ce_namelen(ce), 0);
+               if (result) {
+                       if (result->ce_flags & CE_REMOVE)
                                return 0;
                }
 
index 50453ed20f755fea2e7138d7f01300b318f28dce..d436d6ced9939beeb4599dc8fddebe0890e55db8 100644 (file)
@@ -9,16 +9,16 @@ typedef int (*merge_fn_t)(struct cache_entry **src,
                struct unpack_trees_options *options);
 
 struct unpack_trees_options {
-       int reset;
-       int merge;
-       int update;
-       int index_only;
-       int nontrivial_merge;
-       int trivial_merges_only;
-       int verbose_update;
-       int aggressive;
-       int skip_unmerged;
-       int gently;
+       unsigned int reset:1,
+                    merge:1,
+                    update:1,
+                    index_only:1,
+                    nontrivial_merge:1,
+                    trivial_merges_only:1,
+                    verbose_update:1,
+                    aggressive:1,
+                    skip_unmerged:1,
+                    gently:1;
        const char *prefix;
        int pos;
        struct dir_struct *dir;
@@ -31,7 +31,7 @@ struct unpack_trees_options {
        void *unpack_data;
 
        struct index_state *dst_index;
-       const struct index_state *src_index;
+       struct index_state *src_index;
        struct index_state result;
 };
 
index c10eca88261eed6b0e5c3fc2fdc19b8332c4ac6b..31de6c16bd021003bdd4fc1475bfe500f28773f4 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -190,9 +190,13 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
        if (!get_sha1_hex(target, sha1))
                return 0;
        if (!check_ref_format(target)) {
-               if (!walker->fetch_ref(walker, target, sha1)) {
+               struct ref *ref = alloc_ref_from_str(target);
+               if (!walker->fetch_ref(walker, ref)) {
+                       hashcpy(sha1, ref->old_sha1);
+                       free(ref);
                        return 0;
                }
+               free(ref);
        }
        return -1;
 }
index e1d40deaffa5965b1edd381a610ab225e027e3b2..8a149e11084eeec4501b5b2c5d22e5266f4852e7 100644 (file)
--- a/walker.h
+++ b/walker.h
@@ -5,7 +5,7 @@
 
 struct walker {
        void *data;
-       int (*fetch_ref)(struct walker *, char *ref, unsigned char *sha1);
+       int (*fetch_ref)(struct walker *, struct ref *ref);
        void (*prefetch)(struct walker *, unsigned char *sha1);
        int (*fetch)(struct walker *, unsigned char *sha1);
        void (*cleanup)(struct walker *);
index 532b4ea2c1960f18e41a5167a60f6de1b481606b..6e6516cd874a92376a672dcdc181aed6a3b5636c 100644 (file)
@@ -18,6 +18,7 @@ static char wt_status_colors[][COLOR_MAXLEN] = {
        "\033[32m", /* WT_STATUS_UPDATED: green */
        "\033[31m", /* WT_STATUS_CHANGED: red */
        "\033[31m", /* WT_STATUS_UNTRACKED: red */
+       "\033[31m", /* WT_STATUS_NOBRANCH: red */
 };
 
 static const char use_add_msg[] =
@@ -38,6 +39,8 @@ static int parse_status_slot(const char *var, int offset)
                return WT_STATUS_CHANGED;
        if (!strcasecmp(var+offset, "untracked"))
                return WT_STATUS_UNTRACKED;
+       if (!strcasecmp(var+offset, "nobranch"))
+               return WT_STATUS_NOBRANCH;
        die("bad config variable '%s'", var);
 }
 
@@ -206,7 +209,7 @@ static void wt_status_print_updated(struct wt_status *s)
        rev.diffopt.format_callback = wt_status_print_updated_cb;
        rev.diffopt.format_callback_data = s;
        rev.diffopt.detect_rename = 1;
-       rev.diffopt.rename_limit = 100;
+       rev.diffopt.rename_limit = 200;
        rev.diffopt.break_opt = 0;
        run_diff_index(&rev, 1);
 }
@@ -314,8 +317,9 @@ static void wt_status_print_verbose(struct wt_status *s)
 void wt_status_print(struct wt_status *s)
 {
        unsigned char sha1[20];
-       s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+       const char *branch_color = color(WT_STATUS_HEADER);
 
+       s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
        if (s->branch) {
                const char *on_what = "On branch ";
                const char *branch_name = s->branch;
@@ -323,10 +327,11 @@ void wt_status_print(struct wt_status *s)
                        branch_name += 11;
                else if (!strcmp(branch_name, "HEAD")) {
                        branch_name = "";
+                       branch_color = color(WT_STATUS_NOBRANCH);
                        on_what = "Not currently on any branch.";
                }
-               color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
-                       "# %s%s", on_what, branch_name);
+               color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
+               color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
        }
 
        if (s->is_initial) {
index 02afaa60eee74018e074a4dcb3bec97c2e0ce9dc..f0675fdff33420ba42f3a700501003a43afda376 100644 (file)
@@ -8,6 +8,7 @@ enum color_wt_status {
        WT_STATUS_UPDATED,
        WT_STATUS_CHANGED,
        WT_STATUS_UNTRACKED,
+       WT_STATUS_NOBRANCH,
 };
 
 struct wt_status {
@@ -28,8 +29,8 @@ struct wt_status {
 };
 
 int git_status_config(const char *var, const char *value);
-int wt_status_use_color;
-int wt_status_relative_paths;
+extern int wt_status_use_color;
+extern int wt_status_relative_paths;
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);