Merge branch 'jn/gitweb-customlinks'
authorJunio C Hamano <gitster@pobox.com>
Thu, 13 Nov 2008 06:27:53 +0000 (22:27 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 13 Nov 2008 06:27:53 +0000 (22:27 -0800)
* jn/gitweb-customlinks:
gitweb: Better processing format string in custom links in navbar

224 files changed:
Documentation/Makefile
Documentation/RelNotes-1.6.0.3.txt
Documentation/RelNotes-1.6.0.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.0.5.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.1.txt
Documentation/SubmittingPatches
Documentation/asciidoc.conf
Documentation/blame-options.txt
Documentation/config.txt
Documentation/diff-format.txt
Documentation/diff-options.txt
Documentation/git-add.txt
Documentation/git-bisect.txt
Documentation/git-check-attr.txt
Documentation/git-checkout.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-daemon.txt
Documentation/git-describe.txt
Documentation/git-diff.txt
Documentation/git-filter-branch.txt
Documentation/git-format-patch.txt
Documentation/git-log.txt
Documentation/git-pack-objects.txt
Documentation/git-rebase.txt
Documentation/git-remote.txt
Documentation/git-reset.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gittutorial-2.txt
Documentation/gitworkflows.txt [new file with mode: 0644]
Documentation/i18n.txt
Documentation/rev-list-options.txt
Documentation/urls-remotes.txt
Documentation/user-manual.txt
INSTALL
Makefile
archive.c
branch.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-check-attr.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clone.c
builtin-commit.c
builtin-config.c
builtin-describe.c
builtin-diff.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-for-each-ref.c
builtin-gc.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-merge.c
builtin-pack-objects.c
builtin-read-tree.c
builtin-receive-pack.c
builtin-reflog.c
builtin-remote.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-tag.c
builtin-unpack-objects.c
builtin-update-ref.c
bundle.c
cache.h
combine-diff.c
commit.h
compat/cygwin.c
compat/mingw.c
config.c
config.mak.in
configure.ac
connect.c
contrib/completion/git-completion.bash
contrib/examples/git-remote.perl
contrib/fast-import/git-p4
contrib/hooks/post-receive-email
contrib/hooks/pre-auto-gc-battery
contrib/stats/packinfo.pl
daemon.c
diff.c
diff.h
diffcore-rename.c
diffcore.h
environment.c
fsck.c
git-add--interactive.perl
git-bisect.sh
git-gui/Makefile
git-gui/git-gui--askpass [new file with mode: 0755]
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/diff.tcl
git-gui/lib/index.tcl
git-gui/lib/merge.tcl
git-gui/lib/mergetool.tcl
git-gui/lib/remote.tcl
git-gui/lib/remote_add.tcl [new file with mode: 0644]
git-gui/lib/remote_branch_delete.tcl
git-gui/lib/search.tcl [new file with mode: 0644]
git-gui/lib/spellcheck.tcl
git-gui/lib/sshkey.tcl [new file with mode: 0644]
git-gui/lib/transport.tcl
git-gui/po/de.po
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-repack.sh
git-send-email.perl
git-submodule.sh
git-svn.perl
gitk-git/gitk
gitk-git/po/de.po
gitk-git/po/es.po
gitk-git/po/it.po
gitk-git/po/sv.po
gitweb/INSTALL
gitweb/gitweb.perl
grep.c
hash-object.c
http-push.c
index-pack.c
lockfile.c
log-tree.c
log-tree.h
merge-file.c
merge-recursive.c
pack-refs.c
pack-revindex.c
path.c
perl/Git.pm
pretty.c
read-cache.c
refs.c
remote.c
remote.h
rerere.c
revision.c
revision.h
server-info.c
sha1_file.c
sha1_name.c
t/t0003-attributes.sh
t/t1005-read-tree-reset.sh
t/t1301-shared-repo.sh
t/t1400-update-ref.sh
t/t2005-checkout-index-symlinks.sh
t/t2011-checkout-invalid-head.sh [new file with mode: 0755]
t/t2102-update-index-symlinks.sh
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3409-rebase-hook.sh
t/t3409-rebase-preserve-merges.sh [new file with mode: 0755]
t/t3410-rebase-preserve-dropped-merges.sh [new file with mode: 0755]
t/t3411-rebase-preserve-around-merges.sh [new file with mode: 0644]
t/t3600-rm.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4013/diff.format-patch_--attach_--stdout_initial..master
t/t4013/diff.format-patch_--attach_--stdout_initial..master^
t/t4013/diff.format-patch_--inline_--stdout_--subject-prefix=TESTCASE_initial..master
t/t4013/diff.format-patch_--inline_--stdout_initial..master
t/t4013/diff.format-patch_--inline_--stdout_initial..master^
t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master [new file with mode: 0644]
t/t4013/diff.format-patch_--stdout_--numbered_initial..master [new file with mode: 0644]
t/t4013/diff.format-patch_--stdout_initial..master
t/t4013/diff.format-patch_--stdout_initial..master^
t/t4018-diff-funcname.sh
t/t4021-format-patch-numbered.sh
t/t4030-diff-textconv.sh [new file with mode: 0755]
t/t4128-apply-root.sh
t/t4151-am-abort.sh
t/t5000-tar-tree.sh
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5303-pack-corruption-resilience.sh
t/t5400-send-pack.sh
t/t5405-send-pack-rewind.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5520-pull.sh
t/t5702-clone-options.sh
t/t6025-merge-symlinks.sh
t/t6120-describe.sh
t/t7004-tag.sh
t/t7201-co.sh
t/t7502-commit.sh
t/t7502-status.sh
t/t7507-commit-verbose.sh [new file with mode: 0755]
t/t7600-merge.sh
t/t7700-repack.sh [new file with mode: 0755]
t/t8005-blame-i18n.sh [new file with mode: 0755]
t/t8005/cp1251.txt [new file with mode: 0644]
t/t8005/sjis.txt [new file with mode: 0644]
t/t8005/utf8.txt [new file with mode: 0644]
t/t9300-fast-import.sh
t/t9400-git-cvsserver-server.sh
test-chmtime.c
transport.c
transport.h
unpack-trees.c
userdiff.c [new file with mode: 0644]
userdiff.h [new file with mode: 0644]
walker.c
wt-status.c
xdiff-interface.c
xdiff-interface.h
xdiff/xdiff.h
xdiff/xdiffi.c
xdiff/xemit.c
xdiff/xemit.h
xdiff/xprepare.c
index ded0e40b978e6f4b85530541c45782f2b383ea44..c34c1cae2072fa27de32f553866426bbc01436d5 100644 (file)
@@ -6,7 +6,7 @@ MAN5_TXT=gitattributes.txt gitignore.txt gitmodules.txt githooks.txt \
        gitrepository-layout.txt
 MAN7_TXT=gitcli.txt gittutorial.txt gittutorial-2.txt \
        gitcvs-migration.txt gitcore-tutorial.txt gitglossary.txt \
-       gitdiffcore.txt
+       gitdiffcore.txt gitworkflows.txt
 
 MAN_TXT = $(MAN1_TXT) $(MAN5_TXT) $(MAN7_TXT)
 MAN_XML=$(patsubst %.txt,%.xml,$(MAN_TXT))
@@ -87,7 +87,9 @@ man7: $(DOC_MAN7)
 
 info: git.info gitman.info
 
-install: man
+install: install-man
+
+install-man: man
        $(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
        $(INSTALL) -d -m 755 $(DESTDIR)$(man5dir)
        $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
@@ -220,7 +222,9 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
 install-webdoc : html
        sh ./install-webdoc.sh $(WEBDOC_DEST)
 
-quick-install:
+quick-install: quick-install-man
+
+quick-install-man:
        sh ./install-doc-quick.sh $(DOC_REF) $(DESTDIR)$(mandir)
 
 quick-install-html:
index 6cf8ae4ea1423d3dce3522890a666a424d163fb7..ae0577836ae8b3ea73f8565acc42964d4144dd26 100644 (file)
@@ -16,12 +16,17 @@ Fixes since v1.6.0.2
 * "git rebase -i" now honors the pre-rebase hook, just like the
   other rebase implementations "git rebase" and "git rebase -m".
 
+* "git rebase -i" incorrectly aborted when there is no commit to replay.
+
 * Behaviour of "git diff --quiet" was inconsistent with "diff --exit-code"
   with the output redirected to /dev/null.
 
 * "git diff --no-index" on binary files no longer outputs a bogus
   "diff --git" header line.
 
+* "git diff" hunk header patterns with multiple elements separated by LF
+  were not used correctly.
+
 * Hunk headers in "git diff" default to using extended regular
   expressions, fixing some of the internal patterns on non-GNU
   platforms.
@@ -29,6 +34,15 @@ Fixes since v1.6.0.2
 * New config "diff.*.xfuncname" exposes extended regular expressions
   for user specified hunk header patterns.
 
+* "git gc" when ejecting otherwise unreachable objects from packfiles into
+  loose form leaked memory.
+
+* "git index-pack" was recently broken and mishandled objects added by
+  thin-pack completion processing under memory pressure.
+
+* "git index-pack" was recently broken and misbehaved when run from inside
+  .git/objects/pack/ directory.
+
 * "git stash apply sash@{1}" was fixed to error out.  Prior versions
   would have applied stash@{0} incorrectly.
 
@@ -42,6 +56,8 @@ Fixes since v1.6.0.2
 
 * "git remote show -v" now displays all URLs of a remote.
 
+* "git checkout -b branch" was confused when branch already existed.
+
 * "git checkout -q" once again suppresses the locally modified file list.
 
 * "git clone -q", "git fetch -q" asks remote side to not send
@@ -56,9 +72,15 @@ Fixes since v1.6.0.2
 * "git merge" once again removes directories after the last file has
   been removed from it during the merge.
 
+* "git merge" did not allocate enough memory for the structure itself when
+  enumerating the parents of the resulting commit.
+
 * "git blame -C -C" no longer segfaults while trying to pass blame if
    it encounters a submodule reference.
 
+* "git rm" incorrectly claimed that you have local modifications when a
+  path was merely stat-dirty.
+
 * "git svn" fixed to display an error message when 'set-tree' failed,
    instead of a Perl compile error.
 
@@ -68,10 +90,16 @@ Fixes since v1.6.0.2
 * The "git commit" error message when there are still unmerged
   files present was clarified to match "git write-tree".
 
+* "git init" was confused when core.bare or core.sharedRepository are set
+  in system or user global configuration file by mistake.  When --bare or
+  --shared is given from the command line, these now override such
+  settings made outside the repositories.
+
 * Some segfaults due to uncaught NULL pointers were fixed in multiple
   tools such as apply, reset, update-index.
 
-* Solaris builds now default to OLD_ICONV=1 to avoid compile warnings.
+* Solaris builds now default to OLD_ICONV=1 to avoid compile warnings;
+  Solaris 8 does not define NEEDS_LIBICONV by default.
 
 * "Git.pm" tests relied on unnecessarily more recent version of Perl.
 
@@ -87,9 +115,3 @@ Fixes since v1.6.0.2
   ("git fetch") is still however supported.
 
 Many other documentation updates.
-
---
-exec >/var/tmp/1
-O=v1.6.0.2-76-gd70b4a8
-echo O=$(git describe maint)
-git shortlog --no-merges $O..maint
diff --git a/Documentation/RelNotes-1.6.0.4.txt b/Documentation/RelNotes-1.6.0.4.txt
new file mode 100644 (file)
index 0000000..fba3f30
--- /dev/null
@@ -0,0 +1,39 @@
+GIT v1.6.0.4 Release Notes
+==========================
+
+Fixes since v1.6.0.3
+--------------------
+
+* 'git add -p' said "No changes" when only binary files were changed.
+
+* 'git archive' did not work correctly in bare repositories.
+
+* 'git checkout -t -b newbranch' when you are on detached HEAD was broken.
+
+* when we refuse to detect renames because there are too many new or
+  deleted files, 'git diff' did not say how many there are.
+
+* 'git push --mirror' tried and failed to push the stash; there is no
+  point in sending it to begin with.
+
+* 'git push' did not update the remote tracking reference if the corresponding
+  ref on the remote end happened to be already up to date.
+
+* 'git pull $there $branch:$current_branch' did not work when you were on
+  a branch yet to be born.
+
+* when giving up resolving a conflicted merge, 'git reset --hard' failed
+  to remove new paths from the working tree.
+
+* 'git send-email' had a small fd leak while scanning directory.
+
+* 'git status' incorrectly reported a submodule directory as an untracked
+  directory.
+
+* 'git svn' used deprecated 'git-foo' form of subcommand invocaition.
+
+* 'git update-ref -d' to remove a reference did not honor --no-deref option.
+
+* Plugged small memleaks here and there.
+
+* Also contains many documentation updates.
diff --git a/Documentation/RelNotes-1.6.0.5.txt b/Documentation/RelNotes-1.6.0.5.txt
new file mode 100644 (file)
index 0000000..62f95e6
--- /dev/null
@@ -0,0 +1,21 @@
+GIT v1.6.0.5 Release Notes
+==========================
+
+Fixes since v1.6.0.4
+--------------------
+
+* 'git checkout' used to crash when your HEAD was pointing at a deleted
+  branch.
+
+* 'git checkout' from an un-checked-out state did not allow switching out
+  of the current branch.
+
+* 'git pack-objects' did not make its best effort to honor --max-pack-size
+  option when a single first object already busted the given limit and
+  placed many objects in a single pack.
+
+* 'make check' cannot be run without sparse; people may have meant to say
+  'make test' instead, so suggest that.
+
+* Many unsafe call to sprintf() style varargs functions are corrected.
+
index 906932cfa1a51c6cd0c7098eabd067b6698b7fa4..d0e30e95d46f561f1545fd8468ee9281a5eb482e 100644 (file)
@@ -16,13 +16,16 @@ on.
 * gitk can call out to git-gui to view "git blame" output; git-gui in turn
   can run gitk from its blame view.
 
-(portability)
+* Various git-gui updates including updated translations.
+
+* Various gitweb updates from repo.or.cz installation.
 
-* ...
+(portability)
 
-(documentation)
+* A few test scripts used nonportable "grep" that did not work well on
+  some platforms, e.g. Solaris.
 
-* ...
+* Sample pre-auto-gc script has OS X support.
 
 (performance)
 
@@ -35,6 +38,14 @@ on.
 * Bash completion of refnames in a repository with massive number of
   refs has been optimized.
 
+* Cygwin port uses native stat/lstat implementations when applicable,
+  which leads to improved performance.
+
+* "git push" pays attention to alternate repositories to avoid sending
+  unnecessary objects.
+
+* "git svn" can rebuild an out-of-date rev_map file.
+
 (usability, bells and whistles)
 
 * When you mistype a command name, git helpfully suggests what it guesses
@@ -42,35 +53,77 @@ on.
   to a non-zero value to accept the suggestion when git can uniquely
   guess.
 
+* "git add -N path..." adds the named paths as an empty blob, so that
+  subsequent "git diff" will show a diff as if they are creation events.
+
+* "git apply" learned --include=paths option, similar to the existing
+  --exclude=paths option.
+
 * "git bisect" is careful about a user mistake and suggests testing of
   merge base first when good is not a strict ancestor of bad.
 
+* "git blame" re-encodes the commit metainfo to UTF-8 from i18n.commitEncoding
+  by default.
+
+* "git check-attr --stdin" can check attributes for multiple paths.
+
 * "git checkout --track origin/hack" used to be a syntax error.  It now
   DWIMs to create a corresponding local branch "hack", i.e. acts as if you
   said "git checkout --track -b hack origin/hack".
 
+* "git checkout --ours/--theirs" can be used to check out one side of a
+  conflicting merge during conflict resolution.
+
+* "git checkout -m" can be used to recreate the initial conflicted state
+  during conflict resolution.
+
 * "git cherry-pick" can also utilize rerere for conflict resolution.
 
+* "git clone" learned to be verbose with -v
+
 * "git commit --author=$name" can look up author name from existing
   commits.
 
+* output from "git commit" has been reworded in a more concise and yet
+  more informative way.
+
 * "git count-objects" reports the on-disk footprint for packfiles and
   their corresponding idx files.
 
 * "git daemon" learned --max-connections=<count> option.
 
+* "git daemon" exports REMOTE_ADDR to record client address, so that
+  spawned programs can act differently on it.
+
+* "git describe --tags" favours closer lightweight tags than farther
+  annotated tags now.
+
 * "git diff" learned to mimic --suppress-blank-empty from GNU diff via a
   configuration option.
 
-* "git diff" learned to put more sensible hunk headers for Python and
-  HTML contents.
+* "git diff" learned to put more sensible hunk headers for Python,
+  HTML and ObjC contents.
 
 * "git diff" learned to vary the a/ vs b/ prefix depending on what are
   being compared, controlled by diff.mnemonicprefix configuration.
 
+* "git diff" learned --dirstat-by-file to count changed files, not number
+  of lines, when summarizing the global picture.
+
+* "git diff" hunk header pattern for ObjC has been added.
+
+* a "textconv" filter that makes binary files textual form for human
+   consumption can be specified as an attribute for paths; "git diff"
+   learnt to make use of it.
+
 * "git for-each-ref" learned "refname:short" token that gives an
   unambiguously abbreviated refname.
 
+* Auto-numbering of the subject lines is the default for "git
+  format-patch" now.
+
+* "git grep" learned to accept -z similar to GNU grep.
+
 * "git help" learned to use GIT_MAN_VIEWER environment variable before
   using "man" program.
 
@@ -94,21 +147,50 @@ on.
 * "git merge -s $strategy" can use a custom built strategy if you have a
   command "git-merge-$strategy" on your $PATH.
 
+* "git push" can be told to reject deletion of refs with receive.denyDeletes
+  configuration.
+
+* "git rebase" honours pre-rebase hook; use --no-verify to bypass it.
+
+* "git rebase -p" uses interactive rebase machinery now to preserve the merges.
+
 * "git reflog expire branch" can be used in place of "git reflog expire
   refs/heads/branch".
 
+* "git remote show $remote" lists remote branches one-per-line now.
+
+* when giving up resolving a conflicted merge, "git reset --hard" failed
+  to remove new paths from the working tree. [cherry-pick to 'maint'?]
+
 * "git submodule foreach" subcommand allows you to iterate over checked
   out submodules.
 
 * "git submodule sync" subcommands allows you to update the origin URL
   recorded in submodule directories from the toplevel .gitmodules file.
 
+* "git svn branch" can create new branches on the other end.
+
+* "gitweb" can use more saner PATH_INFO based URL.
+
 (internal)
 
 * "git hash-object" learned to lie about the path being hashed, so that
   correct gitattributes processing can be done while hashing contents
   stored in a temporary file.
 
+* various callers of git-merge-recursive avoid forking it as an external
+  process.
+
+* Git class defined in "Git.pm" can be subclasses a bit more easily.
+
+* We used to link GNU regex library as a compatibility layer for some
+  platforms, but it turns out it is not necessary on most of them.
+
+* Some path handling routines used fixed number of buffers used alternately
+  but depending on the call depth, this arrangement led to hard to track
+  bugs.  This issue is being addressed.
+
+
 Fixes since v1.6.0
 ------------------
 
@@ -124,6 +206,11 @@ release, unless otherwise noted.
   but we dropped support for such a use case long time ago.  This has
   been resurrected.
 
+* Giving 3 or more tree-ish to "git diff" is supposed to show the combined
+  diff from second and subsequent trees to the first one.  b75271d ("git
+  diff <tree>{3,}": do not reverse order of arguments, 2008-10-10) needs
+  to be cherry-picked to 'maint'.
+
 * "git filter-branch" failed to rewrite a tag name with slashes in it.
 
 * "git push --tags --all $there" failed with generic usage message without
@@ -135,6 +222,6 @@ release, unless otherwise noted.
 
 --
 exec >/var/tmp/1
-O=v1.6.0.2-295-g34a5d35
+O=v1.6.0.3-639-ga1a846a
 echo O=$(git describe master)
 git shortlog --no-merges $O..master ^maint
index a1e9100f9e3ccb8466ec603e154cb230dc2cb33b..f0295c60f5aceb975575903327228776d6b2bb9e 100644 (file)
@@ -456,3 +456,30 @@ This should help you to submit patches inline using KMail.
 
 5) Back in the compose window: add whatever other text you wish to the
 message, complete the addressing and subject fields, and press send.
+
+
+Gmail
+-----
+
+Submitting properly formatted patches via Gmail is simple now that
+IMAP support is available. First, edit your ~/.gitconfig to specify your
+account settings:
+
+[imap]
+       folder = "[Gmail]/Drafts"
+       host = imaps://imap.gmail.com
+       user = user@gmail.com
+       pass = p4ssw0rd
+       port = 993
+       sslverify = false
+
+Next, ensure that your Gmail settings are correct. In "Settings" the
+"Use Unicode (UTF-8) encoding for outgoing messages" should be checked.
+
+Once your commits are ready to send to the mailing list, run the following
+command to send the patch emails to your Gmail Drafts folder.
+
+       $ git format-patch -M --stdout origin/master | git imap-send
+
+Go to your Gmail account, open the Drafts folder, find the patch email, fill
+in the To: and CC: fields and send away!
index 40d43b78ee9d6c3827bcf631c1f41f54d0e3dfbc..2da867d2f8dd1e5272d33571062bda5f169cd278 100644 (file)
@@ -40,6 +40,26 @@ endif::doctype-manpage[]
 </literallayout>
 {title#}</example>
 endif::docbook-xsl-172[]
+
+ifdef::docbook-xsl-172[]
+ifdef::doctype-manpage[]
+# The following two small workarounds insert a simple paragraph after screen
+[listingblock]
+<example><title>{title}</title>
+<screen>
+|
+</screen><simpara></simpara>
+{title#}</example>
+
+[verseblock]
+<formalpara{id? id="{id}"}><title>{title}</title><para>
+{title%}<literallayout{id? id="{id}"}>
+{title#}<literallayout>
+|
+</literallayout><simpara></simpara>
+{title#}</para></formalpara>
+endif::doctype-manpage[]
+endif::docbook-xsl-172[]
 endif::backend-docbook[]
 
 ifdef::doctype-manpage[]
index 5428111d732cb38dbb257ddfa860ebd04088b4e9..1ab1b96cf9e4c72a7d4e2a8a1d5dd8f016ac9d79 100644 (file)
@@ -49,6 +49,13 @@ of lines before or after the line given by <start>.
        Show the result incrementally in a format designed for
        machine consumption.
 
+--encoding=<encoding>::
+       Specifies the encoding used to output author names
+       and commit summaries. Setting it to `none` makes blame
+       output unconverted data. For more information see the
+       discussion about encoding in the linkgit:git-log[1]
+       manual page.
+
 --contents <file>::
        When <rev> is not specified, the command annotates the
        changes starting backwards from the working tree copy.
index da18a5458e82e3d3d72e7ef1832cdb6461324ea4..32dcd643d2e9ea82a7b65e295e9aa55f7dd999c2 100644 (file)
@@ -124,7 +124,9 @@ core.ignoreCygwinFSTricks::
        one hierarchy using Cygwin mount. If true, Git uses native Win32 API
        whenever it is possible and falls back to Cygwin functions only to
        handle symbol links. The native mode is more than twice faster than
-       normal Cygwin l/stat() functions. True by default.
+       normal Cygwin l/stat() functions. True by default, unless core.filemode
+       is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's
+       POSIX emulation is required to support core.filemode.
 
 core.trustctime::
        If false, the ctime differences between the index and the
@@ -640,10 +642,11 @@ fetch.unpackLimit::
        `transfer.unpackLimit` is used instead.
 
 format.numbered::
-       A boolean which can enable sequence numbers in patch subjects.
-       Setting this option to "auto" will enable it only if there is
-       more than one patch.  See --numbered option in
-       linkgit:git-format-patch[1].
+       A boolean which can enable or disable sequence numbers in patch
+       subjects.  It defaults to "auto" which enables it only if there
+       is more than one patch.  It can be enabled or disabled for all
+       messages by setting it to "true" or "false".  See --numbered
+       option in linkgit:git-format-patch[1].
 
 format.headers::
        Additional email headers to include in a patch to be submitted
@@ -1185,12 +1188,25 @@ receive.unpackLimit::
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
 
+receive.denyDeletes::
+       If set to true, git-receive-pack will deny a ref update that deletes
+       the ref. Use this to prevent such a ref deletion via a push.
+
 receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
        not a fast forward. Use this to prevent such an update via a push,
        even if that push is forced. This configuration variable is
        set when initializing a shared repository.
 
+receive.denyCurrentBranch::
+       If set to true or "refuse", receive-pack will deny a ref update
+       to the currently checked out branch of a non-bare repository.
+       Such a push is potentially dangerous because it brings the HEAD
+       out of sync with the index and working tree. If set to "warn",
+       print a warning of such a push to stderr, but allow the push to
+       proceed. If set to false or "ignore", allow such pushes with no
+       message. Defaults to "warn".
+
 transfer.unpackLimit::
        When `fetch.unpackLimit` or `receive.unpackLimit` are
        not set, the value of this variable is used instead.
index 400cbb3b1c120b93278472678ee7bdb87a74f95b..aafd3a394126e4718b593eb5727412e16d2334e4 100644 (file)
@@ -46,6 +46,22 @@ That is, from the left to the right:
 . path for "dst"; only exists for C or R.
 . an LF or a NUL when '-z' option is used, to terminate the record.
 
+Possible status letters are:
+
+- A: addition of a file
+- C: copy of a file into a new one
+- D: deletion of a file
+- M: modification of the contents or mode of a file
+- R: renaming of a file
+- T: change in the type of the file
+- U: file is unmerged (you must complete the merge before it can
+be committed)
+- X: "unknown" change type (most probably a bug, please report it)
+
+Status letters C and M are always followed by a score (denoting the
+percentage of similarity between the source and target of the move or
+copy), and are the only ones to be so.
+
 <sha1> is shown as all 0's if a file is new on the filesystem
 and it is out of sync with the index.
 
index 7788d4fa4a1209cbed564a20f882f0946ba400cf..c62b45cdbade990878b8e8e9371d41d4e3c43bfe 100644 (file)
@@ -137,7 +137,8 @@ endif::git-format-patch[]
 --diff-filter=[ACDMRTUXB*]::
        Select only files that are Added (`A`), Copied (`C`),
        Deleted (`D`), Modified (`M`), Renamed (`R`), have their
-       type (mode) changed (`T`), are Unmerged (`U`), are
+       type (i.e. regular file, symlink, submodule, ...) changed (`T`),
+       are Unmerged (`U`), are
        Unknown (`X`), or have had their pairing Broken (`B`).
        Any combination of the filter characters may be used.
        When `*` (All-or-none) is added to the combination, all
index 2b6d6c86547b2cec370d34b14ddef25264404892..6fc20b0baf1592c88deecc37636b1c644483616b 100644 (file)
@@ -9,8 +9,8 @@ SYNOPSIS
 --------
 [verse]
 'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
-         [--all | [--update | -u]] [--refresh] [--ignore-errors] [--]
-         <filepattern>...
+         [--all | [--update | -u]] [--intent-to-add | -N]
+         [--refresh] [--ignore-errors] [--] <filepattern>...
 
 DESCRIPTION
 -----------
@@ -92,6 +92,15 @@ OPTIONS
        and add all untracked files that are not ignored by '.gitignore'
        mechanism.
 
+
+-N::
+--intent-to-add::
+       Record only the fact that the path will be added later. An entry
+       for the path is placed in the index with no content. This is
+       useful for, among other things, showing the unstaged content of
+       such files with 'git diff' and commiting them with 'git commit
+       -a'.
+
 --refresh::
        Don't add the file(s), but only refresh their stat()
        information in the index.
index c7981efcd9b86287bbea9ddcaf187a9bd48c77eb..39034ec7d6dff42f4be282c4216a7e13ff255d91 100644 (file)
@@ -26,7 +26,7 @@ on the subcommand:
  git bisect log
  git bisect run <cmd>...
 
-This command uses 'git-rev-list --bisect' to help drive the
+This command uses 'git rev-list --bisect' to help drive the
 binary search process to find which change introduced a bug, given an
 old "good" commit object name and a later "bad" commit object name.
 
@@ -101,7 +101,7 @@ $ git bisect visualize
 to see the currently remaining suspects in 'gitk'.  `visualize` is a bit
 too long to type and `view` is provided as a synonym.
 
-If 'DISPLAY' environment variable is not set, 'git-log' is used
+If 'DISPLAY' environment variable is not set, 'git log' is used
 instead.  You can even give command line options such as `-p` and
 `--stat`.
 
@@ -215,7 +215,7 @@ tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
 work around other problem this bisection is not interested in")
 applied to the revision being tested.
 
-To cope with such a situation, after the inner 'git-bisect' finds the
+To cope with such a situation, after the inner 'git bisect' finds the
 next revision to test, with the "run" script, you can apply that tweak
 before compiling, run the real test, and after the test decides if the
 revision (possibly with the needed tweaks) passed the test, rewind the
index 2b821f2a1d70fa108ce279135fd5028453a04fd9..8c2ac12f5d316a5db8c5a9be2c4d9639ca30653b 100644 (file)
@@ -3,12 +3,14 @@ git-check-attr(1)
 
 NAME
 ----
-git-check-attr - Display gitattributes information.
+git-check-attr - Display gitattributes information
 
 
 SYNOPSIS
 --------
+[verse]
 'git check-attr' attr... [--] pathname...
+'git check-attr' --stdin [-z] attr... < <list-of-paths>
 
 DESCRIPTION
 -----------
@@ -17,11 +19,68 @@ For every pathname, this command will list if each attr is 'unspecified',
 
 OPTIONS
 -------
+--stdin::
+       Read file names from stdin instead of from the command-line.
+
+-z::
+       Only meaningful with `--stdin`; paths are separated with
+       NUL character instead of LF.
+
 \--::
        Interpret all preceding arguments as attributes, and all following
        arguments as path names. If not supplied, only the first argument will
        be treated as an attribute.
 
+OUTPUT
+------
+
+The output is of the form:
+<path> COLON SP <attribute> COLON SP <info> LF
+
+Where <path> is the path of a file being queried, <attribute> is an attribute
+being queried and <info> can be either:
+
+'unspecified';; when the attribute is not defined for the path.
+'unset';;      when the attribute is defined to false.
+'set';;                when the attribute is defined to true.
+<value>;;      when a value has been assigned to the attribute.
+
+EXAMPLES
+--------
+
+In the examples, the following '.gitattributes' file is used:
+---------------
+*.java diff=java -crlf myAttr
+NoMyAttr.java !myAttr
+README caveat=unspecified
+---------------
+
+* Listing a single attribute:
+---------------
+$ git check-attr diff org/example/MyClass.java
+org/example/MyClass.java: diff: java
+---------------
+
+* Listing multiple attributes for a file:
+---------------
+$ git check-attr crlf diff myAttr -- org/example/MyClass.java
+org/example/MyClass.java: crlf: unset
+org/example/MyClass.java: diff: java
+org/example/MyClass.java: myAttr: set
+---------------
+
+* Listing attribute for multiple files:
+---------------
+$ git check-attr myAttr -- org/example/MyClass.java org/example/NoMyAttr.java
+org/example/MyClass.java: myAttr: set
+org/example/NoMyAttr.java: myAttr: unspecified
+---------------
+
+* Not all values are equally unambiguous:
+---------------
+$ git check-attr caveat README
+README: caveat: unspecified
+---------------
 
 SEE ALSO
 --------
index 82e154de4970c1c2cb35335591cfe457e80a89c1..168333a588e0828d2d14687774fd41cc57f43721 100644 (file)
@@ -88,7 +88,7 @@ This would tell us to use "hack" as the local branch when branching
 off of "origin/hack" (or "remotes/origin/hack", or even
 "refs/remotes/origin/hack").  If the given name has no slash, or the above
 guessing results in an empty name, the guessing is aborted.  You can
-exlicitly give a name with '-b' in such a case.
+explicitly give a name with '-b' in such a case.
 
 --no-track::
        Ignore the branch.autosetupmerge configuration variable.
index 0e14e732fd470b7f48882c9959235785df19b7b0..95f08b911464b348525e4c65cca32946c583e762 100644 (file)
@@ -90,6 +90,11 @@ then the cloned repository will become corrupt.
        Operate quietly.  This flag is also passed to the `rsync'
        command when given.
 
+--verbose::
+-v::
+       Display the progressbar, even in case the standard output is not
+       a terminal.
+
 --no-checkout::
 -n::
        No checkout of HEAD is performed after the clone is complete.
index eb05b0f49b0c513581150415d1a4e679d7bfbea6..2e62165fa9f48e1bff506b75895c081eb922b4ec 100644 (file)
@@ -94,7 +94,8 @@ OPTIONS
 
 -s::
 --signoff::
-       Add Signed-off-by line at the end of the commit message.
+       Add Signed-off-by line by the commiter at the end of the commit
+       log message.
 
 -n::
 --no-verify::
@@ -144,6 +145,10 @@ It is a rough equivalent for:
 ------
 but can be used to amend a merge commit.
 --
++
+You should understand the implications of rewriting history if you
+amend a commit that has already been published.  (See the "RECOVERING
+FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
 
 -i::
 --include::
index b08a08cd95b6da192a008c58d7973769dfe3fc8c..f1a570a874ff0ccc2c2ffb6271ac76529506c4cc 100644 (file)
@@ -270,6 +270,15 @@ selectively enable/disable services per repository::
 ----------------------------------------------------------------
 
 
+ENVIRONMENT
+-----------
+'git-daemon' will set REMOTE_ADDR to the IP address of the client
+that connected to it, if the IP address is available. REMOTE_ADDR will
+be available in the environment of hooks called when
+services are performed.
+
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>, YOSHIFUJI Hideaki
index c4dbc2ae342833561080f7a18a93bcf5ac5a0dd3..3d79f05995d28214f68640dd3f693033dff03547 100644 (file)
@@ -18,6 +18,9 @@ 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.
 
+By default (without --all or --tags) `git describe` only shows
+annotated tags.  For more information about creating annotated tags
+see the -a and -s options to linkgit:git-tag[1].
 
 OPTIONS
 -------
@@ -26,11 +29,13 @@ OPTIONS
 
 --all::
        Instead of using only the annotated tags, use any ref
-       found in `.git/refs/`.
+       found in `.git/refs/`.  This option enables matching
+       any known branch, remote branch, or lightweight tag.
 
 --tags::
        Instead of using only the annotated tags, use any tag
-       found in `.git/refs/tags`.
+       found in `.git/refs/tags`.  This option enables matching
+       a lightweight (non-annotated) tag.
 
 --contains::
        Instead of finding the tag that predates the commit, find
index c53eba557d0a242a0d8553d9569ce8b2eb86331b..a2f192fb7519117f8e47f3ec4608e3301c200dc3 100644 (file)
@@ -33,6 +33,7 @@ forced by --no-index.
        commit relative to the named <commit>.  Typically you
        would want comparison with the latest commit, so if you
        do not give <commit>, it defaults to HEAD.
+       --staged is a synonym of --cached.
 
 'git diff' [--options] <commit> [--] [<path>...]::
 
index b0e710d5f9c05eb86ce3ccc4e1aa4a7868f3abff..fed6de6a7fa0e720994f7094be2f464818af289a 100644 (file)
@@ -36,7 +36,9 @@ the objects and will not converge with the original branch.  You will not
 be able to easily push and distribute the rewritten branch on top of the
 original branch.  Please do not use this command if you do not know the
 full implications, and avoid using it anyway, if a simple single commit
-would suffice to fix your problem.
+would suffice to fix your problem.  (See the "RECOVERING FROM UPSTREAM
+REBASE" section in linkgit:git-rebase[1] for further information about
+rewriting published history.)
 
 Always verify that the rewritten version is correct: The original refs,
 if different from the rewritten ones, will be stored in the namespace
index adb4ea7b1b6c70fb6fc2486487ef34ed280ec015..11a7d772618f48b961d41fb5fe1cb269d7ae7cd1 100644 (file)
@@ -46,7 +46,8 @@ applies to that command line and you do not get "everything
 since the beginning of the time".  If you want to format
 everything since project inception to one commit, say "git
 format-patch \--root <commit>" to make it clear that it is the
-latter case.
+latter case.  If you want to format a single commit, you can do
+this with "git format-patch -1 <commit>".
 
 By default, each output file is numbered sequentially from 1, and uses the
 first line of the commit message (massaged for pathname safety) as
@@ -58,8 +59,10 @@ output, unless the --stdout option is specified.
 If -o is specified, output files are created in <dir>.  Otherwise
 they are created in the current working directory.
 
-If -n is specified, instead of "[PATCH] Subject", the first line
-is formatted as "[PATCH n/m] Subject".
+By default, the subject of a single patch is "[PATCH] First Line" and
+the subject when multiple patches are output is "[PATCH n/m] First
+Line". To force 1/1 to be added for a single patch, use -n.  To omit
+patch numbers from the subject, use -N
 
 If given --thread, 'git-format-patch' will generate In-Reply-To and
 References headers to make the second and subsequent patch mails appear
@@ -81,7 +84,7 @@ include::diff-options.txt[]
 
 -n::
 --numbered::
-       Name output in '[PATCH n/m]' format.
+       Name output in '[PATCH n/m]' format, even with a single patch.
 
 -N::
 --no-numbered::
index 93a2a227c4816720b82330aca1b8c3ab0245b1c1..34cf4e5811d1a6f46fcbd333a2ff48c200eadff8 100644 (file)
@@ -40,6 +40,10 @@ include::diff-options.txt[]
 --decorate::
        Print out the ref names of any commits that are shown.
 
+--source::
+       Print out the ref name given on the command line by which each
+       commit was reached.
+
 --full-diff::
        Without this flag, "git log -p <path>..." shows commits that
        touch the specified paths, and diffs about the same specified
index 8c354bd47014825de71243d73158b6b080ecb350..7d4c1a75562de80aa80dfbfa665330b14ec948c6 100644 (file)
@@ -109,6 +109,11 @@ base-name::
        The default is unlimited, unless the config variable
        `pack.packSizeLimit` is set.
 
+--honor-pack-keep::
+       This flag causes an object already in a local pack that
+       has a .keep file to be ignored, even if it appears in the
+       standard input.
+
 --incremental::
        This flag causes an object already in a pack ignored
        even if it appears in the standard input.
@@ -116,7 +121,7 @@ base-name::
 --local::
        This flag is similar to `--incremental`; instead of
        ignoring all packed objects, it only ignores objects
-       that are packed and not in the local object store
+       that are packed and/or not in the local object store
        (i.e. borrowed from an alternate).
 
 --non-empty::
index 32f0f122e937aab6b248d0ed86fa09854dbe253d..c8ad86a56fc6bff70cb6e7c74cc8ef10ef2da73e 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
-       [-s <strategy> | --strategy=<strategy>]
+       [-s <strategy> | --strategy=<strategy>] [--no-verify]
        [-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
        [--onto <newbase>] <upstream> [<branch>]
 'git rebase' --continue | --skip | --abort
@@ -232,6 +232,9 @@ OPTIONS
 --verbose::
        Display a diffstat of what changed upstream since the last rebase.
 
+--no-verify::
+       This option bypasses the pre-rebase hook.  See also linkgit:githooks[5].
+
 -C<n>::
        Ensure at least <n> lines of surrounding context match before
        and after each change.  When fewer lines of surrounding
@@ -250,18 +253,16 @@ OPTIONS
 
 -p::
 --preserve-merges::
-       Instead of ignoring merges, try to recreate them.  This option
-       only works in interactive mode.
+       Instead of ignoring merges, try to recreate them.
 
 include::merge-strategies.txt[]
 
 NOTES
 -----
-When you rebase a branch, you are changing its history in a way that
-will cause problems for anyone who already has a copy of the branch
-in their repository and tries to pull updates from you.  You should
-understand the implications of using 'git-rebase' on a repository that
-you share.
+
+You should understand the implications of using 'git-rebase' on a
+repository that you share.  See also RECOVERING FROM UPSTREAM REBASE
+below.
 
 When the git-rebase command is run, it will first execute a "pre-rebase"
 hook if one exists.  You can use this hook to do sanity checks and
@@ -396,6 +397,127 @@ consistent (they compile, pass the testsuite, etc.) you should use
 after each commit, test, and amend the commit if fixes are necessary.
 
 
+RECOVERING FROM UPSTREAM REBASE
+-------------------------------
+
+Rebasing (or any other form of rewriting) a branch that others have
+based work on is a bad idea: anyone downstream of it is forced to
+manually fix their history.  This section explains how to do the fix
+from the downstream's point of view.  The real fix, however, would be
+to avoid rebasing the upstream in the first place.
+
+To illustrate, suppose you are in a situation where someone develops a
+'subsystem' branch, and you are working on a 'topic' that is dependent
+on this 'subsystem'.  You might end up with a history like the
+following:
+
+------------
+    o---o---o---o---o---o---o---o---o  master
+        \
+         o---o---o---o---o  subsystem
+                          \
+                           *---*---*  topic
+------------
+
+If 'subsystem' is rebased against 'master', the following happens:
+
+------------
+    o---o---o---o---o---o---o---o  master
+        \                       \
+         o---o---o---o---o       o'--o'--o'--o'--o'  subsystem
+                          \
+                           *---*---*  topic
+------------
+
+If you now continue development as usual, and eventually merge 'topic'
+to 'subsystem', the commits from 'subsystem' will remain duplicated forever:
+
+------------
+    o---o---o---o---o---o---o---o  master
+        \                       \
+         o---o---o---o---o       o'--o'--o'--o'--o'--M  subsystem
+                          \                         /
+                           *---*---*-..........-*--*  topic
+------------
+
+Such duplicates are generally frowned upon because they clutter up
+history, making it harder to follow.  To clean things up, you need to
+transplant the commits on 'topic' to the new 'subsystem' tip, i.e.,
+rebase 'topic'.  This becomes a ripple effect: anyone downstream from
+'topic' is forced to rebase too, and so on!
+
+There are two kinds of fixes, discussed in the following subsections:
+
+Easy case: The changes are literally the same.::
+
+       This happens if the 'subsystem' rebase was a simple rebase and
+       had no conflicts.
+
+Hard case: The changes are not the same.::
+
+       This happens if the 'subsystem' rebase had conflicts, or used
+       `\--interactive` to omit, edit, or squash commits; or if the
+       upstream used one of `commit \--amend`, `reset`, or
+       `filter-branch`.
+
+
+The easy case
+~~~~~~~~~~~~~
+
+Only works if the changes (patch IDs based on the diff contents) on
+'subsystem' are literally the same before and after the rebase
+'subsystem' did.
+
+In that case, the fix is easy because 'git-rebase' knows to skip
+changes that are already present in the new upstream.  So if you say
+(assuming you're on 'topic')
+------------
+    $ git rebase subsystem
+------------
+you will end up with the fixed history
+------------
+    o---o---o---o---o---o---o---o  master
+                                \
+                                 o'--o'--o'--o'--o'  subsystem
+                                                  \
+                                                   *---*---*  topic
+------------
+
+
+The hard case
+~~~~~~~~~~~~~
+
+Things get more complicated if the 'subsystem' changes do not exactly
+correspond to the ones before the rebase.
+
+NOTE: While an "easy case recovery" sometimes appears to be successful
+      even in the hard case, it may have unintended consequences.  For
+      example, a commit that was removed via `git rebase
+      \--interactive` will be **resurrected**!
+
+The idea is to manually tell 'git-rebase' "where the old 'subsystem'
+ended and your 'topic' began", that is, what the old merge-base
+between them was.  You will have to find a way to name the last commit
+of the old 'subsystem', for example:
+
+* With the 'subsystem' reflog: after 'git-fetch', the old tip of
+  'subsystem' is at `subsystem@\{1}`.  Subsequent fetches will
+  increase the number.  (See linkgit:git-reflog[1].)
+
+* Relative to the tip of 'topic': knowing that your 'topic' has three
+  commits, the old tip of 'subsystem' must be `topic~3`.
+
+You can then transplant the old `subsystem..topic` to the new tip by
+saying (for the reflog case, and assuming you are on 'topic' already):
+------------
+    $ git rebase --onto subsystem subsystem@{1}
+------------
+
+The ripple effect of a "hard case" recovery is especially bad:
+'everyone' downstream from 'topic' will now have to perform a "hard
+case" recovery too!
+
+
 Authors
 ------
 Written by Junio C Hamano <gitster@pobox.com> and
index bb99810ec76f93ff1cdc59aacdf592c64419701a..fad983e297514da7e847196b0375e3254fdf235d 100644 (file)
@@ -11,6 +11,7 @@ SYNOPSIS
 [verse]
 'git remote' [-v | --verbose]
 'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>
+'git remote rename' <old> <new>
 'git remote rm' <name>
 'git remote show' [-n] <name>
 'git remote prune' [-n | --dry-run] <name>
@@ -61,6 +62,15 @@ only makes sense in bare repositories.  If a remote uses mirror
 mode, furthermore, `git push` will always behave as if `\--mirror`
 was passed.
 
+'rename'::
+
+Rename the remote named <old> to <new>. All remote tracking branches and
+configuration settings for the remote are updated.
++
+In case <old> and <new> are the same, and <old> is a file under
+`$GIT_DIR/remotes` or `$GIT_DIR/branches`, the remote is converted to
+the configuration file format.
+
 'rm'::
 
 Remove the remote named <name>. All remote tracking branches and
index 6abaeac28cb70bcff809c803d732f79630c8046f..52aab5e68002cbc2f132f57997ecbeb48de998c3 100644 (file)
@@ -82,7 +82,9 @@ $ git reset --hard HEAD~3   <1>
 +
 <1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
 and you do not want to ever see them again.  Do *not* do this if
-you have already given these commits to somebody else.
+you have already given these commits to somebody else.  (See the
+"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
+the implications of doing so.)
 
 Undo a commit, making it a topic branch::
 +
index df420aeb331592192ab27d76af780c8c97a95e87..7e0a04143674506e700f35a1d3c47ed871722064 100644 (file)
@@ -43,9 +43,11 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.0.2/git.html[documentation for release 1.6.0.2]
+* link:v1.6.0.4/git.html[documentation for release 1.6.0.4]
 
 * release notes for
+  link:RelNotes-1.6.0.4.txt[1.6.0.4],
+  link:RelNotes-1.6.0.3.txt[1.6.0.3],
   link:RelNotes-1.6.0.2.txt[1.6.0.2],
   link:RelNotes-1.6.0.1.txt[1.6.0.1],
   link:RelNotes-1.6.0.txt[1.6.0].
index 26945593cb1739bb6a6e1e2acc2cd78caab3a102..a172baf993e5171932533ad713a5d0b34cb6850b 100644 (file)
@@ -163,8 +163,8 @@ few exceptions.  Even though...
 `ident`
 ^^^^^^^
 
-When the attribute `ident` is set to a path, git replaces
-`$Id$` in the blob object with `$Id:`, followed by
+When the attribute `ident` is set for a path, git replaces
+`$Id$` in the blob object with `$Id:`, followed by the
 40-character hexadecimal blob object name, followed by a dollar
 sign `$` upon checkout.  Any byte sequence that begins with
 `$Id:` and ends with `$` in the worktree file is replaced
@@ -213,10 +213,15 @@ with `crlf`, and then `ident` and fed to `filter`.
 Generating diff text
 ~~~~~~~~~~~~~~~~~~~~
 
-The attribute `diff` affects if 'git-diff' generates textual
-patch for the path or just says `Binary files differ`.  It also
-can affect what line is shown on the hunk header `@@ -k,l +n,m @@`
-line.
+`diff`
+^^^^^^
+
+The attribute `diff` affects how 'git' generates diffs for particular
+files. It can tell git whether to generate a textual patch for the path
+or to treat the path as a binary file.  It can also affect what line is
+shown on the hunk header `@@ -k,l +n,m @@` line, tell git to use an
+external command to generate the diff, or ask git to convert binary
+files to a text format before generating the diff.
 
 Set::
 
@@ -227,7 +232,8 @@ Set::
 Unset::
 
        A path to which the `diff` attribute is unset will
-       generate `Binary files differ`.
+       generate `Binary files differ` (or a binary patch, if
+       binary patches are enabled).
 
 Unspecified::
 
@@ -238,21 +244,21 @@ Unspecified::
 
 String::
 
-       Diff is shown using the specified custom diff driver.
-       The driver program is given its input using the same
-       calling convention as used for GIT_EXTERNAL_DIFF
-       program.  This name is also used for custom hunk header
-       selection.
+       Diff is shown using the specified diff driver.  Each driver may
+       specify one or more options, as described in the following
+       section. The options for the diff driver "foo" are defined
+       by the configuration variables in the "diff.foo" section of the
+       git config file.
 
 
-Defining a custom diff driver
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Defining an external diff driver
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The definition of a diff driver is done in `gitconfig`, not
 `gitattributes` file, so strictly speaking this manual page is a
 wrong place to talk about it.  However...
 
-To define a custom diff driver `jcdiff`, add a section to your
+To define an external diff driver `jcdiff`, add a section to your
 `$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
 
 ----------------------------------------------------------------
@@ -328,9 +334,49 @@ patterns are available:
 - `tex` suitable for source code for LaTeX documents.
 
 
+Performing text diffs of binary files
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Sometimes it is desirable to see the diff of a text-converted
+version of some binary files. For example, a word processor
+document can be converted to an ASCII text representation, and
+the diff of the text shown. Even though this conversion loses
+some information, the resulting diff is useful for human
+viewing (but cannot be applied directly).
+
+The `textconv` config option is used to define a program for
+performing such a conversion. The program should take a single
+argument, the name of a file to convert, and produce the
+resulting text on stdout.
+
+For example, to show the diff of the exif information of a
+file instead of the binary information (assuming you have the
+exif tool installed):
+
+------------------------
+[diff "jpg"]
+       textconv = exif
+------------------------
+
+NOTE: The text conversion is generally a one-way conversion;
+in this example, we lose the actual image contents and focus
+just on the text data. This means that diffs generated by
+textconv are _not_ suitable for applying. For this reason,
+only `git diff` and the `git log` family of commands (i.e.,
+log, whatchanged, show) will perform text conversion. `git
+format-patch` will never generate this output. If you want to
+send somebody a text-converted diff of a binary file (e.g.,
+because it quickly conveys the changes you have made), you
+should generate it separately and send it as a comment _in
+addition to_ the usual binary diff that you might send.
+
+
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+`merge`
+^^^^^^^
+
 The attribute `merge` affects how three versions of a file is
 merged when a file-level merge is necessary during `git merge`,
 and other programs such as `git revert` and `git cherry-pick`.
index 660904686c656fd00078aa272d0b9a5a198e1833..bab0f34b452bfab7174d216700b46220e3a4d2c3 100644 (file)
@@ -32,22 +32,27 @@ Initialized empty Git repository in .git/
 $ echo 'hello world' > file.txt
 $ git add .
 $ git commit -a -m "initial commit"
-Created initial commit 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
+[master (root-commit)] created 54196cc: "initial commit"
+ 1 files changed, 1 insertions(+), 0 deletions(-)
  create mode 100644 file.txt
 $ echo 'hello world!' >file.txt
 $ git commit -a -m "add emphasis"
-Created commit c4d59f390b9cfd4318117afde11d601c1085f241
+[master] created c4d59f3: "add emphasis"
+ 1 files changed, 1 insertions(+), 1 deletions(-)
 ------------------------------------------------
 
-What are the 40 digits of hex that git responded to the commit with?
+What are the 7 digits of hex that git responded to the commit with?
 
 We saw in part one of the tutorial that commits have names like this.
 It turns out that every object in the git history is stored under
-such a 40-digit hex name.  That name is the SHA1 hash of the object's
+a 40-digit hex name.  That name is the SHA1 hash of the object's
 contents; among other things, this ensures that git will never store
 the same data twice (since identical data is given an identical SHA1
 name), and that the contents of a git object will never change (since
-that would change the object's name as well).
+that would change the object's name as well). The 7 char hex strings
+here are simply the abbreviation of such 40 character long strings.
+Abbreviations can be used everywhere where the 40 character strings
+can be used, so long as they are unambiguous.
 
 It is expected that the content of the commit object you created while
 following the example above generates a different SHA1 hash than
diff --git a/Documentation/gitworkflows.txt b/Documentation/gitworkflows.txt
new file mode 100644 (file)
index 0000000..2b021e3
--- /dev/null
@@ -0,0 +1,364 @@
+gitworkflows(7)
+===============
+
+NAME
+----
+gitworkflows - An overview of recommended workflows with git
+
+SYNOPSIS
+--------
+git *
+
+
+DESCRIPTION
+-----------
+
+This document attempts to write down and motivate some of the workflow
+elements used for `git.git` itself.  Many ideas apply in general,
+though the full workflow is rarely required for smaller projects with
+fewer people involved.
+
+We formulate a set of 'rules' for quick reference, while the prose
+tries to motivate each of them.  Do not always take them literally;
+you should value good reasons for your actions higher than manpages
+such as this one.
+
+
+SEPARATE CHANGES
+----------------
+
+As a general rule, you should try to split your changes into small
+logical steps, and commit each of them.  They should be consistent,
+working independently of any later commits, pass the test suite, etc.
+This makes the review process much easier, and the history much more
+useful for later inspection and analysis, for example with
+linkgit:git-blame[1] and linkgit:git-bisect[1].
+
+To achieve this, try to split your work into small steps from the very
+beginning. It is always easier to squash a few commits together than
+to split one big commit into several.  Don't be afraid of making too
+small or imperfect steps along the way. You can always go back later
+and edit the commits with `git rebase \--interactive` before you
+publish them.  You can use `git stash save \--keep-index` to run the
+test suite independent of other uncommitted changes; see the EXAMPLES
+section of linkgit:git-stash[1].
+
+
+MANAGING BRANCHES
+-----------------
+
+There are two main tools that can be used to include changes from one
+branch on another: linkgit:git-merge[1] and
+linkgit:git-cherry-pick[1].
+
+Merges have many advantages, so we try to solve as many problems as
+possible with merges alone.  Cherry-picking is still occasionally
+useful; see "Merging upwards" below for an example.
+
+Most importantly, merging works at the branch level, while
+cherry-picking works at the commit level.  This means that a merge can
+carry over the changes from 1, 10, or 1000 commits with equal ease,
+which in turn means the workflow scales much better to a large number
+of contributors (and contributions).  Merges are also easier to
+understand because a merge commit is a "promise" that all changes from
+all its parents are now included.
+
+There is a tradeoff of course: merges require a more careful branch
+management.  The following subsections discuss the important points.
+
+
+Graduation
+~~~~~~~~~~
+
+As a given feature goes from experimental to stable, it also
+"graduates" between the corresponding branches of the software.
+`git.git` uses the following 'integration branches':
+
+* 'maint' tracks the commits that should go into the next "maintenance
+  release", i.e., update of the last released stable version;
+
+* 'master' tracks the commits that should go into the next release;
+
+* 'next' is intended as a testing branch for topics being tested for
+  stability for master.
+
+There is a fourth official branch that is used slightly differently:
+
+* 'pu' (proposed updates) is an integration branch for things that are
+  not quite ready for inclusion yet (see "Integration Branches"
+  below).
+
+Each of the four branches is usually a direct descendant of the one
+above it.
+
+Conceptually, the feature enters at an unstable branch (usually 'next'
+or 'pu'), and "graduates" to 'master' for the next release once it is
+considered stable enough.
+
+
+Merging upwards
+~~~~~~~~~~~~~~~
+
+The "downwards graduation" discussed above cannot be done by actually
+merging downwards, however, since that would merge 'all' changes on
+the unstable branch into the stable one.  Hence the following:
+
+.Merge upwards
+[caption="Rule: "]
+=====================================
+Always commit your fixes to the oldest supported branch that require
+them.  Then (periodically) merge the integration branches upwards into each
+other.
+=====================================
+
+This gives a very controlled flow of fixes.  If you notice that you
+have applied a fix to e.g. 'master' that is also required in 'maint',
+you will need to cherry-pick it (using linkgit:git-cherry-pick[1])
+downwards.  This will happen a few times and is nothing to worry about
+unless you do it very frequently.
+
+
+Topic branches
+~~~~~~~~~~~~~~
+
+Any nontrivial feature will require several patches to implement, and
+may get extra bugfixes or improvements during its lifetime.
+
+Committing everything directly on the integration branches leads to many
+problems: Bad commits cannot be undone, so they must be reverted one
+by one, which creates confusing histories and further error potential
+when you forget to revert part of a group of changes.  Working in
+parallel mixes up the changes, creating further confusion.
+
+Use of "topic branches" solves these problems.  The name is pretty
+self explanatory, with a caveat that comes from the "merge upwards"
+rule above:
+
+.Topic branches
+[caption="Rule: "]
+=====================================
+Make a side branch for every topic (feature, bugfix, ...). Fork it off
+at the oldest integration branch that you will eventually want to merge it
+into.
+=====================================
+
+Many things can then be done very naturally:
+
+* To get the feature/bugfix into an integration branch, simply merge
+  it.  If the topic has evolved further in the meantime, merge again.
+  (Note that you do not necessarily have to merge it to the oldest
+  integration branch first.  For example, you can first merge a bugfix
+  to 'next', give it some testing time, and merge to 'maint' when you
+  know it is stable.)
+
+* If you find you need new features from the branch 'other' to continue
+  working on your topic, merge 'other' to 'topic'.  (However, do not
+  do this "just habitually", see below.)
+
+* If you find you forked off the wrong branch and want to move it
+  "back in time", use linkgit:git-rebase[1].
+
+Note that the last point clashes with the other two: a topic that has
+been merged elsewhere should not be rebased.  See the section on
+RECOVERING FROM UPSTREAM REBASE in linkgit:git-rebase[1].
+
+We should point out that "habitually" (regularly for no real reason)
+merging an integration branch into your topics -- and by extension,
+merging anything upstream into anything downstream on a regular basis
+-- is frowned upon:
+
+.Merge to downstream only at well-defined points
+[caption="Rule: "]
+=====================================
+Do not merge to downstream except with a good reason: upstream API
+changes affect your branch; your branch no longer merges to upstream
+cleanly; etc.
+=====================================
+
+Otherwise, the topic that was merged to suddenly contains more than a
+single (well-separated) change.  The many resulting small merges will
+greatly clutter up history.  Anyone who later investigates the history
+of a file will have to find out whether that merge affected the topic
+in development.  An upstream might even inadvertently be merged into a
+"more stable" branch.  And so on.
+
+
+Throw-away integration
+~~~~~~~~~~~~~~~~~~~~~~
+
+If you followed the last paragraph, you will now have many small topic
+branches, and occasionally wonder how they interact.  Perhaps the
+result of merging them does not even work?  But on the other hand, we
+want to avoid merging them anywhere "stable" because such merges
+cannot easily be undone.
+
+The solution, of course, is to make a merge that we can undo: merge
+into a throw-away branch.
+
+.Throw-away integration branches
+[caption="Rule: "]
+=====================================
+To test the interaction of several topics, merge them into a
+throw-away branch.  You must never base any work on such a branch!
+=====================================
+
+If you make it (very) clear that this branch is going to be deleted
+right after the testing, you can even publish this branch, for example
+to give the testers a chance to work with it, or other developers a
+chance to see if their in-progress work will be compatible.  `git.git`
+has such an official throw-away integration branch called 'pu'.
+
+
+DISTRIBUTED WORKFLOWS
+---------------------
+
+After the last section, you should know how to manage topics.  In
+general, you will not be the only person working on the project, so
+you will have to share your work.
+
+Roughly speaking, there are two important workflows: merge and patch.
+The important difference is that the merge workflow can propagate full
+history, including merges, while patches cannot.  Both workflows can
+be used in parallel: in `git.git`, only subsystem maintainers use
+the merge workflow, while everyone else sends patches.
+
+Note that the maintainer(s) may impose restrictions, such as
+"Signed-off-by" requirements, that all commits/patches submitted for
+inclusion must adhere to.  Consult your project's documentation for
+more information.
+
+
+Merge workflow
+~~~~~~~~~~~~~~
+
+The merge workflow works by copying branches between upstream and
+downstream.  Upstream can merge contributions into the official
+history; downstream base their work on the official history.
+
+There are three main tools that can be used for this:
+
+* linkgit:git-push[1] copies your branches to a remote repository,
+  usually to one that can be read by all involved parties;
+
+* linkgit:git-fetch[1] that copies remote branches to your repository;
+  and
+
+* linkgit:git-pull[1] that does fetch and merge in one go.
+
+Note the last point.  Do 'not' use 'git-pull' unless you actually want
+to merge the remote branch.
+
+Getting changes out is easy:
+
+.Push/pull: Publishing branches/topics
+[caption="Recipe: "]
+=====================================
+`git push <remote> <branch>` and tell everyone where they can fetch
+from.
+=====================================
+
+You will still have to tell people by other means, such as mail.  (Git
+provides the linkgit:git-request-pull[1] to send preformatted pull
+requests to upstream maintainers to simplify this task.)
+
+If you just want to get the newest copies of the integration branches,
+staying up to date is easy too:
+
+.Push/pull: Staying up to date
+[caption="Recipe: "]
+=====================================
+Use `git fetch <remote>` or `git remote update` to stay up to date.
+=====================================
+
+Then simply fork your topic branches from the stable remotes as
+explained earlier.
+
+If you are a maintainer and would like to merge other people's topic
+branches to the integration branches, they will typically send a
+request to do so by mail.  Such a request looks like
+
+-------------------------------------
+Please pull from
+    <url> <branch>
+-------------------------------------
+
+In that case, 'git-pull' can do the fetch and merge in one go, as
+follows.
+
+.Push/pull: Merging remote topics
+[caption="Recipe: "]
+=====================================
+`git pull <url> <branch>`
+=====================================
+
+Occasionally, the maintainer may get merge conflicts when he tries to
+pull changes from downstream.  In this case, he can ask downstream to
+do the merge and resolve the conflicts themselves (perhaps they will
+know better how to resolve them).  It is one of the rare cases where
+downstream 'should' merge from upstream.
+
+
+Patch workflow
+~~~~~~~~~~~~~~
+
+If you are a contributor that sends changes upstream in the form of
+emails, you should use topic branches as usual (see above).  Then use
+linkgit:git-format-patch[1] to generate the corresponding emails
+(highly recommended over manually formatting them because it makes the
+maintainer's life easier).
+
+.format-patch/am: Publishing branches/topics
+[caption="Recipe: "]
+=====================================
+* `git format-patch -M upstream..topic` to turn them into preformatted
+  patch files
+* `git send-email --to=<recipient> <patches>`
+=====================================
+
+See the linkgit:git-format-patch[1] and linkgit:git-send-email[1]
+manpages for further usage notes.
+
+If the maintainer tells you that your patch no longer applies to the
+current upstream, you will have to rebase your topic (you cannot use a
+merge because you cannot format-patch merges):
+
+.format-patch/am: Keeping topics up to date
+[caption="Recipe: "]
+=====================================
+`git pull --rebase <url> <branch>`
+=====================================
+
+You can then fix the conflicts during the rebase.  Presumably you have
+not published your topic other than by mail, so rebasing it is not a
+problem.
+
+If you receive such a patch series (as maintainer, or perhaps as a
+reader of the mailing list it was sent to), save the mails to files,
+create a new topic branch and use 'git-am' to import the commits:
+
+.format-patch/am: Importing patches
+[caption="Recipe: "]
+=====================================
+`git am < patch`
+=====================================
+
+One feature worth pointing out is the three-way merge, which can help
+if you get conflicts: `git am -3` will use index information contained
+in patches to figure out the merge base.  See linkgit:git-am[1] for
+other options.
+
+
+SEE ALSO
+--------
+linkgit:gittutorial[7],
+linkgit:git-push[1],
+linkgit:git-pull[1],
+linkgit:git-merge[1],
+linkgit:git-rebase[1],
+linkgit:git-format-patch[1],
+linkgit:git-send-email[1],
+linkgit:git-am[1]
+
+GIT
+---
+Part of the linkgit:git[1] suite.
index d2970f8357505f5973989f2e118b812d3d3dde67..2cdacd94cd1b53536d4726e05a9f9f8620e85ec6 100644 (file)
@@ -37,9 +37,9 @@ of `i18n.commitencoding` in its `encoding` header.  This is to
 help other people who look at them later.  Lack of this header
 implies that the commit log message is encoded in UTF-8.
 
-. 'git-log', 'git-show' and friends looks at the `encoding`
-  header of a commit object, and tries to re-code the log
-  message into UTF-8 unless otherwise specified.  You can
+. 'git-log', 'git-show', 'git-blame' and friends look at the
+  `encoding` header of a commit object, and try to re-code the
+  log message into UTF-8 unless otherwise specified.  You can
   specify the desired output encoding with
   `i18n.logoutputencoding` in `.git/config` file, like this:
 +
index 0ce916a1887b0846bfc5a6e2233242601e0dde79..6d7cf6d51fc5c2511c8ed411ed36aefd93d4ad60 100644 (file)
@@ -174,6 +174,10 @@ endif::git-rev-list[]
        Limit the commits output to ones with log message that
        matches the specified pattern (regular expression).
 
+--all-match::
+       Limit the commits output to ones that match all given --grep,
+       --author and --committer instead of ones that match at least one.
+
 -i::
 --regexp-ignore-case::
 
@@ -281,8 +285,52 @@ See also linkgit:git-reflog[1].
 History Simplification
 ~~~~~~~~~~~~~~~~~~~~~~
 
-When optional paths are given, 'git-rev-list' simplifies commits with
-various strategies, according to the options you have selected.
+Sometimes you are only interested in parts of the history, for example the
+commits modifying a particular <path>. But there are two parts of
+'History Simplification', one part is selecting the commits and the other
+is how to do it, as there are various strategies to simplify the history.
+
+The following options select the commits to be shown:
+
+<paths>::
+
+       Commits modifying the given <paths> are selected.
+
+--simplify-by-decoration::
+
+       Commits that are referred by some branch or tag are selected.
+
+Note that extra commits can be shown to give a meaningful history.
+
+The following options affect the way the simplification is performed:
+
+Default mode::
+
+       Simplifies the history to the simplest history explaining the
+       final state of the tree. Simplest because it prunes some side
+       branches if the end result is the same (i.e. merging branches
+       with the same content)
+
+--full-history::
+
+       As the default mode but does not prune some history.
+
+--dense::
+
+       Only the selected commits are shown, plus some to have a
+       meaningful history.
+
+--sparse::
+
+       All commits in the simplified history are shown.
+
+--simplify-merges::
+
+       Additional option to '--full-history' to remove some needless
+       merges from the resulting history, as there are no selected
+       commits contributing to this merge.
+
+A more detailed explanation follows.
 
 Suppose you specified `foo` as the <paths>.  We shall call commits
 that modify `foo` !TREESAME, and the rest TREESAME.  (In a diff
@@ -452,6 +500,14 @@ Note the major differences in `N` and `P` over '\--full-history':
   removed completely, because it had one parent and is TREESAME.
 --
 
+The '\--simplify-by-decoration' option allows you to view only the
+big picture of the topology of the history, by omitting commits
+that are not referenced by tags.  Commits are marked as !TREESAME
+(in other words, kept after history simplification rules described
+above) if (1) they are referenced by tags, or (2) they change the
+contents of the paths given on the command line.  All other
+commits are marked as TREESAME (subject to be simplified away).
+
 ifdef::git-rev-list[]
 Bisection Helpers
 ~~~~~~~~~~~~~~~~~
@@ -462,14 +518,14 @@ Limit output to the one commit object which is roughly halfway between
 the included and excluded commits. Thus, if
 
 -----------------------------------------------------------------------
-       $ git-rev-list --bisect foo ^bar ^baz
+       $ git rev-list --bisect foo ^bar ^baz
 -----------------------------------------------------------------------
 
 outputs 'midpoint', the output of the two commands
 
 -----------------------------------------------------------------------
-       $ git-rev-list foo ^midpoint
-       $ git-rev-list midpoint ^bar ^baz
+       $ git rev-list foo ^midpoint
+       $ git rev-list midpoint ^bar ^baz
 -----------------------------------------------------------------------
 
 would be of roughly the same length.  Finding the change which
index 504ae8a53bca42d7c9ec560b65ddfe14699387a4..41ec7774f481fd2d70492be4ab2b5e0bf887fc0b 100644 (file)
@@ -68,13 +68,22 @@ This file should have the following format:
 ------------
 
 `<url>` is required; `#<head>` is optional.
-When you do not provide a refspec on the command line,
-git will use the following refspec, where `<head>` defaults to `master`,
-and `<repository>` is the name of this file
-you provided in the command line.
+
+Depending on the operation, git will use one of the following
+refspecs, if you don't provide one on the command line.
+`<branch>` is the name of this file in `$GIT_DIR/branches` and
+`<head>` defaults to `master`.
+
+git fetch uses:
+
+------------
+       refs/heads/<head>:refs/heads/<branch>
+------------
+
+git push uses:
 
 ------------
-       refs/heads/<head>:<repository>
+       HEAD:refs/heads/<head>
 ------------
 
 
index 08d1310bf5fc5590ada1ee5b2af77d361ff4d874..645d752c5c26d8724e8ded0abe1f207bf3ff6854 100644 (file)
@@ -4356,7 +4356,9 @@ $ git remote show example # get details
 * remote example
   URL: git://example.com/project.git
   Tracked remote branches
-    master next ...
+    master
+    next
+    ...
 $ git fetch example            # update branches from example
 $ git branch -r                        # list all remote branches
 -----------------------------------------------
diff --git a/INSTALL b/INSTALL
index a4fd8624bc6550d0553f2271343f4e6b610be103..d1deb0b3c7a9aba961d40fe541490562fee486ac 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -126,8 +126,9 @@ Issues of note:
 
        http://www.kernel.org/pub/software/scm/git/docs/
 
-   There are also "make quick-install-doc" and "make quick-install-html"
-   which install preformatted man pages and html documentation.
+   There are also "make quick-install-doc", "make quick-install-man"
+   and "make quick-install-html" which install preformatted man pages
+   and html documentation.
    This does not require asciidoc/xmlto, but it only works from within
    a cloned checkout of git.git with these two extra branches, and will
    not work for the maintainer for obvious chicken-and-egg reasons.
index 308dc70b5de118083a14bcb2665372a40ae6522c..35adafa011bdc8b3728bea942d86fba96a90fe6a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -229,6 +229,7 @@ INSTALL = install
 RPMBUILD = rpmbuild
 TCL_PATH = tclsh
 TCLTK_PATH = wish
+PTHREAD_LIBS = -lpthread
 
 export TCL_PATH TCLTK_PATH
 
@@ -389,6 +390,7 @@ LIB_H += transport.h
 LIB_H += tree.h
 LIB_H += tree-walk.h
 LIB_H += unpack-trees.h
+LIB_H += userdiff.h
 LIB_H += utf8.h
 LIB_H += wt-status.h
 
@@ -485,6 +487,7 @@ LIB_OBJS += tree-diff.o
 LIB_OBJS += tree.o
 LIB_OBJS += tree-walk.o
 LIB_OBJS += unpack-trees.o
+LIB_OBJS += userdiff.o
 LIB_OBJS += usage.o
 LIB_OBJS += utf8.o
 LIB_OBJS += walker.o
@@ -638,8 +641,6 @@ ifeq ($(uname_S),Darwin)
        endif
        NO_STRLCPY = YesPlease
        NO_MEMMEM = YesPlease
-       COMPAT_CFLAGS += -Icompat/regex
-       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
@@ -690,8 +691,11 @@ ifeq ($(uname_S),FreeBSD)
        BASIC_LDFLAGS += -L/usr/local/lib
        DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
        THREADED_DELTA_SEARCH = YesPlease
-       COMPAT_CFLAGS += -Icompat/regex
-       COMPAT_OBJS += compat/regex/regex.o
+       ifeq ($(shell expr "$(uname_R)" : '4\.'),2)
+               PTHREAD_LIBS = -pthread
+               NO_UINTMAX_T = YesPlease
+               NO_STRTOUMAX = YesPlease
+       endif
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -718,8 +722,6 @@ ifeq ($(uname_S),AIX)
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV=YesPlease
        BASIC_CFLAGS += -D_LARGE_FILES
-       COMPAT_CFLAGS += -Icompat/regex
-       COMPAT_OBJS += compat/regex/regex.o
 endif
 ifeq ($(uname_S),GNU)
        # GNU/Hurd
@@ -953,6 +955,9 @@ endif
 ifdef NO_IPV6
        BASIC_CFLAGS += -DNO_IPV6
 endif
+ifdef NO_UINTMAX_T
+       BASIC_CFLAGS += -Duintmax_t=uint32_t
+endif
 ifdef NO_SOCKADDR_STORAGE
 ifdef NO_IPV6
        BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in
@@ -1014,7 +1019,7 @@ endif
 
 ifdef THREADED_DELTA_SEARCH
        BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
-       EXTLIBS += -lpthread
+       EXTLIBS += $(PTHREAD_LIBS)
        LIB_OBJS += thread-utils.o
 endif
 ifdef DIR_HAS_BSD_GROUP_SEMANTICS
@@ -1359,7 +1364,16 @@ check-sha1:: test-sha1$X
        ./test-sha1.sh
 
 check: common-cmds.h
-       for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
+       if sparse; \
+       then \
+               for i in *.c; \
+               do \
+                       sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \
+               done; \
+       else \
+               echo 2>&1 "Did you mean 'make test'?"; \
+               exit 1; \
+       fi
 
 remove-dashes:
        ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
@@ -1409,6 +1423,9 @@ endif
 install-doc:
        $(MAKE) -C Documentation install
 
+install-man:
+       $(MAKE) -C Documentation install-man
+
 install-html:
        $(MAKE) -C Documentation install-html
 
@@ -1418,6 +1435,9 @@ install-info:
 quick-install-doc:
        $(MAKE) -C Documentation quick-install
 
+quick-install-man:
+       $(MAKE) -C Documentation quick-install-man
+
 quick-install-html:
        $(MAKE) -C Documentation quick-install-html
 
index 849eed553e9d6455c021664135a01b322eef9a31..9ac455d889b72deba8c949da1d9efe2be3a50244 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -336,5 +336,7 @@ int write_archive(int argc, const char **argv, const char *prefix,
        parse_treeish_arg(argv, &args, prefix);
        parse_pathspec_arg(argv + 1, &args);
 
+       git_config(git_default_config, NULL);
+
        return ar->write_archive(&args);
 }
index b1e59f2196b933ab7169a30efc5d1d340f8f9c5c..b1ac837f3d30c826ddbe29492b906bf2d0de0a1a 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -129,7 +129,9 @@ void create_branch(const char *head,
                        die("Cannot setup tracking information; starting point is not a branch.");
                break;
        case 1:
-               /* Unique completion -- good */
+               /* Unique completion -- good, only if it is a real ref */
+               if (track == BRANCH_TRACK_EXPLICIT && !strcmp(real_ref, "HEAD"))
+                       die("Cannot setup tracking information; starting point is not a branch.");
                break;
        default:
                die("Ambiguous object name: '%s'.", start_name);
@@ -168,5 +170,6 @@ void remove_branch_state(void)
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_RR"));
        unlink(git_path("MERGE_MSG"));
+       unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
 }
index cfd8fceb9033cf5c4c7924d96437b94a9c4fe226..4c4d1e1774ade358dbc3283df5d1b764a32821b8 100644 (file)
@@ -2841,8 +2841,8 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                unsigned int nr = getpid();
 
                for (;;) {
-                       const char *newpath;
-                       newpath = mkpath("%s~%u", path, nr);
+                       char newpath[PATH_MAX];
+                       mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
                        if (!try_create_file(newpath, mode, buf, size)) {
                                if (!rename(newpath, path))
                                        return;
index 432ce2acc6bb687b35f3e9663f3058ebef2354e7..5ceec433fd590e8bf6a51700ea69c37f9af30fa7 100644 (file)
@@ -111,8 +111,6 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
 {
        const char *remote = NULL;
 
-       git_config(git_default_config, NULL);
-
        remote = extract_remote_arg(&argc, argv);
        if (remote)
                return run_remote_archiver(remote, argc, argv);
index 48cc0c175d52446266658d785de0311daaaf4776..a0d60145f26d32e45f4d04c22164cbe8060739ca 100644 (file)
@@ -442,131 +442,6 @@ static struct origin *find_rename(struct scoreboard *sb,
        return porigin;
 }
 
-/*
- * Parsing of patch chunks...
- */
-struct chunk {
-       /* line number in postimage; up to but not including this
-        * line is the same as preimage
-        */
-       int same;
-
-       /* preimage line number after this chunk */
-       int p_next;
-
-       /* postimage line number after this chunk */
-       int t_next;
-};
-
-struct patch {
-       struct chunk *chunks;
-       int num;
-};
-
-struct blame_diff_state {
-       struct patch *ret;
-       unsigned hunk_post_context;
-       unsigned hunk_in_pre_context : 1;
-};
-
-static void process_u_diff(void *state_, char *line, unsigned long len)
-{
-       struct blame_diff_state *state = state_;
-       struct chunk *chunk;
-       int off1, off2, len1, len2, num;
-
-       num = state->ret->num;
-       if (len < 4 || line[0] != '@' || line[1] != '@') {
-               if (state->hunk_in_pre_context && line[0] == ' ')
-                       state->ret->chunks[num - 1].same++;
-               else {
-                       state->hunk_in_pre_context = 0;
-                       if (line[0] == ' ')
-                               state->hunk_post_context++;
-                       else
-                               state->hunk_post_context = 0;
-               }
-               return;
-       }
-
-       if (num && state->hunk_post_context) {
-               chunk = &state->ret->chunks[num - 1];
-               chunk->p_next -= state->hunk_post_context;
-               chunk->t_next -= state->hunk_post_context;
-       }
-       state->ret->num = ++num;
-       state->ret->chunks = xrealloc(state->ret->chunks,
-                                     sizeof(struct chunk) * num);
-       chunk = &state->ret->chunks[num - 1];
-       if (parse_hunk_header(line, len, &off1, &len1, &off2, &len2)) {
-               state->ret->num--;
-               return;
-       }
-
-       /* Line numbers in patch output are one based. */
-       off1--;
-       off2--;
-
-       chunk->same = len2 ? off2 : (off2 + 1);
-
-       chunk->p_next = off1 + (len1 ? len1 : 1);
-       chunk->t_next = chunk->same + len2;
-       state->hunk_in_pre_context = 1;
-       state->hunk_post_context = 0;
-}
-
-static struct patch *compare_buffer(mmfile_t *file_p, mmfile_t *file_o,
-                                   int context)
-{
-       struct blame_diff_state state;
-       xpparam_t xpp;
-       xdemitconf_t xecfg;
-       xdemitcb_t ecb;
-
-       xpp.flags = xdl_opts;
-       memset(&xecfg, 0, sizeof(xecfg));
-       xecfg.ctxlen = context;
-       memset(&state, 0, sizeof(state));
-       state.ret = xmalloc(sizeof(struct patch));
-       state.ret->chunks = NULL;
-       state.ret->num = 0;
-
-       xdi_diff_outf(file_p, file_o, process_u_diff, &state, &xpp, &xecfg, &ecb);
-
-       if (state.ret->num) {
-               struct chunk *chunk;
-               chunk = &state.ret->chunks[state.ret->num - 1];
-               chunk->p_next -= state.hunk_post_context;
-               chunk->t_next -= state.hunk_post_context;
-       }
-       return state.ret;
-}
-
-/*
- * Run diff between two origins and grab the patch output, so that
- * we can pass blame for lines origin is currently suspected for
- * to its parent.
- */
-static struct patch *get_patch(struct origin *parent, struct origin *origin)
-{
-       mmfile_t file_p, file_o;
-       struct patch *patch;
-
-       fill_origin_blob(parent, &file_p);
-       fill_origin_blob(origin, &file_o);
-       if (!file_p.ptr || !file_o.ptr)
-               return NULL;
-       patch = compare_buffer(&file_p, &file_o, 0);
-       num_get_patch++;
-       return patch;
-}
-
-static void free_patch(struct patch *p)
-{
-       free(p->chunks);
-       free(p);
-}
-
 /*
  * Link in a new blame entry to the scoreboard.  Entries that cover the
  * same line range have been removed from the scoreboard previously.
@@ -813,6 +688,22 @@ static void blame_chunk(struct scoreboard *sb,
        }
 }
 
+struct blame_chunk_cb_data {
+       struct scoreboard *sb;
+       struct origin *target;
+       struct origin *parent;
+       long plno;
+       long tlno;
+};
+
+static void blame_chunk_cb(void *data, long same, long p_next, long t_next)
+{
+       struct blame_chunk_cb_data *d = data;
+       blame_chunk(d->sb, d->tlno, d->plno, same, d->target, d->parent);
+       d->plno = p_next;
+       d->tlno = t_next;
+}
+
 /*
  * We are looking at the origin 'target' and aiming to pass blame
  * for the lines it is suspected to its parent.  Run diff to find
@@ -822,26 +713,28 @@ static int pass_blame_to_parent(struct scoreboard *sb,
                                struct origin *target,
                                struct origin *parent)
 {
-       int i, last_in_target, plno, tlno;
-       struct patch *patch;
+       int last_in_target;
+       mmfile_t file_p, file_o;
+       struct blame_chunk_cb_data d = { sb, target, parent, 0, 0 };
+       xpparam_t xpp;
+       xdemitconf_t xecfg;
 
        last_in_target = find_last_in_target(sb, target);
        if (last_in_target < 0)
                return 1; /* nothing remains for this target */
 
-       patch = get_patch(parent, target);
-       plno = tlno = 0;
-       for (i = 0; i < patch->num; i++) {
-               struct chunk *chunk = &patch->chunks[i];
+       fill_origin_blob(parent, &file_p);
+       fill_origin_blob(target, &file_o);
+       num_get_patch++;
 
-               blame_chunk(sb, tlno, plno, chunk->same, target, parent);
-               plno = chunk->p_next;
-               tlno = chunk->t_next;
-       }
+       memset(&xpp, 0, sizeof(xpp));
+       xpp.flags = xdl_opts;
+       memset(&xecfg, 0, sizeof(xecfg));
+       xecfg.ctxlen = 0;
+       xdi_diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, &xpp, &xecfg);
        /* The rest (i.e. anything after tlno) are the same as the parent */
-       blame_chunk(sb, tlno, plno, last_in_target, target, parent);
+       blame_chunk(sb, d.tlno, d.plno, last_in_target, target, parent);
 
-       free_patch(patch);
        return 0;
 }
 
@@ -933,6 +826,23 @@ static void handle_split(struct scoreboard *sb,
        }
 }
 
+struct handle_split_cb_data {
+       struct scoreboard *sb;
+       struct blame_entry *ent;
+       struct origin *parent;
+       struct blame_entry *split;
+       long plno;
+       long tlno;
+};
+
+static void handle_split_cb(void *data, long same, long p_next, long t_next)
+{
+       struct handle_split_cb_data *d = data;
+       handle_split(d->sb, d->ent, d->tlno, d->plno, same, d->parent, d->split);
+       d->plno = p_next;
+       d->tlno = t_next;
+}
+
 /*
  * Find the lines from parent that are the same as ent so that
  * we can pass blames to it.  file_p has the blob contents for
@@ -947,8 +857,9 @@ static void find_copy_in_blob(struct scoreboard *sb,
        const char *cp;
        int cnt;
        mmfile_t file_o;
-       struct patch *patch;
-       int i, plno, tlno;
+       struct handle_split_cb_data d = { sb, ent, parent, split, 0, 0 };
+       xpparam_t xpp;
+       xdemitconf_t xecfg;
 
        /*
         * Prepare mmfile that contains only the lines in ent.
@@ -963,24 +874,18 @@ static void find_copy_in_blob(struct scoreboard *sb,
        }
        file_o.size = cp - file_o.ptr;
 
-       patch = compare_buffer(file_p, &file_o, 1);
-
        /*
         * file_o is a part of final image we are annotating.
         * file_p partially may match that image.
         */
+       memset(&xpp, 0, sizeof(xpp));
+       xpp.flags = xdl_opts;
+       memset(&xecfg, 0, sizeof(xecfg));
+       xecfg.ctxlen = 1;
        memset(split, 0, sizeof(struct blame_entry [3]));
-       plno = tlno = 0;
-       for (i = 0; i < patch->num; i++) {
-               struct chunk *chunk = &patch->chunks[i];
-
-               handle_split(sb, ent, tlno, plno, chunk->same, parent, split);
-               plno = chunk->p_next;
-               tlno = chunk->t_next;
-       }
+       xdi_diff_hunks(file_p, &file_o, handle_split_cb, &d, &xpp, &xecfg);
        /* remainder, if any, all match the preimage */
-       handle_split(sb, ent, tlno, plno, ent->num_lines, parent, split);
-       free_patch(patch);
+       handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
 }
 
 /*
@@ -1431,7 +1336,7 @@ static void get_commit_info(struct commit *commit,
                            int detailed)
 {
        int len;
-       char *tmp, *endp;
+       char *tmp, *endp, *reencoded, *message;
        static char author_buf[1024];
        static char committer_buf[1024];
        static char summary_buf[1024];
@@ -1449,24 +1354,29 @@ static void get_commit_info(struct commit *commit,
                        die("Cannot read commit %s",
                            sha1_to_hex(commit->object.sha1));
        }
+       reencoded = reencode_commit_message(commit, NULL);
+       message   = reencoded ? reencoded : commit->buffer;
        ret->author = author_buf;
-       get_ac_line(commit->buffer, "\nauthor ",
+       get_ac_line(message, "\nauthor ",
                    sizeof(author_buf), author_buf, &ret->author_mail,
                    &ret->author_time, &ret->author_tz);
 
-       if (!detailed)
+       if (!detailed) {
+               free(reencoded);
                return;
+       }
 
        ret->committer = committer_buf;
-       get_ac_line(commit->buffer, "\ncommitter ",
+       get_ac_line(message, "\ncommitter ",
                    sizeof(committer_buf), committer_buf, &ret->committer_mail,
                    &ret->committer_time, &ret->committer_tz);
 
        ret->summary = summary_buf;
-       tmp = strstr(commit->buffer, "\n\n");
+       tmp = strstr(message, "\n\n");
        if (!tmp) {
        error_out:
                sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
+               free(reencoded);
                return;
        }
        tmp += 2;
@@ -1478,6 +1388,7 @@ static void get_commit_info(struct commit *commit,
                goto error_out;
        memcpy(summary_buf, tmp, len);
        summary_buf[len] = 0;
+       free(reencoded);
 }
 
 /*
index 8d634ff571ce34ce21a5519628d6f66a7e52aa93..2b3613fea2d7799b7d20e9e30da20527d811ed64 100644 (file)
@@ -160,7 +160,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                        continue;
                }
 
-               if (delete_ref(name, sha1)) {
+               if (delete_ref(name, sha1, 0)) {
                        error("Error deleting %sbranch '%s'", remote,
                               argv[i]);
                        ret = 1;
index cb783fc77e75515a02ed2268dfb37ee3bbd03749..15a04b7179a09492764d43c16a3ec5ff7cdd1b61 100644 (file)
@@ -2,21 +2,84 @@
 #include "cache.h"
 #include "attr.h"
 #include "quote.h"
+#include "parse-options.h"
 
-static const char check_attr_usage[] =
-"git check-attr attr... [--] pathname...";
+static int stdin_paths;
+static const char * const check_attr_usage[] = {
+"git check-attr attr... [--] pathname...",
+"git check-attr --stdin attr... < <list-of-paths>",
+NULL
+};
+
+static int null_term_line;
+
+static const struct option check_attr_options[] = {
+       OPT_BOOLEAN(0 , "stdin", &stdin_paths, "read file names from stdin"),
+       OPT_BOOLEAN('z', NULL, &null_term_line,
+               "input paths are terminated by a null character"),
+       OPT_END()
+};
+
+static void check_attr(int cnt, struct git_attr_check *check,
+       const char** name, const char *file)
+{
+       int j;
+       if (git_checkattr(file, cnt, check))
+               die("git_checkattr died");
+       for (j = 0; j < cnt; j++) {
+               const char *value = check[j].value;
+
+               if (ATTR_TRUE(value))
+                       value = "set";
+               else if (ATTR_FALSE(value))
+                       value = "unset";
+               else if (ATTR_UNSET(value))
+                       value = "unspecified";
+
+               quote_c_style(file, NULL, stdout, 0);
+               printf(": %s: %s\n", name[j], value);
+       }
+}
+
+static void check_attr_stdin_paths(int cnt, struct git_attr_check *check,
+       const char** name)
+{
+       struct strbuf buf, nbuf;
+       int line_termination = null_term_line ? 0 : '\n';
+
+       strbuf_init(&buf, 0);
+       strbuf_init(&nbuf, 0);
+       while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
+               if (line_termination && buf.buf[0] == '"') {
+                       strbuf_reset(&nbuf);
+                       if (unquote_c_style(&nbuf, buf.buf, NULL))
+                               die("line is badly quoted");
+                       strbuf_swap(&buf, &nbuf);
+               }
+               check_attr(cnt, check, name, buf.buf);
+               maybe_flush_or_die(stdout, "attribute to stdout");
+       }
+       strbuf_release(&buf);
+       strbuf_release(&nbuf);
+}
 
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
        struct git_attr_check *check;
        int cnt, i, doubledash;
+       const char *errstr = NULL;
+
+       argc = parse_options(argc, argv, check_attr_options, check_attr_usage,
+               PARSE_OPT_KEEP_DASHDASH);
+       if (!argc)
+               usage_with_options(check_attr_usage, check_attr_options);
 
        if (read_cache() < 0) {
                die("invalid cache");
        }
 
        doubledash = -1;
-       for (i = 1; doubledash < 0 && i < argc; i++) {
+       for (i = 0; doubledash < 0 && i < argc; i++) {
                if (!strcmp(argv[i], "--"))
                        doubledash = i;
        }
@@ -24,41 +87,37 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
        /* If there is no double dash, we handle only one attribute */
        if (doubledash < 0) {
                cnt = 1;
-               doubledash = 1;
+               doubledash = 0;
        } else
-               cnt = doubledash - 1;
+               cnt = doubledash;
        doubledash++;
 
-       if (cnt <= 0 || argc < doubledash)
-               usage(check_attr_usage);
+       if (cnt <= 0)
+               errstr = "No attribute specified";
+       else if (stdin_paths && doubledash < argc)
+               errstr = "Can't specify files with --stdin";
+       if (errstr) {
+               error("%s", errstr);
+               usage_with_options(check_attr_usage, check_attr_options);
+       }
+
        check = xcalloc(cnt, sizeof(*check));
        for (i = 0; i < cnt; i++) {
                const char *name;
                struct git_attr *a;
-               name = argv[i + 1];
+               name = argv[i];
                a = git_attr(name, strlen(name));
                if (!a)
                        return error("%s: not a valid attribute name", name);
                check[i].attr = a;
        }
 
-       for (i = doubledash; i < argc; i++) {
-               int j;
-               if (git_checkattr(argv[i], cnt, check))
-                       die("git_checkattr died");
-               for (j = 0; j < cnt; j++) {
-                       const char *value = check[j].value;
-
-                       if (ATTR_TRUE(value))
-                               value = "set";
-                       else if (ATTR_FALSE(value))
-                               value = "unset";
-                       else if (ATTR_UNSET(value))
-                               value = "unspecified";
-
-                       quote_c_style(argv[i], NULL, stdout, 0);
-                       printf(": %s: %s\n", argv[j+1], value);
-               }
+       if (stdin_paths)
+               check_attr_stdin_paths(cnt, check, argv);
+       else {
+               for (i = doubledash; i < argc; i++)
+                       check_attr(cnt, check, argv, argv[i]);
+               maybe_flush_or_die(stdout, "attribute to stdout");
        }
        return 0;
 }
index 4ba27024c53ca1b67e9b6d6f4a62aa7548b7b96d..0d534bc023a1a3367bdf19f6450ce17e627959f0 100644 (file)
@@ -40,6 +40,7 @@
 #include "cache.h"
 #include "quote.h"
 #include "cache-tree.h"
+#include "parse-options.h"
 
 #define CHECKOUT_ALL 4
 static int line_termination = '\n';
@@ -153,11 +154,58 @@ static void checkout_all(const char *prefix, int prefix_length)
                exit(128);
 }
 
-static const char checkout_cache_usage[] =
-"git checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]|all] [--prefix=<string>] [--temp] [--] <file>...";
+static const char * const builtin_checkout_index_usage[] = {
+       "git checkout-index [options] [--] <file>...",
+       NULL
+};
 
 static struct lock_file lock_file;
 
+static int option_parse_u(const struct option *opt,
+                             const char *arg, int unset)
+{
+       int *newfd = opt->value;
+
+       state.refresh_cache = 1;
+       if (*newfd < 0)
+               *newfd = hold_locked_index(&lock_file, 1);
+       return 0;
+}
+
+static int option_parse_z(const struct option *opt,
+                         const char *arg, int unset)
+{
+       if (unset)
+               line_termination = '\n';
+       else
+               line_termination = 0;
+       return 0;
+}
+
+static int option_parse_prefix(const struct option *opt,
+                              const char *arg, int unset)
+{
+       state.base_dir = arg;
+       state.base_dir_len = strlen(arg);
+       return 0;
+}
+
+static int option_parse_stage(const struct option *opt,
+                             const char *arg, int unset)
+{
+       if (!strcmp(arg, "all")) {
+               to_tempfile = 1;
+               checkout_stage = CHECKOUT_ALL;
+       } else {
+               int ch = arg[0];
+               if ('1' <= ch && ch <= '3')
+                       checkout_stage = arg[0] - '0';
+               else
+                       die("stage should be between 1 and 3 or all");
+       }
+       return 0;
+}
+
 int cmd_checkout_index(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -165,6 +213,33 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        int all = 0;
        int read_from_stdin = 0;
        int prefix_length;
+       int force = 0, quiet = 0, not_new = 0;
+       struct option builtin_checkout_index_options[] = {
+               OPT_BOOLEAN('a', "all", &all,
+                       "checks out all files in the index"),
+               OPT_BOOLEAN('f', "force", &force,
+                       "forces overwrite of existing files"),
+               OPT__QUIET(&quiet),
+               OPT_BOOLEAN('n', "no-create", &not_new,
+                       "don't checkout new files"),
+               { OPTION_CALLBACK, 'u', "index", &newfd, NULL,
+                       "update stat information in the index file",
+                       PARSE_OPT_NOARG, option_parse_u },
+               { OPTION_CALLBACK, 'z', NULL, NULL, NULL,
+                       "paths are separated with NUL character",
+                       PARSE_OPT_NOARG, option_parse_z },
+               OPT_BOOLEAN(0, "stdin", &read_from_stdin,
+                       "read list of paths from the standard input"),
+               OPT_BOOLEAN(0, "temp", &to_tempfile,
+                       "write the content to temporary files"),
+               OPT_CALLBACK(0, "prefix", NULL, "string",
+                       "when creating files, prepend <string>",
+                       option_parse_prefix),
+               OPT_CALLBACK(0, "stage", NULL, NULL,
+                       "copy out the files from named stage",
+                       option_parse_stage),
+               OPT_END()
+       };
 
        git_config(git_default_config, NULL);
        state.base_dir = "";
@@ -174,72 +249,11 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
                die("invalid cache");
        }
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-
-               if (!strcmp(arg, "--")) {
-                       i++;
-                       break;
-               }
-               if (!strcmp(arg, "-a") || !strcmp(arg, "--all")) {
-                       all = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-f") || !strcmp(arg, "--force")) {
-                       state.force = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
-                       state.quiet = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-n") || !strcmp(arg, "--no-create")) {
-                       state.not_new = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-u") || !strcmp(arg, "--index")) {
-                       state.refresh_cache = 1;
-                       if (newfd < 0)
-                               newfd = hold_locked_index(&lock_file, 1);
-                       continue;
-               }
-               if (!strcmp(arg, "-z")) {
-                       line_termination = 0;
-                       continue;
-               }
-               if (!strcmp(arg, "--stdin")) {
-                       if (i != argc - 1)
-                               die("--stdin must be at the end");
-                       read_from_stdin = 1;
-                       i++; /* do not consider arg as a file name */
-                       break;
-               }
-               if (!strcmp(arg, "--temp")) {
-                       to_tempfile = 1;
-                       continue;
-               }
-               if (!prefixcmp(arg, "--prefix=")) {
-                       state.base_dir = arg+9;
-                       state.base_dir_len = strlen(state.base_dir);
-                       continue;
-               }
-               if (!prefixcmp(arg, "--stage=")) {
-                       if (!strcmp(arg + 8, "all")) {
-                               to_tempfile = 1;
-                               checkout_stage = CHECKOUT_ALL;
-                       } else {
-                               int ch = arg[8];
-                               if ('1' <= ch && ch <= '3')
-                                       checkout_stage = arg[8] - '0';
-                               else
-                                       die("stage should be between 1 and 3 or all");
-                       }
-                       continue;
-               }
-               if (arg[0] == '-')
-                       usage(checkout_cache_usage);
-               break;
-       }
+       argc = parse_options(argc, argv, builtin_checkout_index_options,
+                       builtin_checkout_index_usage, 0);
+       state.force = force;
+       state.quiet = quiet;
+       state.not_new = not_new;
 
        if (state.base_dir_len || to_tempfile) {
                /* when --prefix is specified we do not
@@ -253,7 +267,7 @@ int cmd_checkout_index(int argc, const char **argv, const char *prefix)
        }
 
        /* Check out named files first */
-       for ( ; i < argc; i++) {
+       for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                const char *p;
 
index ad04a184a28b7cdbb5278cda9c467a2e37b0add8..464fd2570735485a35d34c4aa8d50e5a1930228c 100644 (file)
@@ -47,7 +47,7 @@ static int post_checkout_hook(struct commit *old, struct commit *new,
 
        memset(&proc, 0, sizeof(proc));
        argv[0] = name;
-       argv[1] = xstrdup(sha1_to_hex(old->object.sha1));
+       argv[1] = xstrdup(sha1_to_hex(old ? old->object.sha1 : null_sha1));
        argv[2] = xstrdup(sha1_to_hex(new->object.sha1));
        argv[3] = changed ? "1" : "0";
        argv[4] = NULL;
@@ -397,8 +397,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                }
 
                /* 2-way merge to the new branch */
-               topts.initial_checkout = (!active_nr &&
-                                         (old->commit == new->commit));
+               topts.initial_checkout = is_cache_unborn();
                topts.update = 1;
                topts.merge = 1;
                topts.gently = opts->merge;
@@ -492,10 +491,10 @@ static void update_refs_for_switch(struct checkout_opts *opts,
        }
 
        old_desc = old->name;
-       if (!old_desc)
+       if (!old_desc && old->commit)
                old_desc = sha1_to_hex(old->commit->object.sha1);
        strbuf_addf(&msg, "checkout: moving from %s to %s",
-                   old_desc, new->name);
+                   old_desc ? old_desc : "(invalid)", new->name);
 
        if (new->path) {
                create_symref("HEAD", new->path, msg.buf);
@@ -551,7 +550,7 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
         * a new commit, we want to mention the old commit once more
         * to remind the user that it might be lost.
         */
-       if (!opts->quiet && !old.path && new->commit != old.commit)
+       if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
                describe_detached_head("Previous HEAD position was", old.commit);
 
        if (!old.commit) {
@@ -635,9 +634,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
        }
 
-       if (!opts.new_branch && (opts.track != git_branch_track))
-               die("git checkout: --track and --no-track require -b");
-
        if (opts.force && opts.merge)
                die("git checkout: -f and -m are incompatible");
 
index 1ddc14b9c70b3318ab5e1cad779c8af029e56ce3..8e1a1d399580af3faa39c3c50350b3ca19df0634 100644 (file)
@@ -38,9 +38,11 @@ static int option_local, option_no_hardlinks, option_shared;
 static char *option_template, *option_reference, *option_depth;
 static char *option_origin = NULL;
 static char *option_upload_pack = "git-upload-pack";
+static int option_verbose;
 
 static struct option builtin_clone_options[] = {
        OPT__QUIET(&option_quiet),
+       OPT__VERBOSE(&option_verbose),
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
                    "don't create a checkout"),
        OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
@@ -504,6 +506,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
                if (option_quiet)
                        transport->verbose = -1;
+               else if (option_verbose)
+                       transport->progress = 1;
 
                if (option_upload_pack)
                        transport_set_option(transport, TRANS_OPT_UPLOADPACK,
index a2755dc3befeb5ed768f692ee58e5147ad933e26..591d16b91eab09d89fefecbd058e4bca78439455 100644 (file)
@@ -320,7 +320,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
                die("unable to write new_index file");
 
        fd = hold_lock_file_for_update(&false_lock,
-                                      git_path("next-index-%"PRIuMAX, (uintmax_t) getpid()), 1);
+                                      git_path("next-index-%"PRIuMAX,
+                                               (uintmax_t) getpid()),
+                                      LOCK_DIE_ON_ERROR);
 
        create_base_index();
        add_remove_files(&partial);
@@ -879,6 +881,9 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 {
        struct rev_info rev;
        struct commit *commit;
+       static const char *format = "format:%h: \"%s\"";
+       unsigned char junk_sha1[20];
+       const char *head = resolve_ref("HEAD", junk_sha1, 0, NULL);
 
        commit = lookup_commit(sha1);
        if (!commit)
@@ -896,18 +901,24 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
 
        rev.verbose_header = 1;
        rev.show_root_diff = 1;
-       get_commit_format("format:%h: %s", &rev);
+       get_commit_format(format, &rev);
        rev.always_show_header = 0;
        rev.diffopt.detect_rename = 1;
        rev.diffopt.rename_limit = 100;
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
 
-       printf("Created %scommit ", initial_commit ? "initial " : "");
+       printf("[%s%s]: created ",
+               !prefixcmp(head, "refs/heads/") ?
+                       head + 11 :
+                       !strcmp(head, "HEAD") ?
+                               "detached HEAD" :
+                               head,
+               initial_commit ? " (root-commit)" : "");
 
        if (!log_tree_commit(&rev, commit)) {
                struct strbuf buf = STRBUF_INIT;
-               format_commit_message(commit, "%h: %s", &buf, DATE_NORMAL);
+               format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
                printf("%s\n", buf.buf);
                strbuf_release(&buf);
        }
@@ -934,6 +945,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        unsigned char commit_sha1[20];
        struct ref_lock *ref_lock;
        struct commit_list *parents = NULL, **pptr = &parents;
+       struct stat statbuf;
+       int allow_fast_forward = 1;
 
        git_config(git_commit_config, NULL);
 
@@ -980,22 +993,33 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                }
                fclose(fp);
                strbuf_release(&m);
+               if (!stat(git_path("MERGE_MODE"), &statbuf)) {
+                       if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
+                               die("could not read MERGE_MODE: %s",
+                                               strerror(errno));
+                       if (!strcmp(sb.buf, "no-ff"))
+                               allow_fast_forward = 0;
+               }
+               if (allow_fast_forward)
+                       parents = reduce_heads(parents);
        } else {
                reflog_msg = "commit";
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
        }
-       parents = reduce_heads(parents);
 
        /* Finally, get the commit message */
+       strbuf_reset(&sb);
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
                rollback_index_files();
                die("could not read commit message");
        }
 
        /* Truncate the message just before the diff, if any. */
-       p = strstr(sb.buf, "\ndiff --git a/");
-       if (p != NULL)
-               strbuf_setlen(&sb, p - sb.buf + 1);
+       if (verbose) {
+               p = strstr(sb.buf, "\ndiff --git ");
+               if (p != NULL)
+                       strbuf_setlen(&sb, p - sb.buf + 1);
+       }
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, cleanup_mode == CLEANUP_ALL);
@@ -1035,6 +1059,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
+       unlink(git_path("MERGE_MODE"));
        unlink(git_path("SQUASH_MSG"));
 
        if (commit_index_files())
index 91fdc4985d8e64fae12209174dd4aa2d887793e5..f71016204b540d0d935323c909a0ffccb1abdbe2 100644 (file)
@@ -84,7 +84,7 @@ static int get_value(const char* key_, const char* regex_)
        local = config_exclusive_filename;
        if (!local) {
                const char *home = getenv("HOME");
-               local = repo_config = xstrdup(git_path("config"));
+               local = repo_config = git_pathdup("config");
                if (git_config_global() && home)
                        global = xstrdup(mkpath("%s/.gitconfig", home));
                if (git_config_system())
index ec404c839b6542deb4e15ca342fd3c0afbbedd2e..d2cfb1b0837ffd1ab3b1f69e8dee0ffa36f2e1ab 100644 (file)
@@ -15,8 +15,8 @@ static const char * const describe_usage[] = {
 };
 
 static int debug;      /* Display lots of verbose info */
-static int all;        /* Default to annotated tags only */
-static int tags;       /* But allow any tags if --tags is specified */
+static int all;        /* Any valid ref can be used */
+static int tags;       /* Allow lightweight tags */
 static int longformat;
 static int abbrev = DEFAULT_ABBREV;
 static int max_candidates = 10;
@@ -112,8 +112,6 @@ static int compare_pt(const void *a_, const void *b_)
 {
        struct possible_tag *a = (struct possible_tag *)a_;
        struct possible_tag *b = (struct possible_tag *)b_;
-       if (a->name->prio != b->name->prio)
-               return b->name->prio - a->name->prio;
        if (a->depth != b->depth)
                return a->depth - b->depth;
        if (a->found_order != b->found_order)
index 9c8c295732bf12990b8324bf75968fd68dd41d59..7ceceeb6ff49d60468293941b5f37141e8fc01b9 100644 (file)
@@ -118,7 +118,7 @@ static int builtin_diff_index(struct rev_info *revs,
        int cached = 0;
        while (1 < argc) {
                const char *arg = argv[1];
-               if (!strcmp(arg, "--cached"))
+               if (!strcmp(arg, "--cached") || !strcmp(arg, "--staged"))
                        cached = 1;
                else
                        usage(builtin_diff_usage);
@@ -300,6 +300,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
        }
        DIFF_OPT_SET(&rev.diffopt, ALLOW_EXTERNAL);
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
+       DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
 
        /*
         * If the user asked for our exit code then don't start a
@@ -319,7 +320,8 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                        const char *arg = argv[i];
                        if (!strcmp(arg, "--"))
                                break;
-                       else if (!strcmp(arg, "--cached")) {
+                       else if (!strcmp(arg, "--cached") ||
+                                !strcmp(arg, "--staged")) {
                                add_head_to_pending(&rev);
                                if (!rev.pending.nr)
                                        die("No HEAD commit to compare with (yet)");
index fa3c936493cc0b139edf3e4e8154569f453afc02..372bfa20a2eac978f7511f5d8d9296be789b527f 100644 (file)
@@ -813,7 +813,8 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
                          )
                        die("shallow file was changed during fetch");
 
-               fd = hold_lock_file_for_update(&lock, shallow, 1);
+               fd = hold_lock_file_for_update(&lock, shallow,
+                                              LOCK_DIE_ON_ERROR);
                if (!write_shallow_commits(fd, 0)) {
                        unlink(shallow);
                        rollback_lock_file(&lock);
index ee93d3a93da0267caa485fa552c29d779aecfdf7..f151cfa2fd028dd37ad0cc3b6b35b2017417fffa 100644 (file)
@@ -521,8 +521,8 @@ static void find_non_local_tags(struct transport *transport,
                     will_fetch(head, ref->old_sha1))) {
                        string_list_insert(ref_name, &new_refs);
 
-                       rm = alloc_ref_from_str(ref_name);
-                       rm->peer_ref = alloc_ref_from_str(ref_name);
+                       rm = alloc_ref(ref_name);
+                       rm->peer_ref = alloc_ref(ref_name);
                        hashcpy(rm->old_sha1, ref_sha1);
 
                        **tail = rm;
@@ -534,6 +534,19 @@ static void find_non_local_tags(struct transport *transport,
        string_list_clear(&new_refs, 0);
 }
 
+static void check_not_current_branch(struct ref *ref_map)
+{
+       struct branch *current_branch = branch_get(NULL);
+
+       if (is_bare_repository() || !current_branch)
+               return;
+
+       for (; ref_map; ref_map = ref_map->next)
+               if (ref_map->peer_ref && !strcmp(current_branch->refname,
+                                       ref_map->peer_ref->name))
+                       die("Refusing to fetch into current branch");
+}
+
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
@@ -558,6 +571,8 @@ static int do_fetch(struct transport *transport,
        }
 
        ref_map = get_ref_map(transport, refs, ref_count, tags, &autotags);
+       if (!update_head_ok)
+               check_not_current_branch(ref_map);
 
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref)
index fa6c1ed75285649943e0e62ee0b66ef146dda517..e46b7adc9719e147536398e8e365d6d3e65a4ba7 100644 (file)
@@ -620,14 +620,16 @@ static char *get_short_ref(struct refinfo *ref)
                for (j = 0; j < i; j++) {
                        const char *rule = ref_rev_parse_rules[j];
                        unsigned char short_objectname[20];
+                       char refname[PATH_MAX];
 
                        /*
                         * the short name is ambiguous, if it resolves
                         * (with this previous rule) to a valid ref
                         * read_ref() returns 0 on success
                         */
-                       if (!read_ref(mkpath(rule, short_name_len, short_name),
-                                     short_objectname))
+                       mksnpath(refname, sizeof(refname),
+                                rule, short_name_len, short_name);
+                       if (!read_ref(refname, short_objectname))
                                break;
                }
 
index 7af65bb31bfbf3e9a0e47901d44168389d736d20..781df601c5f95e874297e0e8bec3fddeb929cc2d 100644 (file)
@@ -131,19 +131,9 @@ static int too_many_packs(void)
 
        prepare_packed_git();
        for (cnt = 0, p = packed_git; p; p = p->next) {
-               char path[PATH_MAX];
-               size_t len;
-               int keep;
-
                if (!p->pack_local)
                        continue;
-               len = strlen(p->pack_name);
-               if (PATH_MAX <= len + 1)
-                       continue; /* oops, give up */
-               memcpy(path, p->pack_name, len-5);
-               memcpy(path + len - 5, ".keep", 6);
-               keep = access(p->pack_name, F_OK) && (errno == ENOENT);
-               if (keep)
+               if (p->pack_keep)
                        continue;
                /*
                 * Perhaps check the size of the pack and count only
index 794821f6bca6500cbb040403c1350932ae7126fb..b164717379627b7bfc303bd3fdf360ecd2db152d 100644 (file)
@@ -28,7 +28,6 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                      struct rev_info *rev)
 {
        int i;
-       int decorate = 0;
 
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
@@ -55,10 +54,13 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                const char *arg = argv[i];
                if (!strcmp(arg, "--decorate")) {
                        load_ref_decorations();
-                       decorate = 1;
+                       rev->show_decorations = 1;
+               } else if (!strcmp(arg, "--source")) {
+                       rev->show_source = 1;
                } else
                        die("unrecognized argument: %s", arg);
        }
+       DIFF_OPT_SET(&rev->diffopt, ALLOW_TEXTCONV);
 }
 
 /*
@@ -426,7 +428,7 @@ static int istitlechar(char c)
 
 static const char *fmt_patch_suffix = ".patch";
 static int numbered = 0;
-static int auto_number = 0;
+static int auto_number = 1;
 
 static char **extra_hdr;
 static int extra_hdr_nr;
@@ -485,6 +487,7 @@ static int git_format_config(const char *var, const char *value, void *cb)
                        return 0;
                }
                numbered = git_config_bool(var, value);
+               auto_number = auto_number && numbered;
                return 0;
        }
 
index 068f424696864f3db1c1b78ef25a92a197ff5474..b48327db950e5d8a54045f4956fa92b7879227bc 100644 (file)
@@ -91,39 +91,10 @@ static void show_other_files(struct dir_struct *dir)
 {
        int i;
 
-
-       /*
-        * Skip matching and unmerged entries for the paths,
-        * since we want just "others".
-        *
-        * (Matching entries are normally pruned during
-        * the directory tree walk, but will show up for
-        * gitlinks because we don't necessarily have
-        * dir->show_other_directories set to suppress
-        * them).
-        */
        for (i = 0; i < dir->nr; i++) {
                struct dir_entry *ent = dir->entries[i];
-               int len, pos;
-               struct cache_entry *ce;
-
-               /*
-                * Remove the '/' at the end that directory
-                * walking adds for directory entries.
-                */
-               len = ent->len;
-               if (len && ent->name[len-1] == '/')
-                       len--;
-               pos = cache_name_pos(ent->name, len);
-               if (0 <= pos)
-                       continue;       /* exact match */
-               pos = -pos - 1;
-               if (pos < active_nr) {
-                       ce = active_cache[pos];
-                       if (ce_namelen(ce) == len &&
-                           !memcmp(ce->name, ent->name, len))
-                               continue; /* Yup, this one exists unmerged */
-               }
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
                show_dir_entry(tag_other, ent);
        }
 }
index c21b841e7c5e8d27a6e66e7f70786d77aa4653b5..78a88f74769645f0be86aa77d3dee3f5e99c916f 100644 (file)
@@ -4,7 +4,7 @@
 #include "remote.h"
 
 static const char ls_remote_usage[] =
-"git ls-remote [--upload-pack=<git-upload-pack>] [<host>:]<directory>";
+"git ls-remote [--heads] [--tags]  [-u <exec> | --upload-pack <exec>] <repository> <refs>...";
 
 /*
  * Is there one among the list of patterns that match the tail part
index 5e2b7f12c35ab1bfebda66f7ee9180c68e7f23c4..5e7910bd8da0a603dd82e7502c28a603bf80fa82 100644 (file)
@@ -179,6 +179,7 @@ static void drop_save(void)
 {
        unlink(git_path("MERGE_HEAD"));
        unlink(git_path("MERGE_MSG"));
+       unlink(git_path("MERGE_MODE"));
 }
 
 static void save_state(void)
@@ -1210,6 +1211,15 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        merge_msg.len)
                        die("Could not write to %s", git_path("MERGE_MSG"));
                close(fd);
+               fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
+               if (fd < 0)
+                       die("Could open %s for writing", git_path("MERGE_MODE"));
+               strbuf_reset(&buf);
+               if (!allow_fast_forward)
+                       strbuf_addf(&buf, "no-ff");
+               if (write_in_full(fd, buf.buf, buf.len) != buf.len)
+                       die("Could not write to %s", git_path("MERGE_MODE"));
+               close(fd);
        }
 
        if (merge_was_ok) {
index 59c30d1caa37416041177ff4aaf01b67f4e8add4..67eefa2932717bfd4a5daa8ad3a4379af1ef78db 100644 (file)
@@ -71,6 +71,7 @@ static int reuse_delta = 1, reuse_object = 1;
 static int keep_unreachable, unpack_unreachable, include_tag;
 static int local;
 static int incremental;
+static int ignore_packed_keep;
 static int allow_ofs_delta;
 static const char *base_name;
 static int progress = 1;
@@ -245,8 +246,16 @@ static unsigned long write_object(struct sha1file *f,
        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 (!pack_size_limit || !nr_written)
+               limit = 0;
+       else if (pack_size_limit <= write_offset)
+               /*
+                * the earlier object did not fit the limit; avoid
+                * mistaking this with unlimited (i.e. limit = 0).
+                */
+               limit = 1;
+       else
+               limit = pack_size_limit - write_offset;
 
        if (!entry->delta)
                usable_delta = 0;       /* no delta */
@@ -277,6 +286,7 @@ static unsigned long write_object(struct sha1file *f,
                                 */
 
        if (!to_reuse) {
+               no_reuse:
                if (!usable_delta) {
                        buf = read_sha1_file(entry->idx.sha1, &type, &size);
                        if (!buf)
@@ -358,46 +368,60 @@ static unsigned long write_object(struct sha1file *f,
                struct revindex_entry *revidx;
                off_t offset;
 
-               if (entry->delta) {
+               if (entry->delta)
                        type = (allow_ofs_delta && entry->delta->idx.offset) ?
                                OBJ_OFS_DELTA : OBJ_REF_DELTA;
-                       reused_delta++;
-               }
                hdrlen = encode_header(type, entry->size, header);
+
                offset = entry->in_pack_offset;
                revidx = find_pack_revindex(p, offset);
                datalen = revidx[1].offset - offset;
                if (!pack_to_stdout && p->index_version > 1 &&
-                   check_pack_crc(p, &w_curs, offset, datalen, revidx->nr))
-                       die("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+                   check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
+                       error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+                       unuse_pack(&w_curs);
+                       goto no_reuse;
+               }
+
                offset += entry->in_pack_header_size;
                datalen -= entry->in_pack_header_size;
+               if (!pack_to_stdout && p->index_version == 1 &&
+                   check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
+                       error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
+                       unuse_pack(&w_curs);
+                       goto no_reuse;
+               }
+
                if (type == OBJ_OFS_DELTA) {
                        off_t ofs = entry->idx.offset - entry->delta->idx.offset;
                        unsigned pos = sizeof(dheader) - 1;
                        dheader[pos] = ofs & 127;
                        while (ofs >>= 7)
                                dheader[--pos] = 128 | (--ofs & 127);
-                       if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit)
+                       if (limit && hdrlen + sizeof(dheader) - pos + datalen + 20 >= limit) {
+                               unuse_pack(&w_curs);
                                return 0;
+                       }
                        sha1write(f, header, hdrlen);
                        sha1write(f, dheader + pos, sizeof(dheader) - pos);
                        hdrlen += sizeof(dheader) - pos;
+                       reused_delta++;
                } else if (type == OBJ_REF_DELTA) {
-                       if (limit && hdrlen + 20 + datalen + 20 >= limit)
+                       if (limit && hdrlen + 20 + datalen + 20 >= limit) {
+                               unuse_pack(&w_curs);
                                return 0;
+                       }
                        sha1write(f, header, hdrlen);
                        sha1write(f, entry->delta->idx.sha1, 20);
                        hdrlen += 20;
+                       reused_delta++;
                } else {
-                       if (limit && hdrlen + datalen + 20 >= limit)
+                       if (limit && hdrlen + datalen + 20 >= limit) {
+                               unuse_pack(&w_curs);
                                return 0;
+                       }
                        sha1write(f, header, hdrlen);
                }
-
-               if (!pack_to_stdout && p->index_version == 1 &&
-                   check_pack_inflate(p, &w_curs, offset, datalen, entry->size))
-                       die("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
                copy_pack_data(f, p, &w_curs, offset, datalen);
                unuse_pack(&w_curs);
                reused++;
@@ -690,6 +714,9 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
                return 0;
        }
 
+       if (!exclude && local && has_loose_object_nonlocal(sha1))
+               return 0;
+
        for (p = packed_git; p; p = p->next) {
                off_t offset = find_pack_entry_one(sha1, p);
                if (offset) {
@@ -703,6 +730,8 @@ static int add_object_entry(const unsigned char *sha1, enum object_type type,
                                return 0;
                        if (local && !p->pack_local)
                                return 0;
+                       if (ignore_packed_keep && p->pack_local && p->pack_keep)
+                               return 0;
                }
        }
 
@@ -1002,9 +1031,11 @@ static void check_object(struct object_entry *entry)
                 * We want in_pack_type even if we do not reuse delta
                 * since non-delta representations could still be reused.
                 */
-               used = unpack_object_header_gently(buf, avail,
+               used = unpack_object_header_buffer(buf, avail,
                                                   &entry->in_pack_type,
                                                   &entry->size);
+               if (used == 0)
+                       goto give_up;
 
                /*
                 * Determine if this is a delta and if so whether we can
@@ -1016,6 +1047,8 @@ static void check_object(struct object_entry *entry)
                        /* Not a delta hence we've already got all we need. */
                        entry->type = entry->in_pack_type;
                        entry->in_pack_header_size = used;
+                       if (entry->type < OBJ_COMMIT || entry->type > OBJ_BLOB)
+                               goto give_up;
                        unuse_pack(&w_curs);
                        return;
                case OBJ_REF_DELTA:
@@ -1032,19 +1065,25 @@ static void check_object(struct object_entry *entry)
                        ofs = c & 127;
                        while (c & 128) {
                                ofs += 1;
-                               if (!ofs || MSB(ofs, 7))
-                                       die("delta base offset overflow in pack for %s",
-                                           sha1_to_hex(entry->idx.sha1));
+                               if (!ofs || MSB(ofs, 7)) {
+                                       error("delta base offset overflow in pack for %s",
+                                             sha1_to_hex(entry->idx.sha1));
+                                       goto give_up;
+                               }
                                c = buf[used_0++];
                                ofs = (ofs << 7) + (c & 127);
                        }
-                       if (ofs >= entry->in_pack_offset)
-                               die("delta base offset out of bound for %s",
-                                   sha1_to_hex(entry->idx.sha1));
                        ofs = entry->in_pack_offset - ofs;
+                       if (ofs <= 0 || ofs >= entry->in_pack_offset) {
+                               error("delta base offset out of bound for %s",
+                                     sha1_to_hex(entry->idx.sha1));
+                               goto give_up;
+                       }
                        if (reuse_delta && !entry->preferred_base) {
                                struct revindex_entry *revidx;
                                revidx = find_pack_revindex(p, ofs);
+                               if (!revidx)
+                                       goto give_up;
                                base_ref = nth_packed_object_sha1(p, revidx->nr);
                        }
                        entry->in_pack_header_size = used + used_0;
@@ -1064,6 +1103,7 @@ static void check_object(struct object_entry *entry)
                         */
                        entry->type = entry->in_pack_type;
                        entry->delta = base_entry;
+                       entry->delta_size = entry->size;
                        entry->delta_sibling = base_entry->delta_child;
                        base_entry->delta_child = entry;
                        unuse_pack(&w_curs);
@@ -1078,6 +1118,8 @@ static void check_object(struct object_entry *entry)
                         */
                        entry->size = get_size_from_delta(p, &w_curs,
                                        entry->in_pack_offset + entry->in_pack_header_size);
+                       if (entry->size == 0)
+                               goto give_up;
                        unuse_pack(&w_curs);
                        return;
                }
@@ -1087,6 +1129,7 @@ static void check_object(struct object_entry *entry)
                 * with sha1_object_info() to find about the object type
                 * at this point...
                 */
+               give_up:
                unuse_pack(&w_curs);
        }
 
@@ -1375,7 +1418,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
        array = xcalloc(window, sizeof(struct unpacked));
 
        for (;;) {
-               struct object_entry *entry = *list++;
+               struct object_entry *entry;
                struct unpacked *n = array + idx;
                int j, max_depth, best_base = -1;
 
@@ -1384,6 +1427,7 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
                        progress_unlock();
                        break;
                }
+               entry = *list++;
                (*list_size)--;
                if (!entry->preferred_base) {
                        (*processed)++;
@@ -1697,6 +1741,16 @@ static void prepare_pack(int window, int depth)
 
        get_object_details();
 
+       /*
+        * If we're locally repacking then we need to be doubly careful
+        * from now on in order to make sure no stealth corruption gets
+        * propagated to the new pack.  Clients receiving streamed packs
+        * should validate everything they get anyway so no need to incur
+        * the additional cost here in that case.
+        */
+       if (!pack_to_stdout)
+               do_check_packed_object_crc = 1;
+
        if (!nr_objects || !window || !depth)
                return;
 
@@ -2047,6 +2101,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
                        incremental = 1;
                        continue;
                }
+               if (!strcmp("--honor-pack-keep", arg)) {
+                       ignore_packed_keep = 1;
+                       continue;
+               }
                if (!prefixcmp(arg, "--compression=")) {
                        char *end;
                        int level = strtoul(arg+14, &end, 0);
index 0706c958181c54aeb18d91f6e3dbe7c9f572b94d..38fef34d3fb6c24ab89043951f1ecf6c96e9bf51 100644 (file)
@@ -206,7 +206,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                        break;
                case 2:
                        opts.fn = twoway_merge;
-                       opts.initial_checkout = !active_nr;
+                       opts.initial_checkout = is_cache_unborn();
                        break;
                case 3:
                default:
index 45e3cd90fd476cdb0a32e5de27739b18e060e031..db67c3162c6b8ffaba793fd6082324aeae158f86 100644 (file)
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
+enum deny_action {
+       DENY_IGNORE,
+       DENY_WARN,
+       DENY_REFUSE,
+};
+
+static int deny_deletes = 0;
 static int deny_non_fast_forwards = 0;
+static enum deny_action deny_current_branch = DENY_WARN;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
 static int transfer_unpack_limit = -1;
@@ -21,8 +29,28 @@ static int report_status;
 static char capabilities[] = " report-status delete-refs ";
 static int capabilities_sent;
 
+static enum deny_action parse_deny_action(const char *var, const char *value)
+{
+       if (value) {
+               if (!strcasecmp(value, "ignore"))
+                       return DENY_IGNORE;
+               if (!strcasecmp(value, "warn"))
+                       return DENY_WARN;
+               if (!strcasecmp(value, "refuse"))
+                       return DENY_REFUSE;
+       }
+       if (git_config_bool(var, value))
+               return DENY_REFUSE;
+       return DENY_IGNORE;
+}
+
 static int receive_pack_config(const char *var, const char *value, void *cb)
 {
+       if (strcmp(var, "receive.denydeletes") == 0) {
+               deny_deletes = git_config_bool(var, value);
+               return 0;
+       }
+
        if (strcmp(var, "receive.denynonfastforwards") == 0) {
                deny_non_fast_forwards = git_config_bool(var, value);
                return 0;
@@ -43,6 +71,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp(var, "receive.denycurrentbranch")) {
+               deny_current_branch = parse_deny_action(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -167,6 +200,20 @@ static int run_update_hook(struct command *cmd)
        return hook_status(run_command(&proc), update_hook);
 }
 
+static int is_ref_checked_out(const char *ref)
+{
+       unsigned char sha1[20];
+       const char *head;
+
+       if (is_bare_repository())
+               return 0;
+
+       head = resolve_ref("HEAD", sha1, 0, NULL);
+       if (!head)
+               return 0;
+       return !strcmp(head, ref);
+}
+
 static const char *update(struct command *cmd)
 {
        const char *name = cmd->ref_name;
@@ -180,11 +227,35 @@ static const char *update(struct command *cmd)
                return "funny refname";
        }
 
+       switch (deny_current_branch) {
+       case DENY_IGNORE:
+               break;
+       case DENY_WARN:
+               if (!is_ref_checked_out(name))
+                       break;
+               warning("updating the currently checked out branch; this may"
+                       " cause confusion,\n"
+                       "as the index and working tree do not reflect changes"
+                       " that are now in HEAD.");
+               break;
+       case DENY_REFUSE:
+               if (!is_ref_checked_out(name))
+                       break;
+               error("refusing to update checked out branch: %s", name);
+               return "branch is currently checked out";
+       }
+
        if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
                error("unpack should have generated %s, "
                      "but I can't find it!", sha1_to_hex(new_sha1));
                return "bad pack";
        }
+       if (deny_deletes && is_null_sha1(new_sha1) &&
+           !is_null_sha1(old_sha1) &&
+           !prefixcmp(name, "refs/heads/")) {
+               error("denying ref deletion for %s", name);
+               return "deletion prohibited";
+       }
        if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
            !is_null_sha1(old_sha1) &&
            !prefixcmp(name, "refs/heads/")) {
@@ -224,7 +295,7 @@ static const char *update(struct command *cmd)
                        warning ("Allowing deletion of corrupt ref.");
                        old_sha1 = NULL;
                }
-               if (delete_ref(name, old_sha1)) {
+               if (delete_ref(name, old_sha1, 0)) {
                        error("failed to delete %s", name);
                        return "failed to delete";
                }
@@ -466,12 +537,17 @@ static int delete_only(struct command *cmd)
 
 static int add_refs_from_alternate(struct alternate_object_database *e, void *unused)
 {
-       char *other = xstrdup(make_absolute_path(e->base));
-       size_t len = strlen(other);
+       char *other;
+       size_t len;
        struct remote *remote;
        struct transport *transport;
        const struct ref *extra;
 
+       e->name[-1] = '\0';
+       other = xstrdup(make_absolute_path(e->base));
+       e->name[-1] = '/';
+       len = strlen(other);
+
        while (other[len-1] == '/')
                other[--len] = '\0';
        if (len < 8 || memcmp(other + len - 8, "/objects", 8))
index 6b3667ef0ebdfc2b8b70b24e474b22989fbf0cb7..d95f515f2e32aa71c3a09fe5ed9486f352713360 100644 (file)
@@ -277,11 +277,11 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
        lock = lock_any_ref_for_update(ref, sha1, 0);
        if (!lock)
                return error("cannot lock ref '%s'", ref);
-       log_file = xstrdup(git_path("logs/%s", ref));
+       log_file = git_pathdup("logs/%s", ref);
        if (!file_exists(log_file))
                goto finish;
        if (!cmd->dry_run) {
-               newlog_path = xstrdup(git_path("logs/%s.lock", ref));
+               newlog_path = git_pathdup("logs/%s.lock", ref);
                cb.newlog = fopen(newlog_path, "w");
        }
 
index 6b3325dfa9e0aa52d806d1c28692a05cf549abe4..71696b50d333f24f1c7a6a7ace946cf3392ca7d0 100644 (file)
@@ -10,6 +10,7 @@
 static const char * const builtin_remote_usage[] = {
        "git remote",
        "git remote add <name> <url>",
+       "git remote rename <old> <new>",
        "git remote rm <name>",
        "git remote show <name>",
        "git remote prune <name>",
@@ -320,7 +321,7 @@ static int add_branch_for_removal(const char *refname,
 
        /* make sure that symrefs are deleted */
        if (flags & REF_ISSYMREF)
-               return unlink(git_path(refname));
+               return unlink(git_path("%s", refname));
 
        item = string_list_append(refname, branches->branches);
        item->util = xmalloc(20);
@@ -329,6 +330,191 @@ static int add_branch_for_removal(const char *refname,
        return 0;
 }
 
+struct rename_info {
+       const char *old;
+       const char *new;
+       struct string_list *remote_branches;
+};
+
+static int read_remote_branches(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+{
+       struct rename_info *rename = cb_data;
+       struct strbuf buf = STRBUF_INIT;
+       struct string_list_item *item;
+       int flag;
+       unsigned char orig_sha1[20];
+       const char *symref;
+
+       strbuf_addf(&buf, "refs/remotes/%s", rename->old);
+       if(!prefixcmp(refname, buf.buf)) {
+               item = string_list_append(xstrdup(refname), rename->remote_branches);
+               symref = resolve_ref(refname, orig_sha1, 1, &flag);
+               if (flag & REF_ISSYMREF)
+                       item->util = xstrdup(symref);
+               else
+                       item->util = NULL;
+       }
+
+       return 0;
+}
+
+static int migrate_file(struct remote *remote)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int i;
+       char *path = NULL;
+
+       strbuf_addf(&buf, "remote.%s.url", remote->name);
+       for (i = 0; i < remote->url_nr; i++)
+               if (git_config_set_multivar(buf.buf, remote->url[i], "^$", 0))
+                       return error("Could not append '%s' to '%s'",
+                                       remote->url[i], buf.buf);
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s.push", remote->name);
+       for (i = 0; i < remote->push_refspec_nr; i++)
+               if (git_config_set_multivar(buf.buf, remote->push_refspec[i], "^$", 0))
+                       return error("Could not append '%s' to '%s'",
+                                       remote->push_refspec[i], buf.buf);
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s.fetch", remote->name);
+       for (i = 0; i < remote->fetch_refspec_nr; i++)
+               if (git_config_set_multivar(buf.buf, remote->fetch_refspec[i], "^$", 0))
+                       return error("Could not append '%s' to '%s'",
+                                       remote->fetch_refspec[i], buf.buf);
+       if (remote->origin == REMOTE_REMOTES)
+               path = git_path("remotes/%s", remote->name);
+       else if (remote->origin == REMOTE_BRANCHES)
+               path = git_path("branches/%s", remote->name);
+       if (path && unlink(path))
+               warning("failed to remove '%s'", path);
+       return 0;
+}
+
+static int mv(int argc, const char **argv)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+       struct remote *oldremote, *newremote;
+       struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
+       struct string_list remote_branches = { NULL, 0, 0, 0 };
+       struct rename_info rename;
+       int i;
+
+       if (argc != 3)
+               usage_with_options(builtin_remote_usage, options);
+
+       rename.old = argv[1];
+       rename.new = argv[2];
+       rename.remote_branches = &remote_branches;
+
+       oldremote = remote_get(rename.old);
+       if (!oldremote)
+               die("No such remote: %s", rename.old);
+
+       if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
+               return migrate_file(oldremote);
+
+       newremote = remote_get(rename.new);
+       if (newremote && (newremote->url_nr > 1 || newremote->fetch_refspec_nr))
+               die("remote %s already exists.", rename.new);
+
+       strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
+       if (!valid_fetch_refspec(buf.buf))
+               die("'%s' is not a valid remote name", rename.new);
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s", rename.old);
+       strbuf_addf(&buf2, "remote.%s", rename.new);
+       if (git_config_rename_section(buf.buf, buf2.buf) < 1)
+               return error("Could not rename config section '%s' to '%s'",
+                               buf.buf, buf2.buf);
+
+       strbuf_reset(&buf);
+       strbuf_addf(&buf, "remote.%s.fetch", rename.new);
+       if (git_config_set_multivar(buf.buf, NULL, NULL, 1))
+               return error("Could not remove config section '%s'", buf.buf);
+       for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
+               char *ptr;
+
+               strbuf_reset(&buf2);
+               strbuf_addstr(&buf2, oldremote->fetch_refspec[i]);
+               ptr = strstr(buf2.buf, rename.old);
+               if (ptr)
+                       strbuf_splice(&buf2, ptr-buf2.buf, strlen(rename.old),
+                                       rename.new, strlen(rename.new));
+               if (git_config_set_multivar(buf.buf, buf2.buf, "^$", 0))
+                       return error("Could not append '%s'", buf.buf);
+       }
+
+       read_branches();
+       for (i = 0; i < branch_list.nr; i++) {
+               struct string_list_item *item = branch_list.items + i;
+               struct branch_info *info = item->util;
+               if (info->remote && !strcmp(info->remote, rename.old)) {
+                       strbuf_reset(&buf);
+                       strbuf_addf(&buf, "branch.%s.remote", item->string);
+                       if (git_config_set(buf.buf, rename.new)) {
+                               return error("Could not set '%s'", buf.buf);
+                       }
+               }
+       }
+
+       /*
+        * First remove symrefs, then rename the rest, finally create
+        * the new symrefs.
+        */
+       for_each_ref(read_remote_branches, &rename);
+       for (i = 0; i < remote_branches.nr; i++) {
+               struct string_list_item *item = remote_branches.items + i;
+               int flag = 0;
+               unsigned char sha1[20];
+               const char *symref;
+
+               symref = resolve_ref(item->string, sha1, 1, &flag);
+               if (!(flag & REF_ISSYMREF))
+                       continue;
+               if (delete_ref(item->string, NULL, REF_NODEREF))
+                       die("deleting '%s' failed", item->string);
+       }
+       for (i = 0; i < remote_branches.nr; i++) {
+               struct string_list_item *item = remote_branches.items + i;
+
+               if (item->util)
+                       continue;
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, item->string);
+               strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
+                               rename.new, strlen(rename.new));
+               strbuf_reset(&buf2);
+               strbuf_addf(&buf2, "remote: renamed %s to %s",
+                               item->string, buf.buf);
+               if (rename_ref(item->string, buf.buf, buf2.buf))
+                       die("renaming '%s' failed", item->string);
+       }
+       for (i = 0; i < remote_branches.nr; i++) {
+               struct string_list_item *item = remote_branches.items + i;
+
+               if (!item->util)
+                       continue;
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, item->string);
+               strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
+                               rename.new, strlen(rename.new));
+               strbuf_reset(&buf2);
+               strbuf_addstr(&buf2, item->util);
+               strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
+                               rename.new, strlen(rename.new));
+               strbuf_reset(&buf3);
+               strbuf_addf(&buf3, "remote: renamed %s to %s",
+                               item->string, buf.buf);
+               if (create_symref(buf.buf, buf2.buf, buf3.buf))
+                       die("creating '%s' failed", buf.buf);
+       }
+       return 0;
+}
+
 static int remove_branches(struct string_list *branches)
 {
        int i, result = 0;
@@ -337,7 +523,7 @@ static int remove_branches(struct string_list *branches)
                const char *refname = item->string;
                unsigned char *sha1 = item->util;
 
-               if (delete_ref(refname, sha1))
+               if (delete_ref(refname, sha1, 0))
                        result |= error("Could not remove branch %s", refname);
        }
        return result;
@@ -412,10 +598,9 @@ static void show_list(const char *title, struct string_list *list,
                return;
 
        printf(title, list->nr > 1 ? "es" : "", extra_arg);
-       printf("\n    ");
-       for (i = 0; i < list->nr; i++)
-               printf("%s%s", i ? " " : "", list->items[i].string);
        printf("\n");
+       for (i = 0; i < list->nr; i++)
+               printf("    %s\n", list->items[i].string);
 }
 
 static int get_remote_ref_states(const char *name,
@@ -511,17 +696,17 @@ static int show(int argc, const char **argv)
                show_list("  Tracked remote branch%s", &states.tracked, "");
 
                if (states.remote->push_refspec_nr) {
-                       printf("  Local branch%s pushed with 'git push'\n   ",
+                       printf("  Local branch%s pushed with 'git push'\n",
                                states.remote->push_refspec_nr > 1 ?
                                        "es" : "");
                        for (i = 0; i < states.remote->push_refspec_nr; i++) {
                                struct refspec *spec = states.remote->push + i;
-                               printf(" %s%s%s%s", spec->force ? "+" : "",
+                               printf("    %s%s%s%s\n",
+                                      spec->force ? "+" : "",
                                       abbrev_branch(spec->src),
                                       spec->dst ? ":" : "",
                                       spec->dst ? abbrev_branch(spec->dst) : "");
                        }
-                       printf("\n");
                }
 
                /* NEEDSWORK: free remote */
@@ -566,7 +751,7 @@ static int prune(int argc, const char **argv)
                        const char *refname = states.stale.items[i].util;
 
                        if (!dry_run)
-                               result |= delete_ref(refname, NULL);
+                               result |= delete_ref(refname, NULL, 0);
 
                        printf(" * [%s] %s\n", dry_run ? "would prune" : "pruned",
                               abbrev_ref(refname, "refs/remotes/"));
@@ -696,6 +881,8 @@ int cmd_remote(int argc, const char **argv, const char *prefix)
                result = show_all();
        else if (!strcmp(argv[0], "add"))
                result = add(argc, argv);
+       else if (!strcmp(argv[0], "rename"))
+               result = mv(argc, argv);
        else if (!strcmp(argv[0], "rm"))
                result = rm(argc, argv);
        else if (!strcmp(argv[0], "show"))
index dd4573fe8dcd9dc8edd5a7d41bc8daa83034ee7e..d4dec6b7156b081bf63933d4de6d0b62ab2212c8 100644 (file)
@@ -98,6 +98,7 @@ static int diff_two(const char *file1, const char *label1,
 
        printf("--- a/%s\n+++ b/%s\n", label1, label2);
        fflush(stdout);
+       memset(&xpp, 0, sizeof(xpp));
        xpp.flags = XDF_NEED_MINIMAL;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
index 16e6bb20f1af6a7a684a747b3c0cc26ceabe8ce7..9514b77f8c0b4e8a576e888df905b501e198df24 100644 (file)
@@ -279,7 +279,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
        }
        else if (old_orig)
-               delete_ref("ORIG_HEAD", old_orig);
+               delete_ref("ORIG_HEAD", old_orig, 0);
        prepend_reflog_action("updating HEAD", msg, sizeof(msg));
        update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
 
index 06cdeb7ebe7468911a7bee670bf5ec539185ba54..857742a14f82e049c5b9e8b234dae9e9e1a7dc30 100644 (file)
@@ -100,7 +100,7 @@ static void show_commit(struct commit *commit)
                        children = children->next;
                }
        }
-       show_decorations(commit);
+       show_decorations(&revs, commit);
        if (revs.commit_format == CMIT_FMT_ONELINE)
                putchar(' ');
        else
index 84865397405d96aa4fdbfca801e8ecd47e3dea36..4038b4118da087069db82bfcf6e0dd298aec9036 100644 (file)
@@ -251,7 +251,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
        int i, index_fd, clean;
        char *oneline, *reencoded_message = NULL;
        const char *message, *encoding;
-       const char *defmsg = xstrdup(git_path("MERGE_MSG"));
+       char *defmsg = git_pathdup("MERGE_MSG");
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
        static struct lock_file index_lock;
@@ -329,7 +329,8 @@ static int revert_or_cherry_pick(int argc, const char **argv)
         * reverse of it if we are revert.
         */
 
-       msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1);
+       msg_fd = hold_lock_file_for_update(&msg_file, defmsg,
+                                          LOCK_DIE_ON_ERROR);
 
        encoding = get_encoding(message);
        if (!encoding)
@@ -431,6 +432,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                return execv_git_cmd(args);
        }
        free(reencoded_message);
+       free(defmsg);
 
        return 0;
 }
index e06640cf8d3418cbbe177b8fdcdccd19e0f3379f..b7126e3e25d4f52ae8c4a120d524a24c11129785 100644 (file)
@@ -79,7 +79,8 @@ static int check_local_mod(unsigned char *head, int index_only)
                     || hashcmp(ce->sha1, sha1))
                        staged_changes = 1;
 
-               if (local_changes && staged_changes)
+               if (local_changes && staged_changes &&
+                   !(index_only && is_empty_blob_sha1(ce->sha1)))
                        errs = error("'%s' has staged content different "
                                     "from both the file and the HEAD\n"
                                     "(use -f to force removal)", name);
index 910db92b62eb6dd91a4002b2643fef4a76ec8f83..a9fdbf9d45ddd84e6397ab3e559b06e105c52a19 100644 (file)
@@ -140,7 +140,13 @@ static struct ref *remote_refs, **remote_tail;
 static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct ref *ref;
-       int len = strlen(refname) + 1;
+       int len;
+
+       /* we already know it starts with refs/ to get here */
+       if (check_ref_format(refname + 5))
+               return 0;
+
+       len = strlen(refname) + 1;
        ref = xcalloc(1, sizeof(*ref) + len);
        hashcpy(ref->new_sha1, sha1);
        memcpy(ref->name, refname, len);
@@ -224,7 +230,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
 {
        struct refspec rs;
 
-       if (ref->status != REF_STATUS_OK)
+       if (ref->status != REF_STATUS_OK && ref->status != REF_STATUS_UPTODATE)
                return;
 
        rs.src = ref->name;
@@ -234,7 +240,7 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref)
                if (args.verbose)
                        fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
                if (ref->deletion) {
-                       delete_ref(rs.dst, NULL);
+                       delete_ref(rs.dst, NULL, 0);
                } else
                        update_ref("update by push", rs.dst,
                                        ref->new_sha1, NULL, 0, 0);
@@ -429,24 +435,19 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
         */
        new_refs = 0;
        for (ref = remote_refs; ref; ref = ref->next) {
-               const unsigned char *new_sha1;
-
-               if (!ref->peer_ref) {
-                       if (!args.send_mirror)
-                               continue;
-                       new_sha1 = null_sha1;
-               }
-               else
-                       new_sha1 = ref->peer_ref->new_sha1;
 
+               if (ref->peer_ref)
+                       hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+               else if (!args.send_mirror)
+                       continue;
 
-               ref->deletion = is_null_sha1(new_sha1);
+               ref->deletion = is_null_sha1(ref->new_sha1);
                if (ref->deletion && !allow_deleting_refs) {
                        ref->status = REF_STATUS_REJECT_NODELETE;
                        continue;
                }
                if (!ref->deletion &&
-                   !hashcmp(ref->old_sha1, new_sha1)) {
+                   !hashcmp(ref->old_sha1, ref->new_sha1)) {
                        ref->status = REF_STATUS_UPTODATE;
                        continue;
                }
@@ -474,14 +475,13 @@ static int do_send_pack(int in, int out, struct remote *remote, const char *dest
                    !ref->deletion &&
                    !is_null_sha1(ref->old_sha1) &&
                    (!has_sha1_file(ref->old_sha1)
-                     || !ref_newer(new_sha1, ref->old_sha1));
+                     || !ref_newer(ref->new_sha1, ref->old_sha1));
 
                if (ref->nonfastforward && !ref->force && !args.force_update) {
                        ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
                        continue;
                }
 
-               hashcpy(ref->new_sha1, new_sha1);
                if (!ref->deletion)
                        new_refs++;
 
index b13fa34d8c59a9226599ba10b4d679f41ad230b0..d339971fab896bda0297f04a4f3961bae27b21be 100644 (file)
@@ -125,7 +125,7 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn)
 static int delete_tag(const char *name, const char *ref,
                                const unsigned char *sha1)
 {
-       if (delete_ref(ref, sha1))
+       if (delete_ref(ref, sha1, 0))
                return 1;
        printf("Deleted tag '%s'\n", name);
        return 0;
@@ -283,7 +283,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                int fd;
 
                /* write the template message before editing: */
-               path = xstrdup(git_path("TAG_EDITMSG"));
+               path = git_pathdup("TAG_EDITMSG");
                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
                if (fd < 0)
                        die("could not create file '%s': %s",
@@ -344,7 +344,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        const char *object_ref, *tag;
        struct ref_lock *lock;
 
-       int annotate = 0, sign = 0, force = 0, lines = 0,
+       int annotate = 0, sign = 0, force = 0, lines = -1,
                list = 0, delete = 0, verify = 0;
        const char *msgfile = NULL, *keyid = NULL;
        struct msg_arg msg = { 0, STRBUF_INIT };
@@ -380,9 +380,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        }
        if (sign)
                annotate = 1;
+       if (argc == 0 && !(delete || verify))
+               list = 1;
 
+       if ((annotate || msg.given || msgfile || force) &&
+           (list || delete || verify))
+               usage_with_options(git_tag_usage, options);
+
+       if (list + delete + verify > 1)
+               usage_with_options(git_tag_usage, options);
        if (list)
-               return list_tags(argv[0], lines);
+               return list_tags(argv[0], lines == -1 ? 0 : lines);
+       if (lines != -1)
+               die("-n option is only allowed with -l.");
        if (delete)
                return for_each_tag_name(argv, delete_tag);
        if (verify)
@@ -406,11 +416,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                }
        }
 
-       if (argc == 0) {
-               if (annotate)
-                       usage_with_options(git_tag_usage, options);
-               return list_tags(NULL, lines);
-       }
        tag = argv[0];
 
        object_ref = argc == 2 ? argv[1] : "HEAD";
index 9f4bdd3296d19b75211ca0f2434d227eafac44b6..47ed610677fe47f855beaac02f40fa84d132455e 100644 (file)
@@ -370,6 +370,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
                        base_offset = (base_offset << 7) + (c & 127);
                }
                base_offset = obj_list[nr].offset - base_offset;
+               if (base_offset <= 0 || base_offset >= obj_list[nr].offset)
+                       die("offset value out of bound for delta base object");
 
                delta_data = get_data(delta_size);
                if (dry_run || !delta_data) {
index 56a0b1b39cf4c4fc51dbbff256240655bc36a038..378dc1b7a6bb4d56d301a34a4d44dae2f9a37e44 100644 (file)
@@ -13,7 +13,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
        const char *refname, *oldval, *msg=NULL;
        unsigned char sha1[20], oldsha1[20];
-       int delete = 0, no_deref = 0;
+       int delete = 0, no_deref = 0, flags = 0;
        struct option options[] = {
                OPT_STRING( 'm', NULL, &msg, "reason", "reason of the update"),
                OPT_BOOLEAN('d', NULL, &delete, "deletes the reference"),
@@ -47,9 +47,11 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
        if (oldval && *oldval && get_sha1(oldval, oldsha1))
                die("%s: not a valid old SHA1", oldval);
 
+       if (no_deref)
+               flags = REF_NODEREF;
        if (delete)
-               return delete_ref(refname, oldval ? oldsha1 : NULL);
+               return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
        else
                return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
-                                 no_deref ? REF_NODEREF : 0, DIE_ON_ERR);
+                                 flags, DIE_ON_ERR);
 }
index 00b2aabefca49b634f49143523ee31556baa7777..daecd8e1cad4a301e2faa3888c561746d029f09d 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -114,7 +114,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
                        continue;
                }
                if (++ret == 1)
-                       error(message);
+                       error("%s", message);
                error("%s %s", sha1_to_hex(e->sha1), e->name);
        }
        if (revs.pending.nr != p->nr)
@@ -139,7 +139,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
        for (i = 0; i < req_nr; i++)
                if (!(refs.objects[i].item->flags & SHOWN)) {
                        if (++ret == 1)
-                               error(message);
+                               error("%s", message);
                        error("%s %s", sha1_to_hex(refs.objects[i].item->sha1),
                                refs.objects[i].name);
                }
@@ -186,7 +186,8 @@ int create_bundle(struct bundle_header *header, const char *path,
        if (bundle_to_stdout)
                bundle_fd = 1;
        else
-               bundle_fd = hold_lock_file_for_update(&lock, path, 1);
+               bundle_fd = hold_lock_file_for_update(&lock, path,
+                                                     LOCK_DIE_ON_ERROR);
 
        /* write signature */
        write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
diff --git a/cache.h b/cache.h
index 991544cf0bd7e84c5db5bd6486e5922a53ec136f..3b5f0c4c003a611b45e2ca9455681dc8a8347516 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -262,6 +262,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
 
 #define read_cache() read_index(&the_index)
 #define read_cache_from(path) read_index_from(&the_index, (path))
+#define is_cache_unborn() is_index_unborn(&the_index)
 #define read_cache_unmerged() read_index_unmerged(&the_index)
 #define write_cache(newfd, cache, entries) write_index(&the_index, (newfd))
 #define discard_cache() discard_index(&the_index)
@@ -277,6 +278,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
 #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, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
+#define cache_name_is_other(name, namelen) index_name_is_other(&the_index, (name), (namelen))
 #endif
 
 enum object_type {
@@ -367,6 +369,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
 extern int read_index_from(struct index_state *, const char *path);
+extern int is_index_unborn(struct index_state *);
 extern int read_index_unmerged(struct index_state *);
 extern int write_index(const struct index_state *, int newfd);
 extern int discard_index(struct index_state *);
@@ -393,6 +396,7 @@ extern int add_to_index(struct index_state *, const char *path, struct stat *, i
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 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);
+extern int index_name_is_other(const struct index_state *, const char *, int);
 
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID          01
@@ -421,6 +425,8 @@ struct lock_file {
        char on_list;
        char filename[PATH_MAX];
 };
+#define LOCK_DIE_ON_ERROR 1
+#define LOCK_NODEREF 2
 extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
@@ -430,7 +436,7 @@ extern int commit_locked_index(struct lock_file *);
 extern void set_alternate_index_output(const char *);
 extern int close_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
-extern int delete_ref(const char *, const unsigned char *sha1);
+extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
 
 /* Environment bits from configuration mechanism */
 extern int trust_executable_bit;
@@ -491,6 +497,13 @@ extern int check_repository_format(void);
 #define DATA_CHANGED    0x0020
 #define TYPE_CHANGED    0x0040
 
+extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+       __attribute__((format (printf, 3, 4)));
+extern char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+       __attribute__((format (printf, 3, 4)));
+extern char *git_pathdup(const char *fmt, ...)
+       __attribute__((format (printf, 1, 2)));
+
 /* Return a statically allocated filename matching the sha1 signature */
 extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
@@ -515,6 +528,7 @@ static inline void hashclr(unsigned char *hash)
 {
        memset(hash, 0, 20);
 }
+extern int is_empty_blob_sha1(const unsigned char *sha1);
 
 int git_mkstemp(char *path, size_t n, const char *template);
 
@@ -560,12 +574,16 @@ extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 /* just like read_sha1_file(), but non fatal in presence of bad objects */
 extern void *read_object(const unsigned char *sha1, enum object_type *type, unsigned long *size);
 
+/* global flag to enable extra checks when accessing packed objects */
+extern int do_check_packed_object_crc;
+
 extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
 
 extern int move_temp_to_file(const char *tmpfile, const char *filename);
 
 extern int has_sha1_pack(const unsigned char *sha1, const char **ignore);
 extern int has_sha1_file(const unsigned char *sha1);
+extern int has_loose_object_nonlocal(const unsigned char *sha1);
 
 extern int has_pack_file(const unsigned char *sha1);
 extern int has_pack_index(const unsigned char *sha1);
@@ -674,7 +692,8 @@ extern struct packed_git {
        int index_version;
        time_t mtime;
        int pack_fd;
-       int pack_local;
+       unsigned pack_local:1,
+                pack_keep:1;
        unsigned char sha1[20];
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
@@ -746,7 +765,7 @@ extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t
 extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t);
 extern off_t find_pack_entry_one(const unsigned char *, struct packed_git *);
 extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
-extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
+extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
 extern int matches_pack_name(struct packed_git *p, const char *name);
index 5aa1104d3480f8057dd71c49bcf300eccabe2f9d..ec8df39bb01347eb035ad94553357b80af652b78 100644 (file)
@@ -213,6 +213,7 @@ static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
 
        parent_file.ptr = grab_blob(parent, &sz);
        parent_file.size = sz;
+       memset(&xpp, 0, sizeof(xpp));
        xpp.flags = XDF_NEED_MINIMAL;
        memset(&xecfg, 0, sizeof(xecfg));
        memset(&state, 0, sizeof(state));
index 4c05864fb426d61a652f7df12c13315665972846..3a7b06a828930ca23d0fb045feab993a1452b2d3 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -65,6 +65,8 @@ enum cmit_fmt {
 
 extern int non_ascii(int);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+extern char *reencode_commit_message(const struct commit *commit,
+                                    const char **encoding_p);
 extern void get_commit_format(const char *arg, struct rev_info *);
 extern void format_commit_message(const struct commit *commit,
                                  const void *format, struct strbuf *sb,
index 423ff20b0ef88111acbb61bd590fe39a4504a93a..ebac1483929c798905e6558e0013e3de3d6abeb2 100644 (file)
@@ -91,13 +91,24 @@ static int cygwin_stat(const char *path, struct stat *buf)
  * functions should be used. The choice is determined by core.ignorecygwinfstricks.
  * Reading this option is not always possible immediately as git_dir may be
  * not be set yet. So until it is set, use cygwin lstat/stat functions.
+ * However, if core.filemode is set, we must use the Cygwin posix
+ * stat/lstat as the Windows stat fuctions do not determine posix filemode.
+ *
+ * Note that git_cygwin_config() does NOT call git_default_config() and this
+ * is deliberate.  Many commands read from config to establish initial
+ * values in variables and later tweak them from elsewhere (e.g. command line).
+ * init_stat() is called lazily on demand, typically much late in the program,
+ * and calling git_default_config() from here would break such variables.
  */
 static int native_stat = 1;
+static int core_filemode;
 
 static int git_cygwin_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "core.ignorecygwinfstricks"))
                native_stat = git_config_bool(var, value);
+       else if (!strcmp(var, "core.filemode"))
+               core_filemode = git_config_bool(var, value);
        return 0;
 }
 
@@ -105,8 +116,13 @@ static int init_stat(void)
 {
        if (have_git_dir()) {
                git_config(git_cygwin_config, NULL);
-               cygwin_stat_fn = native_stat ? cygwin_stat : stat;
-               cygwin_lstat_fn = native_stat ? cygwin_lstat : lstat;
+               if (!core_filemode && native_stat) {
+                       cygwin_stat_fn = cygwin_stat;
+                       cygwin_lstat_fn = cygwin_lstat;
+               } else {
+                       cygwin_stat_fn = stat;
+                       cygwin_lstat_fn = lstat;
+               }
                return 0;
        }
        return 1;
index 09858f6c593d9a8301c857c81e09bfa24b955eac..b534a8a4725617a3711c1ac4e2cc1883814b4684 100644 (file)
@@ -536,12 +536,16 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
                 * would normally create a console window. But
                 * since we'll be redirecting std streams, we do
                 * not need the console.
+                * It is necessary to use DETACHED_PROCESS
+                * instead of CREATE_NO_WINDOW to make ssh
+                * recognize that it has no console.
                 */
-               flags = CREATE_NO_WINDOW;
+               flags = DETACHED_PROCESS;
        } else {
                /* There is already a console. If we specified
-                * CREATE_NO_WINDOW here, too, Windows would
+                * DETACHED_PROCESS here, too, Windows would
                 * disassociate the child from the console.
+                * The same is true for CREATE_NO_WINDOW.
                 * Go figure!
                 */
                flags = 0;
index b8d289d21789b5017579d90b7894db8ebee4c47a..67cc1dcad0b52f0186c0c9564af43853d8994797 100644 (file)
--- a/config.c
+++ b/config.c
@@ -649,7 +649,7 @@ int git_config(config_fn_t fn, void *data)
                free(user_config);
        }
 
-       repo_config = xstrdup(git_path("config"));
+       repo_config = git_pathdup("config");
        ret += git_config_from_file(fn, repo_config, data);
        free(repo_config);
        return ret;
@@ -889,7 +889,7 @@ int git_config_set_multivar(const char* key, const char* value,
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
-               config_filename = xstrdup(git_path("config"));
+               config_filename = git_pathdup("config");
 
        /*
         * Since "key" actually contains the section name and the real
@@ -1149,7 +1149,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
        if (config_exclusive_filename)
                config_filename = xstrdup(config_exclusive_filename);
        else
-               config_filename = xstrdup(git_path("config"));
+               config_filename = git_pathdup("config");
        out_fd = hold_lock_file_for_update(lock, config_filename, 0);
        if (out_fd < 0) {
                ret = error("could not lock config file %s", config_filename);
index 17e9861c0634133ec5e07af99191cff99f8a40ad..ea7705c1edb6040a900159077a60e129e03a5de1 100644 (file)
@@ -41,6 +41,7 @@ NO_C99_FORMAT=@NO_C99_FORMAT@
 NO_STRCASESTR=@NO_STRCASESTR@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
+NO_UINTMAX_T=@NO_UINTMAX_T@
 NO_STRTOUMAX=@NO_STRTOUMAX@
 NO_SETENV=@NO_SETENV@
 NO_UNSETENV=@NO_UNSETENV@
@@ -50,3 +51,4 @@ OLD_ICONV=@OLD_ICONV@
 NO_DEFLATE_BOUND=@NO_DEFLATE_BOUND@
 FREAD_READS_DIRECTORIES=@FREAD_READS_DIRECTORIES@
 SNPRINTF_RETURNS_BOGUS=@SNPRINTF_RETURNS_BOGUS@
+PTHREAD_LIBS=@PTHREAD_LIBS@
index 27bab00a454c1a29946bbbc0a573ae83f83ee07f..42567420e0b6ab01bc0a905b61c3fd7939f800f9 100644 (file)
@@ -65,7 +65,17 @@ else \
 fi \
 ])# GIT_PARSE_WITH
 
-
+dnl
+dnl GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
+dnl -----------------------------------------
+dnl Similar to AC_CHECK_FUNC, but on systems that do not generate
+dnl warnings for missing prototypes (e.g. FreeBSD when compiling without
+dnl -Wall), it does not work.  By looking for function definition in
+dnl libraries, this problem can be worked around.
+AC_DEFUN([GIT_CHECK_FUNC],[AC_CHECK_FUNC([$1],[
+  AC_SEARCH_LIBS([$1],,
+  [$2],[$3])
+],[$3])])
 ## Site configuration related to programs (before tests)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
 #
@@ -325,7 +335,7 @@ AC_SUBST(NO_SOCKADDR_STORAGE)
 #
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 AC_CHECK_TYPE([struct addrinfo],[
AC_CHECK_FUNC([getaddrinfo],
GIT_CHECK_FUNC([getaddrinfo],
   [NO_IPV6=],
   [NO_IPV6=YesPlease])
 ],[NO_IPV6=YesPlease],[
@@ -419,43 +429,51 @@ AC_SUBST(SNPRINTF_RETURNS_BOGUS)
 AC_MSG_NOTICE([CHECKS for library functions])
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
-AC_CHECK_FUNC(strcasestr,
+GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=],
 [NO_STRCASESTR=YesPlease])
 AC_SUBST(NO_STRCASESTR)
 #
 # Define NO_MEMMEM if you don't have memmem.
-AC_CHECK_FUNC(memmem,
+GIT_CHECK_FUNC(memmem,
 [NO_MEMMEM=],
 [NO_MEMMEM=YesPlease])
 AC_SUBST(NO_MEMMEM)
 #
 # Define NO_STRLCPY if you don't have strlcpy.
-AC_CHECK_FUNC(strlcpy,
+GIT_CHECK_FUNC(strlcpy,
 [NO_STRLCPY=],
 [NO_STRLCPY=YesPlease])
 AC_SUBST(NO_STRLCPY)
 #
+# Define NO_UINTMAX_T if your platform does not have uintmax_t
+AC_CHECK_TYPE(uintmax_t,
+[NO_UINTMAX_T=],
+[NO_UINTMAX_T=YesPlease],[
+#include <inttypes.h>
+])
+AC_SUBST(NO_UINTMAX_T)
+#
 # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
-AC_CHECK_FUNC(strtoumax,
+GIT_CHECK_FUNC(strtoumax,
 [NO_STRTOUMAX=],
 [NO_STRTOUMAX=YesPlease])
 AC_SUBST(NO_STRTOUMAX)
 #
 # Define NO_SETENV if you don't have setenv in the C library.
-AC_CHECK_FUNC(setenv,
+GIT_CHECK_FUNC(setenv,
 [NO_SETENV=],
 [NO_SETENV=YesPlease])
 AC_SUBST(NO_SETENV)
 #
 # Define NO_UNSETENV if you don't have unsetenv in the C library.
-AC_CHECK_FUNC(unsetenv,
+GIT_CHECK_FUNC(unsetenv,
 [NO_UNSETENV=],
 [NO_UNSETENV=YesPlease])
 AC_SUBST(NO_UNSETENV)
 #
 # Define NO_MKDTEMP if you don't have mkdtemp in the C library.
-AC_CHECK_FUNC(mkdtemp,
+GIT_CHECK_FUNC(mkdtemp,
 [NO_MKDTEMP=],
 [NO_MKDTEMP=YesPlease])
 AC_SUBST(NO_MKDTEMP)
@@ -471,6 +489,22 @@ AC_SUBST(NO_MKDTEMP)
 #
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
+#
+# Define PTHREAD_LIBS to the linker flag used for Pthread support.
+AC_LANG_CONFTEST([AC_LANG_PROGRAM(
+  [[#include <pthread.h>]],
+  [[pthread_mutex_t test_mutex;]]
+)])
+${CC} -pthread conftest.c -o conftest.o > /dev/null 2>&1
+if test $? -eq 0;then
+ PTHREAD_LIBS="-pthread"
+else
+ ${CC} -lpthread conftest.c -o conftest.o > /dev/null 2>&1
+ if test $? -eq 0;then
+  PTHREAD_LIBS="-lpthread"
+ fi
+fi
+AC_SUBST(PTHREAD_LIBS)
 
 ## Site configuration (override autodetection)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
index 67d2cd86a89a92f8f75baa7ae5e820ac3150f3b4..584e04c217da4ea8943e33c77fea56ce64547ed1 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -70,6 +70,9 @@ struct ref **get_remote_heads(int in, struct ref **list,
                if (buffer[len-1] == '\n')
                        buffer[--len] = 0;
 
+               if (len > 4 && !prefixcmp(buffer, "ERR "))
+                       die("remote error: %s", buffer + 4);
+
                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
                        die("protocol error: expected sha/ref, got '%s'", buffer);
                name = buffer + 41;
@@ -90,9 +93,8 @@ struct ref **get_remote_heads(int in, struct ref **list,
                        continue;
                if (nr_match && !path_match(name, nr_match, match))
                        continue;
-               ref = alloc_ref(name_len + 1);
+               ref = alloc_ref(buffer + 41);
                hashcpy(ref->old_sha1, old_sha1);
-               memcpy(ref->name, buffer + 41, name_len + 1);
                *list = ref;
                list = &ref->next;
        }
index d192927c20a1b4bca97a6bea91cc10b0b67bdd29..de193ba7c1caf69367410d763f3541555a64746f 100755 (executable)
@@ -881,6 +881,7 @@ _git_help ()
                attributes cli core-tutorial cvs-migration
                diffcore gitk glossary hooks ignore modules
                repository-layout tutorial tutorial-2
+               workflows
                "
 }
 
@@ -1397,6 +1398,8 @@ _git_shortlog ()
 
 _git_show ()
 {
+       __git_has_doubledash && return
+
        local cur="${COMP_WORDS[COMP_CWORD]}"
        case "$cur" in
        --pretty=*)
index 36bd54c985080f8dd5558a3e7a4e19ede9fbab93..b17952a785d9e45dea8a71c211f76f0271e659da 100755 (executable)
@@ -309,7 +309,7 @@ sub update_remote {
                        }
                }
        } else {
-               print STDERR "Remote group $name does not exists.\n";
+               print STDERR "Remote group $name does not exist.\n";
                exit(1);
        }
        for (@remotes) {
index 2216cacba79ad7171b7b5abbd1ff27e7b1ef84a1..9f0a5f92c1f01f82aba14fbf3b92fd56a1e0674b 100755 (executable)
@@ -316,8 +316,11 @@ def gitBranchExists(branch):
                             stderr=subprocess.PIPE, stdout=subprocess.PIPE);
     return proc.wait() == 0;
 
+_gitConfig = {}
 def gitConfig(key):
-    return read_pipe("git config %s" % key, ignore_error=True).strip()
+    if not _gitConfig.has_key(key):
+        _gitConfig[key] = read_pipe("git config %s" % key, ignore_error=True).strip()
+    return _gitConfig[key]
 
 def p4BranchesInGit(branchesAreInRemotes = True):
     branches = {}
@@ -946,7 +949,7 @@ class P4Sync(Command):
 
             if includeFile:
                 filesForCommit.append(f)
-                if f['action'] != 'delete':
+                if f['action'] not in ('delete', 'purge'):
                     filesToRead.append(f)
 
         filedata = []
@@ -965,11 +968,11 @@ class P4Sync(Command):
         while j < len(filedata):
             stat = filedata[j]
             j += 1
-            text = [];
+            text = ''
             while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
-                text.append(filedata[j]['data'])
+                text += filedata[j]['data']
+                del filedata[j]['data']
                 j += 1
-            text = ''.join(text)
 
             if not stat.has_key('depotFile'):
                 sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
@@ -1038,7 +1041,7 @@ class P4Sync(Command):
                 continue
 
             relPath = self.stripRepoPath(file['path'], branchPrefixes)
-            if file["action"] == "delete":
+            if file["action"] in ("delete", "purge"):
                 self.gitStream.write("D %s\n" % relPath)
             else:
                 data = file['data']
@@ -1077,7 +1080,7 @@ class P4Sync(Command):
 
                 cleanedFiles = {}
                 for info in files:
-                    if info["action"] == "delete":
+                    if info["action"] in ("delete", "purge"):
                         continue
                     cleanedFiles[info["depotFile"]] = info["rev"]
 
@@ -1400,7 +1403,7 @@ class P4Sync(Command):
             if change > newestRevision:
                 newestRevision = change
 
-            if info["action"] == "delete":
+            if info["action"] in ("delete", "purge"):
                 # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
                 #fileCnt = fileCnt + 1
                 continue
index 41368950d6b29121089ee9239b8e07ece209a31e..28a3c0e46ecf9951f3f42a025a288a65c70e0424 100644 (file)
 # hooks.emailprefix
 #   All emails have their subjects prefixed with this prefix, or "[SCM]"
 #   if emailprefix is unset, to aid filtering
+# hooks.showrev
+#   The shell command used to format each revision in the email, with
+#   "%s" replaced with the commit id.  Defaults to "git rev-list -1
+#   --pretty %s", displaying the commit id, author, date and log
+#   message.  To list full patches separated by a blank line, you
+#   could set this to "git show -C %s; echo".
 #
 # Notes
 # -----
@@ -224,13 +230,7 @@ generate_create_branch_email()
        echo ""
 
        echo $LOGBEGIN
-       # This shows all log entries that are not already covered by
-       # another ref - i.e. commits that are now accessible from this
-       # ref that were previously not accessible
-       # (see generate_update_branch_email for the explanation of this
-       # command)
-       git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
-       git rev-list --pretty --stdin $newrev
+       show_new_revisions
        echo $LOGEND
 }
 
@@ -390,8 +390,7 @@ generate_update_branch_email()
 
                echo ""
                echo $LOGBEGIN
-               git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
-               git rev-list --pretty --stdin $oldrev..$newrev
+               show_new_revisions
 
                # XXX: Need a way of detecting whether git rev-list actually
                # outputted anything, so that we can issue a "no new
@@ -591,6 +590,45 @@ generate_delete_general_email()
        echo $LOGEND
 }
 
+
+# --------------- Miscellaneous utilities
+
+#
+# Show new revisions as the user would like to see them in the email.
+#
+show_new_revisions()
+{
+       # This shows all log entries that are not already covered by
+       # another ref - i.e. commits that are now accessible from this
+       # ref that were previously not accessible
+       # (see generate_update_branch_email for the explanation of this
+       # command)
+
+       # Revision range passed to rev-list differs for new vs. updated
+       # branches.
+       if [ "$change_type" = create ]
+       then
+               # Show all revisions exclusive to this (new) branch.
+               revspec=$newrev
+       else
+               # Branch update; show revisions not part of $oldrev.
+               revspec=$oldrev..$newrev
+       fi
+
+       git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
+       if [ -z "$custom_showrev" ]
+       then
+               git rev-list --pretty --stdin $revspec
+       else
+               git rev-list --stdin $revspec |
+               while read onerev
+               do
+                       eval $(printf "$custom_showrev" $onerev)
+               done
+       fi
+}
+
+
 send_mail()
 {
        if [ -n "$envelopesender" ]; then
@@ -627,6 +665,7 @@ recipients=$(git config hooks.mailinglist)
 announcerecipients=$(git config hooks.announcelist)
 envelopesender=$(git config hooks.envelopesender)
 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
+custom_showrev=$(git config hooks.showrev)
 
 # --- Main loop
 # Allow dual mode: run from the command line just like the update hook, or
index b0a8caae9e7822fd4ea5c6e9b202373b2947e6af..1f914c94aa748f57bd6c70fdd18dfb681c684556 100644 (file)
@@ -28,6 +28,9 @@ elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null
 then
        exit 0
 elif grep -q '0x01$' /proc/apm 2>/dev/null
+then
+       exit 0
+elif grep -q "AC Power \+: 1" /proc/pmu/info 2>/dev/null
 then
        exit 0
 elif test -x /usr/bin/pmset && /usr/bin/pmset -g batt |
index f4a7b62cd9f1a397118b95792c04c2f70f910f9e..be188c0f11dbea8320737b4fdf426a2f5acd1a00 100755 (executable)
@@ -1,9 +1,9 @@
 #!/usr/bin/perl
 #
 # This tool will print vaguely pretty information about a pack.  It
-# expects the output of "git-verify-pack -v" as input on stdin.
+# expects the output of "git verify-pack -v" as input on stdin.
 #
-# $ git-verify-pack -v | packinfo.pl
+# $ git verify-pack -v | packinfo.pl
 #
 # This prints some full-pack statistics; currently "all sizes", "all
 # path sizes", "tree sizes", "tree path sizes", and "depths".
@@ -20,7 +20,7 @@
 #
 # When run as:
 #
-# $ git-verify-pack -v | packinfo.pl -tree
+# $ git verify-pack -v | packinfo.pl -tree
 #
 # the trees of objects are output along with the stats.  This looks
 # like:
@@ -43,7 +43,7 @@
 #
 # When run as:
 #
-# $ git-verify-pack -v | packinfo.pl -tree -filenames
+# $ git verify-pack -v | packinfo.pl -tree -filenames
 #
 # it adds filenames to the tree.  Getting this information is slow:
 #
@@ -58,7 +58,7 @@
 #
 # When run as:
 #
-# $ git-verify-pack -v | packinfo.pl -dump
+# $ git verify-pack -v | packinfo.pl -dump
 #
 # it prints out "sha1 size pathsize depth" for each sha1 in lexical
 # order.
 }
 
 if ($filenames && ($tree || $dump)) {
-    open(NAMES, "git-name-rev --all|");
+    open(NAMES, "git name-rev --all|");
     while (<NAMES>) {
         if (/^(\S+)\s+(.*)$/) {
             my ($sha1, $name) = ($1, $2);
 
     for my $commit (@commits) {
         my $name = $names{$commit};
-        open(TREE, "git-ls-tree -t -r $commit|");
+        open(TREE, "git ls-tree -t -r $commit|");
         print STDERR "Plumbing tree $name\n";
         while (<TREE>) {
             if (/^(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
index 3e5582d28921af22357f2d6068ef55f847bd016a..b9ba44c582b3b6cfac0f150f7f8901d60b48bbb3 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -537,6 +537,10 @@ static int execute(struct sockaddr *addr)
 #endif
                }
                loginfo("Connection from %s:%d", addrbuf, port);
+               setenv("REMOTE_ADDR", addrbuf, 1);
+       }
+       else {
+               unsetenv("REMOTE_ADDR");
        }
 
        alarm(init_timeout ? init_timeout : timeout);
diff --git a/diff.c b/diff.c
index 1c6be897b2c95fc481c02834e4fe022b6bd405ae..f644947c823e824a294414a660beac02b4182fe4 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -11,6 +11,7 @@
 #include "attr.h"
 #include "run-command.h"
 #include "utf8.h"
+#include "userdiff.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -37,6 +38,9 @@ static char diff_colors[][COLOR_MAXLEN] = {
        "\033[41m",     /* WHITESPACE (red background) */
 };
 
+static void diff_filespec_load_driver(struct diff_filespec *one);
+static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+
 static int parse_diff_color_slot(const char *var, int ofs)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -56,80 +60,6 @@ static int parse_diff_color_slot(const char *var, int ofs)
        die("bad config variable '%s'", var);
 }
 
-static struct ll_diff_driver {
-       const char *name;
-       struct ll_diff_driver *next;
-       const char *cmd;
-} *user_diff, **user_diff_tail;
-
-/*
- * Currently there is only "diff.<drivername>.command" variable;
- * because there are "diff.color.<slot>" variables, we are parsing
- * this in a bit convoluted way to allow low level diff driver
- * called "color".
- */
-static int parse_lldiff_command(const char *var, const char *ep, const char *value)
-{
-       const char *name;
-       int namelen;
-       struct ll_diff_driver *drv;
-
-       name = var + 5;
-       namelen = ep - name;
-       for (drv = user_diff; drv; drv = drv->next)
-               if (!strncmp(drv->name, name, namelen) && !drv->name[namelen])
-                       break;
-       if (!drv) {
-               drv = xcalloc(1, sizeof(struct ll_diff_driver));
-               drv->name = xmemdupz(name, namelen);
-               if (!user_diff_tail)
-                       user_diff_tail = &user_diff;
-               *user_diff_tail = drv;
-               user_diff_tail = &(drv->next);
-       }
-
-       return git_config_string(&(drv->cmd), var, value);
-}
-
-/*
- * 'diff.<what>.funcname' attribute can be specified in the configuration
- * to define a customized regexp to find the beginning of a function to
- * be used for hunk header lines of "diff -p" style output.
- */
-struct funcname_pattern_entry {
-       char *name;
-       char *pattern;
-       int cflags;
-};
-static struct funcname_pattern_list {
-       struct funcname_pattern_list *next;
-       struct funcname_pattern_entry e;
-} *funcname_pattern_list;
-
-static int parse_funcname_pattern(const char *var, const char *ep, const char *value, int cflags)
-{
-       const char *name;
-       int namelen;
-       struct funcname_pattern_list *pp;
-
-       name = var + 5; /* "diff." */
-       namelen = ep - name;
-
-       for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strncmp(pp->e.name, name, namelen) && !pp->e.name[namelen])
-                       break;
-       if (!pp) {
-               pp = xcalloc(1, sizeof(*pp));
-               pp->e.name = xmemdupz(name, namelen);
-               pp->next = funcname_pattern_list;
-               funcname_pattern_list = pp;
-       }
-       free(pp->e.pattern);
-       pp->e.pattern = xstrdup(value);
-       pp->e.cflags = cflags;
-       return 0;
-}
-
 /*
  * These are to give UI layer defaults.
  * The core-level commands such as git-diff-files should
@@ -162,12 +92,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
        }
        if (!strcmp(var, "diff.external"))
                return git_config_string(&external_diff_cmd_cfg, var, value);
-       if (!prefixcmp(var, "diff.")) {
-               const char *ep = strrchr(var, '.');
-
-               if (ep != var + 4 && !strcmp(ep, ".command"))
-                       return parse_lldiff_command(var, ep, value);
-       }
 
        return git_diff_basic_config(var, value, cb);
 }
@@ -179,6 +103,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       switch (userdiff_config(var, value)) {
+               case 0: break;
+               case -1: return -1;
+               default: return 0;
+       }
+
        if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
                int slot = parse_diff_color_slot(var, 11);
                if (!value)
@@ -193,23 +123,6 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!prefixcmp(var, "diff.")) {
-               const char *ep = strrchr(var, '.');
-               if (ep != var + 4) {
-                       if (!strcmp(ep, ".funcname")) {
-                               if (!value)
-                                       return config_error_nonbool(var);
-                               return parse_funcname_pattern(var, ep, value,
-                                       0);
-                       } else if (!strcmp(ep, ".xfuncname")) {
-                               if (!value)
-                                       return config_error_nonbool(var);
-                               return parse_funcname_pattern(var, ep, value,
-                                       REG_EXTENDED);
-                       }
-               }
-       }
-
        return git_color_default_config(var, value, cb);
 }
 
@@ -374,6 +287,7 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
        }
        else if (diff_populate_filespec(one, 0))
                return -1;
+
        mf->ptr = one->data;
        mf->size = one->size;
        return 0;
@@ -470,6 +384,7 @@ static void diff_words_show(struct diff_words_data *diff_words)
        mmfile_t minus, plus;
        int i;
 
+       memset(&xpp, 0, sizeof(xpp));
        memset(&xecfg, 0, sizeof(xecfg));
        minus.size = diff_words->minus.text.size;
        minus.ptr = xmalloc(minus.size);
@@ -1352,136 +1267,37 @@ static void emit_binary_diff(FILE *file, mmfile_t *one, mmfile_t *two)
        emit_binary_diff_body(file, two, one);
 }
 
-static void setup_diff_attr_check(struct git_attr_check *check)
+static void diff_filespec_load_driver(struct diff_filespec *one)
 {
-       static struct git_attr *attr_diff;
-
-       if (!attr_diff) {
-               attr_diff = git_attr("diff", 4);
-       }
-       check[0].attr = attr_diff;
-}
-
-static void diff_filespec_check_attr(struct diff_filespec *one)
-{
-       struct git_attr_check attr_diff_check;
-       int check_from_data = 0;
-
-       if (one->checked_attr)
-               return;
-
-       setup_diff_attr_check(&attr_diff_check);
-       one->is_binary = 0;
-       one->funcname_pattern_ident = NULL;
-
-       if (!git_checkattr(one->path, 1, &attr_diff_check)) {
-               const char *value;
-
-               /* binaryness */
-               value = attr_diff_check.value;
-               if (ATTR_TRUE(value))
-                       ;
-               else if (ATTR_FALSE(value))
-                       one->is_binary = 1;
-               else
-                       check_from_data = 1;
-
-               /* funcname pattern ident */
-               if (ATTR_TRUE(value) || ATTR_FALSE(value) || ATTR_UNSET(value))
-                       ;
-               else
-                       one->funcname_pattern_ident = value;
-       }
-
-       if (check_from_data) {
-               if (!one->data && DIFF_FILE_VALID(one))
-                       diff_populate_filespec(one, 0);
-
-               if (one->data)
-                       one->is_binary = buffer_is_binary(one->data, one->size);
-       }
+       if (!one->driver)
+               one->driver = userdiff_find_by_path(one->path);
+       if (!one->driver)
+               one->driver = userdiff_find_by_name("default");
 }
 
 int diff_filespec_is_binary(struct diff_filespec *one)
 {
-       diff_filespec_check_attr(one);
+       if (one->is_binary == -1) {
+               diff_filespec_load_driver(one);
+               if (one->driver->binary != -1)
+                       one->is_binary = one->driver->binary;
+               else {
+                       if (!one->data && DIFF_FILE_VALID(one))
+                               diff_populate_filespec(one, 0);
+                       if (one->data)
+                               one->is_binary = buffer_is_binary(one->data,
+                                               one->size);
+                       if (one->is_binary == -1)
+                               one->is_binary = 0;
+               }
+       }
        return one->is_binary;
 }
 
-static const struct funcname_pattern_entry *funcname_pattern(const char *ident)
-{
-       struct funcname_pattern_list *pp;
-
-       for (pp = funcname_pattern_list; pp; pp = pp->next)
-               if (!strcmp(ident, pp->e.name))
-                       return &pp->e;
-       return NULL;
-}
-
-static const struct funcname_pattern_entry builtin_funcname_pattern[] = {
-       { "bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
-         REG_EXTENDED },
-       { "html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", REG_EXTENDED },
-       { "java",
-         "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
-         "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$",
-         REG_EXTENDED },
-       { "objc",
-         /* Negate C statements that can look like functions */
-         "!^[ \t]*(do|for|if|else|return|switch|while)\n"
-         /* Objective-C methods */
-         "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
-         /* C functions */
-         "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
-         /* Objective-C class/protocol definitions */
-         "^(@(implementation|interface|protocol)[ \t].*)$",
-         REG_EXTENDED },
-       { "pascal",
-         "^((procedure|function|constructor|destructor|interface|"
-               "implementation|initialization|finalization)[ \t]*.*)$"
-         "\n"
-         "^(.*=[ \t]*(class|record).*)$",
-         REG_EXTENDED },
-       { "php", "^[\t ]*((function|class).*)", REG_EXTENDED },
-       { "python", "^[ \t]*((class|def)[ \t].*)$", REG_EXTENDED },
-       { "ruby", "^[ \t]*((class|module|def)[ \t].*)$",
-         REG_EXTENDED },
-       { "tex",
-         "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
-         REG_EXTENDED },
-};
-
-static const struct funcname_pattern_entry *diff_funcname_pattern(struct diff_filespec *one)
+static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
 {
-       const char *ident;
-       const struct funcname_pattern_entry *pe;
-       int i;
-
-       diff_filespec_check_attr(one);
-       ident = one->funcname_pattern_ident;
-
-       if (!ident)
-               /*
-                * If the config file has "funcname.default" defined, that
-                * regexp is used; otherwise NULL is returned and xemit uses
-                * the built-in default.
-                */
-               return funcname_pattern("default");
-
-       /* Look up custom "funcname.$ident" regexp from config. */
-       pe = funcname_pattern(ident);
-       if (pe)
-               return pe;
-
-       /*
-        * And define built-in fallback patterns here.  Note that
-        * these can be overridden by the user's config settings.
-        */
-       for (i = 0; i < ARRAY_SIZE(builtin_funcname_pattern); i++)
-               if (!strcmp(ident, builtin_funcname_pattern[i].name))
-                       return &builtin_funcname_pattern[i];
-
-       return NULL;
+       diff_filespec_load_driver(one);
+       return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
 }
 
 void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const char *b)
@@ -1492,6 +1308,16 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
                options->b_prefix = b;
 }
 
+static const char *get_textconv(struct diff_filespec *one)
+{
+       if (!DIFF_FILE_VALID(one))
+               return NULL;
+       if (!S_ISREG(one->mode))
+               return NULL;
+       diff_filespec_load_driver(one);
+       return one->driver->textconv;
+}
+
 static void builtin_diff(const char *name_a,
                         const char *name_b,
                         struct diff_filespec *one,
@@ -1506,6 +1332,7 @@ static void builtin_diff(const char *name_a,
        const char *set = diff_get_color_opt(o, DIFF_METAINFO);
        const char *reset = diff_get_color_opt(o, DIFF_RESET);
        const char *a_prefix, *b_prefix;
+       const char *textconv_one = NULL, *textconv_two = NULL;
 
        diff_set_mnemonic_prefix(o, "a/", "b/");
        if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
@@ -1559,8 +1386,14 @@ static void builtin_diff(const char *name_a,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
 
+       if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
+               textconv_one = get_textconv(one);
+               textconv_two = get_textconv(two);
+       }
+
        if (!DIFF_OPT_TST(o, TEXT) &&
-           (diff_filespec_is_binary(one) || diff_filespec_is_binary(two))) {
+           ( (diff_filespec_is_binary(one) && !textconv_one) ||
+             (diff_filespec_is_binary(two) && !textconv_two) )) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1579,12 +1412,28 @@ static void builtin_diff(const char *name_a,
                xdemitconf_t xecfg;
                xdemitcb_t ecb;
                struct emit_callback ecbdata;
-               const struct funcname_pattern_entry *pe;
+               const struct userdiff_funcname *pe;
+
+               if (textconv_one) {
+                       size_t size;
+                       mf1.ptr = run_textconv(textconv_one, one, &size);
+                       if (!mf1.ptr)
+                               die("unable to read files to diff");
+                       mf1.size = size;
+               }
+               if (textconv_two) {
+                       size_t size;
+                       mf2.ptr = run_textconv(textconv_two, two, &size);
+                       if (!mf2.ptr)
+                               die("unable to read files to diff");
+                       mf2.size = size;
+               }
 
                pe = diff_funcname_pattern(one);
                if (!pe)
                        pe = diff_funcname_pattern(two);
 
+               memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                memset(&ecbdata, 0, sizeof(ecbdata));
                ecbdata.label_path = lbl;
@@ -1612,6 +1461,10 @@ static void builtin_diff(const char *name_a,
                              &xpp, &xecfg, &ecb);
                if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
                        free_diff_words_data(&ecbdata);
+               if (textconv_one)
+                       free(mf1.ptr);
+               if (textconv_two)
+                       free(mf2.ptr);
        }
 
  free_ab_and_return:
@@ -1658,6 +1511,7 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
                xdemitconf_t xecfg;
                xdemitcb_t ecb;
 
+               memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
                xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
@@ -1704,6 +1558,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
                xdemitconf_t xecfg;
                xdemitcb_t ecb;
 
+               memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                xecfg.ctxlen = 1; /* at least one context line */
                xpp.flags = XDF_NEED_MINIMAL;
@@ -1733,6 +1588,7 @@ struct diff_filespec *alloc_filespec(const char *path)
        spec->path = (char *)(spec + 1);
        memcpy(spec->path, path, namelen+1);
        spec->count = 1;
+       spec->is_binary = -1;
        return spec;
 }
 
@@ -2117,29 +1973,6 @@ static void run_external_diff(const char *pgm,
        }
 }
 
-static const char *external_diff_attr(const char *name)
-{
-       struct git_attr_check attr_diff_check;
-
-       if (!name)
-               return NULL;
-
-       setup_diff_attr_check(&attr_diff_check);
-       if (!git_checkattr(name, 1, &attr_diff_check)) {
-               const char *value = attr_diff_check.value;
-               if (!ATTR_TRUE(value) &&
-                   !ATTR_FALSE(value) &&
-                   !ATTR_UNSET(value)) {
-                       struct ll_diff_driver *drv;
-
-                       for (drv = user_diff; drv; drv = drv->next)
-                               if (!strcmp(drv->name, value))
-                                       return drv->cmd;
-               }
-       }
-       return NULL;
-}
-
 static void run_diff_cmd(const char *pgm,
                         const char *name,
                         const char *other,
@@ -2153,9 +1986,9 @@ static void run_diff_cmd(const char *pgm,
        if (!DIFF_OPT_TST(o, ALLOW_EXTERNAL))
                pgm = NULL;
        else {
-               const char *cmd = external_diff_attr(attr_path);
-               if (cmd)
-                       pgm = cmd;
+               struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
+               if (drv && drv->external)
+                       pgm = drv->external;
        }
 
        if (pgm) {
@@ -3149,6 +2982,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                struct diff_filepair *p = q->queue[i];
                int len1, len2;
 
+               memset(&xpp, 0, sizeof(xpp));
                memset(&xecfg, 0, sizeof(xecfg));
                if (p->status == 0)
                        return error("internal diff status error");
@@ -3578,3 +3412,34 @@ void diff_unmerge(struct diff_options *options,
        fill_filespec(one, sha1, mode);
        diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
 }
+
+static char *run_textconv(const char *pgm, struct diff_filespec *spec,
+               size_t *outsize)
+{
+       struct diff_tempfile temp;
+       const char *argv[3];
+       const char **arg = argv;
+       struct child_process child;
+       struct strbuf buf = STRBUF_INIT;
+
+       prepare_temp_file(spec->path, &temp, spec);
+       *arg++ = pgm;
+       *arg++ = temp.name;
+       *arg = NULL;
+
+       memset(&child, 0, sizeof(child));
+       child.argv = argv;
+       child.out = -1;
+       if (start_command(&child) != 0 ||
+           strbuf_read(&buf, child.out, 0) < 0 ||
+           finish_command(&child) != 0) {
+               if (temp.name == temp.tmp_path)
+                       unlink(temp.name);
+               error("error running textconv command '%s'", pgm);
+               return NULL;
+       }
+       if (temp.name == temp.tmp_path)
+               unlink(temp.name);
+
+       return strbuf_detach(&buf, outsize);
+}
diff --git a/diff.h b/diff.h
index a49d865bd9cb0fa5ff27ccad7049074afb0002e9..42582edee68a4a4717ae5debebf37e6b9610fc8f 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -65,6 +65,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_OPT_IGNORE_SUBMODULES   (1 << 18)
 #define DIFF_OPT_DIRSTAT_CUMULATIVE  (1 << 19)
 #define DIFF_OPT_DIRSTAT_BY_FILE     (1 << 20)
+#define DIFF_OPT_ALLOW_TEXTCONV      (1 << 21)
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
 #define DIFF_OPT_CLR(opts, flag)    ((opts)->flags &= ~DIFF_OPT_##flag)
index 1b2ebb40014d820fe4fb679509ab694d453be7b4..168a95b541c2d6a4679115ebc9f30b1016645b19 100644 (file)
@@ -493,7 +493,7 @@ void diffcore_rename(struct diff_options *options)
        if ((num_create > rename_limit && num_src > rename_limit) ||
            (num_create * num_src > rename_limit * rename_limit)) {
                if (options->warn_on_too_large_rename)
-                       warning("too many files, skipping inexact rename detection");
+                       warning("too many files (created: %d deleted: %d), skipping inexact rename detection", num_create, num_src);
                goto cleanup;
        }
 
index 8ae35785fd4439f466620ab9186838e7cb20fa36..5b634585e8970de81067b9807d8813c083b86b0a 100644 (file)
@@ -22,6 +22,8 @@
 
 #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */
 
+struct userdiff_driver;
+
 struct diff_filespec {
        unsigned char sha1[20];
        char *path;
@@ -40,8 +42,10 @@ struct diff_filespec {
 #define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
        unsigned should_free : 1; /* data should be free()'ed */
        unsigned should_munmap : 1; /* data should be munmap()'ed */
-       unsigned checked_attr : 1;
-       unsigned is_binary : 1; /* data should be considered "binary" */
+
+       struct userdiff_driver *driver;
+       /* data should be considered "binary"; -1 means "don't know yet" */
+       int is_binary;
 };
 
 extern struct diff_filespec *alloc_filespec(const char *);
@@ -58,7 +62,7 @@ struct diff_filepair {
        struct diff_filespec *one;
        struct diff_filespec *two;
        unsigned short int score;
-       char status; /* M C R N D U (see Documentation/diff-format.txt) */
+       char status; /* M C R A D U etc. (see Documentation/diff-format.txt or DIFF_STATUS_* in diff.h) */
        unsigned broken_pair : 1;
        unsigned renamed_pair : 1;
        unsigned is_unmerged : 1;
index 0693cd9a42adf4914bb219ecf24fc6d5da7aafd3..bb96ac0a71bc5614cd3352902586ff0a70a4eece 100644 (file)
@@ -71,7 +71,7 @@ static void setup_git_env(void)
        }
        git_graft_file = getenv(GRAFT_ENVIRONMENT);
        if (!git_graft_file)
-               git_graft_file = xstrdup(git_path("info/grafts"));
+               git_graft_file = git_pathdup("info/grafts");
 }
 
 int is_bare_repository(void)
@@ -118,7 +118,7 @@ const char *get_git_work_tree(void)
                        work_tree = git_work_tree_cfg;
                        /* make_absolute_path also normalizes the path */
                        if (work_tree && !is_absolute_path(work_tree))
-                               work_tree = xstrdup(make_absolute_path(git_path(work_tree)));
+                               work_tree = xstrdup(make_absolute_path(git_path("%s", work_tree)));
                } else if (work_tree)
                        work_tree = xstrdup(make_absolute_path(work_tree));
                git_work_tree_initialized = 1;
diff --git a/fsck.c b/fsck.c
index 0cf5f012bdfa876fae4e15ed9cb94ed304314bf6..97f76c58155249412d7c59e965b564dfc0f75181 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -326,7 +326,7 @@ int fsck_error_function(struct object *obj, int type, const char *fmt, ...)
                        die("this should not happen, your snprintf is broken");
        }
 
-       error(sb.buf);
+       error("%s", sb.buf);
        strbuf_release(&sb);
        return 1;
 }
index da768ee7acc22e6480f0a067e109239561d43927..b0223c3419301132032fb67519a275e57707df22 100755 (executable)
@@ -811,11 +811,16 @@ sub help_patch_cmd {
 }
 
 sub patch_update_cmd {
-       my @mods = grep { !($_->{BINARY}) } list_modified('file-only');
+       my @all_mods = list_modified('file-only');
+       my @mods = grep { !($_->{BINARY}) } @all_mods;
        my @them;
 
        if (!@mods) {
-               print STDERR "No changes.\n";
+               if (@all_mods) {
+                       print STDERR "Only binary files changed.\n";
+               } else {
+                       print STDERR "No changes.\n";
+               }
                return 0;
        }
        if ($patch_mode) {
index 79de7017e88b746971f5a730b8615290bba2890d..0d0e278c92c39f17b368eb6ead781a877bc8caed 100755 (executable)
@@ -455,7 +455,7 @@ bisect_next() {
        good=$(git for-each-ref --format='^%(objectname)' \
                "refs/bisect/good-*" | tr '\012' ' ') &&
        skip=$(git for-each-ref --format='%(objectname)' \
-               "refs/bisect/skip-*" | tr '\012' ' ') &&
+               "refs/bisect/skip-*" | tr '\012' ' ') || exit
 
        # Maybe some merge bases must be tested first
        check_good_are_ancestors_of_bad "$bad" "$good" "$skip"
index 55765c8a3aa6b3702b230e8efe3c2ab47a6e73e5..3ad8a21b30128ce21b6b6c28094d9af5e5866faf 100644 (file)
@@ -285,6 +285,7 @@ all:: $(GITGUI_MAIN) lib/tclIndex $(ALL_MSGFILES)
 install: all
        $(QUIET)$(INSTALL_D0)'$(DESTDIR_SQ)$(gitexecdir_SQ)' $(INSTALL_D1)
        $(QUIET)$(INSTALL_X0)git-gui $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
+       $(QUIET)$(INSTALL_X0)git-gui--askpass $(INSTALL_X1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(INSTALL_L0)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L1)'$(DESTDIR_SQ)$(gitexecdir_SQ)/git-gui' $(INSTALL_L2)'$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' $(INSTALL_L3) &&) true
 ifdef GITGUI_WINDOWS_WRAPPER
        $(QUIET)$(INSTALL_R0)git-gui.tcl $(INSTALL_R1) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
@@ -302,6 +303,7 @@ endif
 uninstall:
        $(QUIET)$(CLEAN_DST) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
        $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui $(REMOVE_F1)
+       $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui--askpass $(REMOVE_F1)
        $(QUIET)$(foreach p,$(GITGUI_BUILT_INS), $(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/$p $(REMOVE_F1) &&) true
 ifdef GITGUI_WINDOWS_WRAPPER
        $(QUIET)$(REMOVE_F0)'$(DESTDIR_SQ)$(gitexecdir_SQ)'/git-gui.tcl $(REMOVE_F1)
diff --git a/git-gui/git-gui--askpass b/git-gui/git-gui--askpass
new file mode 100755 (executable)
index 0000000..12e117e
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+# Tcl ignores the next line -*- tcl -*- \
+exec wish "$0" -- "$@"
+
+# This is a trivial implementation of an SSH_ASKPASS handler.
+# Git-gui uses this script if none are already configured.
+
+set answer {}
+set yesno  0
+set rc     255
+
+if {$argc < 1} {
+       set prompt "Enter your OpenSSH passphrase:"
+} else {
+       set prompt [join $argv " "]
+       if {[regexp -nocase {\(yes\/no\)\?\s*$} $prompt]} {
+               set yesno 1
+       }
+}
+
+message .m -text $prompt -justify center -aspect 4000
+pack .m -side top -fill x -padx 20 -pady 20 -expand 1
+
+entry .e -textvariable answer -width 50
+pack .e -side top -fill x -padx 10 -pady 10
+
+if {!$yesno} {
+       .e configure -show "*"
+}
+
+frame .b
+button .b.ok     -text OK     -command finish
+button .b.cancel -text Cancel -command {destroy .}
+
+pack .b.ok -side left -expand 1
+pack .b.cancel -side right -expand 1
+pack .b -side bottom -fill x -padx 10 -pady 10
+
+bind . <Visibility> {focus -force .e}
+bind . <Key-Return> finish
+bind . <Key-Escape> {destroy .}
+bind . <Destroy>    {exit $rc}
+
+proc finish {} {
+       if {$::yesno} {
+               if {$::answer ne "yes" && $::answer ne "no"} {
+                       tk_messageBox -icon error -title "Error" -type ok \
+                               -message "Only 'yes' or 'no' input allowed."
+                       return
+               }
+       }
+
+       set ::rc 0
+       puts $::answer
+       destroy .
+}
+
+wm title . "OpenSSH"
+tk::PlaceWindow .
index 4085e8fea59f2b819812f20185df89379d851c0b..cf9ef6ee07244b28ab7725aec8fb595ee613644a 100755 (executable)
@@ -591,6 +591,12 @@ bind . <Visibility> {
 
 if {[is_Windows]} {
        wm iconbitmap . -default $oguilib/git-gui.ico
+       set ::tk::AlwaysShowSelection 1
+
+       # Spoof an X11 display for SSH
+       if {![info exists env(DISPLAY)]} {
+               set env(DISPLAY) :9999
+       }
 }
 
 ######################################################################
@@ -991,10 +997,22 @@ citool {
 }
 }
 
+######################################################################
+##
+## execution environment
+
+set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
+
+# Suggest our implementation of askpass, if none is set
+if {![info exists env(SSH_ASKPASS)]} {
+       set env(SSH_ASKPASS) [gitexec git-gui--askpass]
+}
+
 ######################################################################
 ##
 ## repository setup
 
+set picked 0
 if {[catch {
                set _gitdir $env(GIT_DIR)
                set _prefix {}
@@ -1006,6 +1024,7 @@ if {[catch {
        load_config 1
        apply_config
        choose_repository::pick
+       set picked 1
 }
 if {![file isdirectory $_gitdir] && [is_Cygwin]} {
        catch {set _gitdir [exec cygpath --windows $_gitdir]}
@@ -1869,6 +1888,19 @@ proc do_gitk {revs} {
        }
 }
 
+proc do_explore {} {
+       set explorer {}
+       if {[is_Cygwin] || [is_Windows]} {
+               set explorer "explorer.exe"
+       } elseif {[is_MacOSX]} {
+               set explorer "open"
+       } else {
+               # freedesktop.org-conforming system is our best shot
+               set explorer "xdg-open"
+       }
+       eval exec $explorer [file dirname [gitdir]] &
+}
+
 set is_quitting 0
 set ret_code    1
 
@@ -2090,7 +2122,9 @@ proc toggle_or_diff {w x y} {
        if {$col == 0 && $y > 1} {
                # Conflicts need special handling
                if {[string first {U} $state] >= 0} {
-                       merge_stage_workdir $path $w $lno
+                       # $w must always be $ui_workdir, but...
+                       if {$w ne $ui_workdir} { set lno {} }
+                       merge_stage_workdir $path $lno
                        return
                }
 
@@ -2218,6 +2252,11 @@ if {[is_enabled transport]} {
 #
 menu .mbar.repository
 
+.mbar.repository add command \
+       -label [mc "Explore Working Copy"] \
+       -command {do_explore}
+.mbar.repository add separator
+
 .mbar.repository add command \
        -label [mc "Browse Current Branch's Files"] \
        -command {browser::new $current_branch}
@@ -2413,7 +2452,7 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
 
        .mbar.commit add separator
 
-       if {![is_enabled nocommit]} {
+       if {![is_enabled nocommitmsg]} {
                .mbar.commit add command -label [mc "Sign Off"] \
                        -command do_signoff \
                        -accelerator $M1T-S
@@ -2446,12 +2485,16 @@ if {[is_enabled branch]} {
 if {[is_enabled transport]} {
        menu .mbar.remote
 
+       .mbar.remote add command \
+               -label [mc "Add..."] \
+               -command remote_add::dialog \
+               -accelerator $M1T-A
        .mbar.remote add command \
                -label [mc "Push..."] \
                -command do_push_anywhere \
                -accelerator $M1T-P
        .mbar.remote add command \
-               -label [mc "Delete..."] \
+               -label [mc "Delete Branch..."] \
                -command remote_branch_delete::dialog
 }
 
@@ -2487,8 +2530,7 @@ if {![is_MacOSX]} {
                -command do_about
 }
 
-set browser {}
-catch {set browser $repo_config(instaweb.browser)}
+
 set doc_path [file dirname [gitexec]]
 set doc_path [file join $doc_path Documentation index.html]
 
@@ -2496,34 +2538,23 @@ if {[is_Cygwin]} {
        set doc_path [exec cygpath --mixed $doc_path]
 }
 
-if {$browser eq {}} {
-       if {[is_MacOSX]} {
-               set browser open
-       } elseif {[is_Cygwin]} {
-               set program_files [file dirname [exec cygpath --windir]]
-               set program_files [file join $program_files {Program Files}]
-               set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
-               set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
-               if {[file exists $firefox]} {
-                       set browser $firefox
-               } elseif {[file exists $ie]} {
-                       set browser $ie
-               }
-               unset program_files firefox ie
-       }
-}
-
 if {[file isfile $doc_path]} {
        set doc_url "file:$doc_path"
 } else {
        set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
 }
 
-if {$browser ne {}} {
-       .mbar.help add command -label [mc "Online Documentation"] \
-               -command [list exec $browser $doc_url &]
+proc start_browser {url} {
+       git "web--browse" $url
 }
-unset browser doc_path doc_url
+
+.mbar.help add command -label [mc "Online Documentation"] \
+       -command [list start_browser $doc_url]
+
+.mbar.help add command -label [mc "Show SSH Key"] \
+       -command do_ssh_key
+
+unset doc_path doc_url
 
 # -- Standard bindings
 #
@@ -2743,7 +2774,7 @@ pack .vpane.lower.commarea.buttons.incall -side top -fill x
 lappend disable_on_lock \
        {.vpane.lower.commarea.buttons.incall conf -state}
 
-if {![is_enabled nocommit]} {
+if {![is_enabled nocommitmsg]} {
        button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
                -command do_signoff
        pack .vpane.lower.commarea.buttons.signoff -side top -fill x
@@ -3261,8 +3292,7 @@ if {[is_enabled transport]} {
        load_all_remotes
 
        set n [.mbar.remote index end]
-       populate_push_menu
-       populate_fetch_menu
+       populate_remotes_menu
        set n [expr {[.mbar.remote index end] - $n}]
        if {$n > 0} {
                if {[.mbar.remote type 0] eq "tearoff"} { incr n }
@@ -3369,3 +3399,6 @@ if {[is_enabled multicommit]} {
 if {[is_enabled retcode]} {
        bind . <Destroy> {+terminate_me %W}
 }
+if {$picked && [is_config_true gui.autoexplore]} {
+       do_explore
+}
index eb61374d2db79a1199d455dfdda85e2d063bf1f6..c1cd7f3b92fddb60be9fe261d7b75c5df85b60c1 100644 (file)
@@ -21,9 +21,11 @@ field w_amov     ; # text column: annotations + move tracking
 field w_asim     ; # text column: annotations (simple computation)
 field w_file     ; # text column: actual file data
 field w_cviewer  ; # pane showing commit message
+field finder     ; # find mini-dialog frame
 field status     ; # status mega-widget instance
 field old_height ; # last known height of $w.file_pane
 
+
 # Tk UI colors
 #
 variable active_color #c0edc5
@@ -59,7 +61,7 @@ field tooltip_timer     {} ; # Current timer event for our tooltip
 field tooltip_commit    {} ; # Commit(s) in tooltip
 
 constructor new {i_commit i_path i_jump} {
-       global cursor_ptr
+       global cursor_ptr M1B M1T have_tk85
        variable active_color
        variable group_colors
 
@@ -69,6 +71,8 @@ constructor new {i_commit i_path i_jump} {
        make_toplevel top w
        wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]]
 
+       set font_w [font measure font_diff "0"]
+
        frame $w.header -background gold
        label $w.header.commit_l \
                -text [mc "Commit:"] \
@@ -114,9 +118,9 @@ constructor new {i_commit i_path i_jump} {
        pack $w_path -fill x -side right
        pack $w.header.path_l -side right
 
-       panedwindow $w.file_pane -orient vertical
-       frame $w.file_pane.out
-       frame $w.file_pane.cm
+       panedwindow $w.file_pane -orient vertical -borderwidth 0 -sashwidth 3
+       frame $w.file_pane.out -relief flat -borderwidth 1
+       frame $w.file_pane.cm -relief sunken -borderwidth 1
        $w.file_pane add $w.file_pane.out \
                -sticky nsew \
                -minsize 100 \
@@ -197,6 +201,11 @@ constructor new {i_commit i_path i_jump} {
                -width 80 \
                -xscrollcommand [list $w.file_pane.out.sbx set] \
                -font font_diff
+       if {$have_tk85} {
+               $w_file configure -inactiveselectbackground darkblue
+       }
+       $w_file tag conf found \
+               -background yellow
 
        set w_columns [list $w_amov $w_asim $w_line $w_file]
 
@@ -217,6 +226,11 @@ constructor new {i_commit i_path i_jump} {
                -weight 1
        grid rowconfigure $w.file_pane.out 0 -weight 1
 
+       set finder [::searchbar::new \
+               $w.file_pane.out.ff $w_file \
+               -column [expr {[llength $w_columns] - 1}] \
+               ]
+
        set w_cviewer $w.file_pane.cm.t
        text $w_cviewer \
                -background white \
@@ -257,6 +271,10 @@ constructor new {i_commit i_path i_jump} {
                -label [mc "Copy Commit"] \
                -command [cb _copycommit]
        $w.ctxm add separator
+       $w.ctxm add command \
+               -label [mc "Find Text..."] \
+               -accelerator F7 \
+               -command [list searchbar::show $finder]
        menu $w.ctxm.enc
        build_encoding_menu $w.ctxm.enc [cb _setencoding]
        $w.ctxm add cascade \
@@ -278,9 +296,15 @@ constructor new {i_commit i_path i_jump} {
                        $i tag conf color$g -background [lindex $group_colors $g]
                }
 
+               if {$i eq $w_file} {
+                       $w_file tag raise found
+               }
+               $i tag raise sel
+
                $i conf -cursor $cursor_ptr
-               $i conf -yscrollcommand [list many2scrollbar \
-                       $w_columns yview $w.file_pane.out.sby]
+               $i conf -yscrollcommand \
+                       "[list ::searchbar::scrolled $finder]
+                        [list many2scrollbar $w_columns yview $w.file_pane.out.sby]"
                bind $i <Button-1> "
                        [cb _hide_tooltip]
                        [cb _click $i @%x,%y]
@@ -297,7 +321,7 @@ constructor new {i_commit i_path i_jump} {
                        tk_popup $w.ctxm %X %Y
                "
                bind $i <Shift-Tab> "[list focus $w_cviewer];break"
-               bind $i <Tab>       "[list focus $w_cviewer];break"
+               bind $i <Tab>       "[cb _focus_search $w_cviewer];break"
        }
 
        foreach i [concat $w_columns $w_cviewer] {
@@ -313,10 +337,15 @@ constructor new {i_commit i_path i_jump} {
                bind $i <Control-Key-f> {catch {%W yview scroll  1 pages};break}
        }
 
-       bind $w_cviewer <Shift-Tab> "[list focus $w_file];break"
+       bind $w_cviewer <Shift-Tab> "[cb _focus_search $w_file];break"
        bind $w_cviewer <Tab>       "[list focus $w_file];break"
-       bind $w_cviewer <Button-1> [list focus $w_cviewer]
-       bind $w_file    <Visibility> [list focus $w_file]
+       bind $w_cviewer <Button-1>   [list focus $w_cviewer]
+       bind $w_file    <Visibility> [cb _focus_search $w_file]
+       bind $top       <F7>         [list searchbar::show $finder]
+       bind $top       <Escape>     [list searchbar::hide $finder]
+       bind $top       <F3>         [list searchbar::find_next $finder]
+       bind $top       <Shift-F3>   [list searchbar::find_prev $finder]
+       catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] }
 
        grid configure $w.header -sticky ew
        grid configure $w.file_pane -sticky nsew
@@ -328,9 +357,14 @@ constructor new {i_commit i_path i_jump} {
 
        set req_w [winfo reqwidth  $top]
        set req_h [winfo reqheight $top]
-       set scr_h [expr {[winfo screenheight $top] - 100}]
-       if {$req_w < 600} {set req_w 600}
+       set scr_w [expr {[winfo screenwidth $top] - 40}]
+       set scr_h [expr {[winfo screenheight $top] - 120}]
+       set opt_w [expr {$font_w * (80 + 5*3 + 3)}]
+       if {$req_w < $opt_w} {set req_w $opt_w}
+       if {$req_w > $scr_w} {set req_w $scr_w}
+       set opt_h [expr {$req_w*4/3}]
        if {$req_h < $scr_h} {set req_h $scr_h}
+       if {$req_h > $opt_h} {set req_h $opt_h}
        set g "${req_w}x${req_h}"
        wm geometry $top $g
        update
@@ -338,16 +372,31 @@ constructor new {i_commit i_path i_jump} {
        set old_height [winfo height $w.file_pane]
        $w.file_pane sash place 0 \
                [lindex [$w.file_pane sash coord 0] 0] \
-               [expr {int($old_height * 0.70)}]
+               [expr {int($old_height * 0.80)}]
        bind $w.file_pane <Configure> \
        "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
 
        wm protocol $top WM_DELETE_WINDOW "destroy $top"
-       bind $top <Destroy> [cb _kill]
+       bind $top <Destroy> [cb _handle_destroy %W]
 
        _load $this $i_jump
 }
 
+method _focus_search {win} {
+       if {[searchbar::visible $finder]} {
+               focus [searchbar::editor $finder]
+       } else {
+               focus $win
+       }
+}
+
+method _handle_destroy {win} {
+       if {$win eq $w} {
+               _kill $this
+               delete_this
+       }
+}
+
 method _kill {} {
        if {$current_fd ne {}} {
                kill_file_process $current_fd
@@ -510,7 +559,7 @@ method _read_file {fd jump} {
 } ifdeleted { catch {close $fd} }
 
 method _exec_blame {cur_w cur_d options cur_s} {
-       lappend options --incremental
+       lappend options --incremental --encoding=utf-8
        if {$commit eq {}} {
                lappend options --contents $path
        } else {
@@ -866,6 +915,10 @@ method _showcommit {cur_w lno} {
                foreach i $w_columns {
                        $i tag conf g$cmit -background $active_color
                        $i tag raise g$cmit
+                       if {$i eq $w_file} {
+                               $w_file tag raise found
+                       }
+                       $i tag raise sel
                }
 
                set author_name {}
index 318078615862adab052d0d0f637f82176a0a785a..f9ff62a3b22fcc481c88dc35268d06d2fc4a5795 100644 (file)
@@ -43,12 +43,18 @@ constructor pick {} {
                        $w.mbar.apple add command \
                                -label [mc "About %s" [appname]] \
                                -command do_about
+                       $w.mbar.apple add command \
+                               -label [mc "Show SSH Key"] \
+                               -command do_ssh_key
                } else {
                        $w.mbar add cascade -label [mc Help] -menu $w.mbar.help
                        menu $w.mbar.help
                        $w.mbar.help add command \
                                -label [mc "About %s" [appname]] \
                                -command do_about
+                       $w.mbar.help add command \
+                               -label [mc "Show SSH Key"] \
+                               -command do_ssh_key
                }
 
                wm protocol $top WM_DELETE_WINDOW exit
@@ -381,7 +387,8 @@ method _do_new {} {
        label $w_body.where.l -text [mc "Directory:"]
        entry $w_body.where.t \
                -textvariable @local_path \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $w_body.where.b \
                -text [mc "Browse"] \
@@ -463,20 +470,22 @@ method _do_clone {} {
        frame $w_body.args
        pack $args -fill both
 
-       label $args.origin_l -text [mc "URL:"]
+       label $args.origin_l -text [mc "Source Location:"]
        entry $args.origin_t \
                -textvariable @origin_url \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $args.origin_b \
                -text [mc "Browse"] \
                -command [cb _open_origin]
        grid $args.origin_l $args.origin_t $args.origin_b -sticky ew
 
-       label $args.where_l -text [mc "Directory:"]
+       label $args.where_l -text [mc "Target Directory:"]
        entry $args.where_t \
                -textvariable @local_path \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $args.where_b \
                -text [mc "Browse"] \
@@ -979,7 +988,8 @@ method _do_open {} {
        label $w_body.where.l -text [mc "Repository:"]
        entry $w_body.where.t \
                -textvariable @local_path \
-               -font font_diff \
+               -borderwidth 1 \
+               -relief sunken \
                -width 50
        button $w_body.where.b \
                -text [mc "Browse"] \
index abe502d97104cb62bed308b0f5025c9d014834fb..94ee38cccc1ee9a43f8dd1309353609b283d8af5 100644 (file)
@@ -117,22 +117,22 @@ proc show_unmerged_diff {cont_info} {
        if {$merge_stages(2) eq {}} {
                set is_conflict_diff 1
                lappend current_diff_queue \
-                       [list "LOCAL: deleted\nREMOTE:\n" d======= \
+                       [list [mc "LOCAL: deleted\nREMOTE:\n"] d======= \
                            [list ":1:$current_diff_path" ":3:$current_diff_path"]]
        } elseif {$merge_stages(3) eq {}} {
                set is_conflict_diff 1
                lappend current_diff_queue \
-                       [list "REMOTE: deleted\nLOCAL:\n" d======= \
+                       [list [mc "REMOTE: deleted\nLOCAL:\n"] d======= \
                            [list ":1:$current_diff_path" ":2:$current_diff_path"]]
        } elseif {[lindex $merge_stages(1) 0] eq {120000}
                || [lindex $merge_stages(2) 0] eq {120000}
                || [lindex $merge_stages(3) 0] eq {120000}} {
                set is_conflict_diff 1
                lappend current_diff_queue \
-                       [list "LOCAL:\n" d======= \
+                       [list [mc "LOCAL:\n"] d======= \
                            [list ":1:$current_diff_path" ":2:$current_diff_path"]]
                lappend current_diff_queue \
-                       [list "REMOTE:\n" d======= \
+                       [list [mc "REMOTE:\n"] d======= \
                            [list ":1:$current_diff_path" ":3:$current_diff_path"]]
        } else {
                start_show_diff $cont_info
@@ -164,7 +164,7 @@ proc show_other_diff {path w m cont_info} {
        # - Git won't give us the diff, there's nothing to compare to!
        #
        if {$m eq {_O}} {
-               set max_sz [expr {128 * 1024}]
+               set max_sz 100000
                set type unknown
                if {[catch {
                                set type [file type $path]
@@ -218,17 +218,17 @@ proc show_other_diff {path w m cont_info} {
                                d_@
                } else {
                        if {$sz > $max_sz} {
-                               $ui_diff insert end \
-"* Untracked file is $sz bytes.
-* Showing only first $max_sz bytes.
-" d_@
+                               $ui_diff insert end [mc \
+"* Untracked file is %d bytes.
+* Showing only first %d bytes.
+" $sz $max_sz] d_@
                        }
                        $ui_diff insert end $content
                        if {$sz > $max_sz} {
-                               $ui_diff insert end "
-* Untracked file clipped here by [appname].
+                               $ui_diff insert end [mc "
+* Untracked file clipped here by %s.
 * To see the entire file, use an external editor.
-" d_@
+" [appname]] d_@
                        }
                }
                $ui_diff conf -state disabled
@@ -377,7 +377,6 @@ proc read_diff {fd cont_info} {
                        {+} {
                                if {[regexp {^\+([<>]{7} |={7})} $line _g op]} {
                                        set is_conflict_diff 1
-                                       set line [string replace $line 0 0 { }]
                                        set tags d$op
                                } else {
                                        set tags d_+
index b045219a1cca6bf46218bf211c0c88b8d699e144..d33896a0ce26dd34c8024e21c71123e62832b8c0 100644 (file)
@@ -298,11 +298,18 @@ proc add_helper {txt paths} {
        set after {}
        foreach path $paths {
                switch -glob -- [lindex $file_states($path) 0] {
+               _U -
+               U? {
+                       if {$path eq $current_diff_path} {
+                               unlock_index
+                               merge_stage_workdir $path
+                               return
+                       }
+               }
                _O -
                ?M -
                ?D -
-               ?T -
-               U? {
+               ?T {
                        lappend pathList $path
                        if {$path eq $current_diff_path} {
                                set after {reshow_diff;}
index 5c01875b051305b5b40a17ac7a2245f69081f41b..283e4915e928df0c188d8ed2ec49e49eeed3b519 100644 (file)
@@ -40,6 +40,7 @@ The rescan will be automatically started now.
                _O {
                        continue; # and pray it works!
                }
+               _U -
                U? {
                        error_popup [mc "You are in the middle of a conflicted merge.
 
index 6ab5701d937b9a719570907e745609b60d1d56a8..eb2b4b56a4db4c20727432c7a71d5192d580ce9e 100644 (file)
@@ -23,13 +23,14 @@ This operation can be undone only by restarting the merge." \
        }
 }
 
-proc merge_stage_workdir {path w lno} {
+proc merge_stage_workdir {path {lno {}}} {
        global current_diff_path diff_active
+       global current_diff_side ui_workdir
 
        if {$diff_active} return
 
-       if {$path ne $current_diff_path} {
-               show_diff $path $w $lno {} [list do_merge_stage_workdir $path]
+       if {$path ne $current_diff_path || $ui_workdir ne $current_diff_side} {
+               show_diff $path $ui_workdir $lno {} [list do_merge_stage_workdir $path]
        } else {
                do_merge_stage_workdir $path
        }
@@ -375,14 +376,6 @@ proc merge_tool_finish {fd} {
                }
        }
 
-       # Check the modification time of the target file
-       if {!$failed && [file mtime $mtool_target] eq $mtool_mtime} {
-               if {[ask_popup [mc "File %s unchanged, still accept as resolved?" \
-                               [short_path $mtool_target]]] ne {yes}} {
-                       set failed 1
-               }
-       }
-
        # Finish
        if {$failed} {
                file rename -force -- $backup $mtool_target
@@ -395,6 +388,6 @@ proc merge_tool_finish {fd} {
 
                delete_temp_files $mtool_tmpfiles
 
-               merge_add_resolution $mtool_target
+               reshow_diff
        }
 }
index 0e86ddac0981fbb575a7dd5294ddaed29f7c3917..b92b429cf766d525402047175ed0a69af015c682 100644 (file)
@@ -132,91 +132,145 @@ proc load_all_remotes {} {
        set all_remotes [lsort -unique $all_remotes]
 }
 
-proc populate_fetch_menu {} {
-       global all_remotes repo_config
-
+proc add_fetch_entry {r} {
+       global repo_config
        set remote_m .mbar.remote
        set fetch_m $remote_m.fetch
        set prune_m $remote_m.prune
-
-       foreach r $all_remotes {
-               set enable 0
-               if {![catch {set a $repo_config(remote.$r.url)}]} {
-                       if {![catch {set a $repo_config(remote.$r.fetch)}]} {
-                               set enable 1
-                       }
-               } else {
-                       catch {
-                               set fd [open [gitdir remotes $r] r]
-                               while {[gets $fd n] >= 0} {
-                                       if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
-                                               set enable 1
-                                               break
-                                       }
+       set remove_m $remote_m.remove
+       set enable 0
+       if {![catch {set a $repo_config(remote.$r.url)}]} {
+               if {![catch {set a $repo_config(remote.$r.fetch)}]} {
+                       set enable 1
+               }
+       } else {
+               catch {
+                       set fd [open [gitdir remotes $r] r]
+                       while {[gets $fd n] >= 0} {
+                               if {[regexp {^Pull:[ \t]*([^:]+):} $n]} {
+                                       set enable 1
+                                       break
                                }
-                               close $fd
                        }
+                       close $fd
                }
+       }
 
-               if {$enable} {
-                       if {![winfo exists $fetch_m]} {
-                               menu $prune_m
-                               $remote_m insert 0 cascade \
-                                       -label [mc "Prune from"] \
-                                       -menu $prune_m
-
-                               menu $fetch_m
-                               $remote_m insert 0 cascade \
-                                       -label [mc "Fetch from"] \
-                                       -menu $fetch_m
-                       }
+       if {$enable} {
+               if {![winfo exists $fetch_m]} {
+                       menu $remove_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Remove Remote"] \
+                               -menu $remove_m
+
+                       menu $prune_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Prune from"] \
+                               -menu $prune_m
 
-                       $fetch_m add command \
-                               -label $r \
-                               -command [list fetch_from $r]
-                       $prune_m add command \
-                               -label $r \
-                               -command [list prune_from $r]
+                       menu $fetch_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Fetch from"] \
+                               -menu $fetch_m
                }
+
+               $fetch_m add command \
+                       -label $r \
+                       -command [list fetch_from $r]
+               $prune_m add command \
+                       -label $r \
+                       -command [list prune_from $r]
+               $remove_m add command \
+                       -label $r \
+                       -command [list remove_remote $r]
        }
 }
 
-proc populate_push_menu {} {
-       global all_remotes repo_config
-
+proc add_push_entry {r} {
+       global repo_config
        set remote_m .mbar.remote
        set push_m $remote_m.push
-
-       foreach r $all_remotes {
-               set enable 0
-               if {![catch {set a $repo_config(remote.$r.url)}]} {
-                       if {![catch {set a $repo_config(remote.$r.push)}]} {
-                               set enable 1
-                       }
-               } else {
-                       catch {
-                               set fd [open [gitdir remotes $r] r]
-                               while {[gets $fd n] >= 0} {
-                                       if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
-                                               set enable 1
-                                               break
-                                       }
+       set enable 0
+       if {![catch {set a $repo_config(remote.$r.url)}]} {
+               if {![catch {set a $repo_config(remote.$r.push)}]} {
+                       set enable 1
+               }
+       } else {
+               catch {
+                       set fd [open [gitdir remotes $r] r]
+                       while {[gets $fd n] >= 0} {
+                               if {[regexp {^Push:[ \t]*([^:]+):} $n]} {
+                                       set enable 1
+                                       break
                                }
-                               close $fd
                        }
+                       close $fd
                }
+       }
 
-               if {$enable} {
-                       if {![winfo exists $push_m]} {
-                               menu $push_m
-                               $remote_m insert 0 cascade \
-                                       -label [mc "Push to"] \
-                                       -menu $push_m
-                       }
-
-                       $push_m add command \
-                               -label $r \
-                               -command [list push_to $r]
+       if {$enable} {
+               if {![winfo exists $push_m]} {
+                       menu $push_m
+                       $remote_m insert 0 cascade \
+                               -label [mc "Push to"] \
+                               -menu $push_m
                }
+
+               $push_m add command \
+                       -label $r \
+                       -command [list push_to $r]
+       }
+}
+
+proc populate_remotes_menu {} {
+       global all_remotes
+
+       foreach r $all_remotes {
+               add_fetch_entry $r
+               add_push_entry $r
+       }
+}
+
+proc add_single_remote {name location} {
+       global all_remotes repo_config
+       lappend all_remotes $name
+
+       git remote add $name $location
+
+       # XXX: Better re-read the config so that we will never get out
+       # of sync with git remote implementation?
+       set repo_config(remote.$name.url) $location
+       set repo_config(remote.$name.fetch) "+refs/heads/*:refs/remotes/$name/*"
+
+       add_fetch_entry $name
+       add_push_entry $name
+}
+
+proc delete_from_menu {menu name} {
+       if {[winfo exists $menu]} {
+               $menu delete $name
        }
 }
+
+proc remove_remote {name} {
+       global all_remotes repo_config
+
+       git remote rm $name
+
+       catch {
+               # Missing values are ok
+               unset repo_config(remote.$name.url)
+               unset repo_config(remote.$name.fetch)
+               unset repo_config(remote.$name.push)
+       }
+
+       set i [lsearch -exact all_remotes $name]
+       lreplace all_remotes $i $i
+
+       set remote_m .mbar.remote
+       delete_from_menu $remote_m.fetch $name
+       delete_from_menu $remote_m.prune $name
+       delete_from_menu $remote_m.remove $name
+       # Not all remotes are in the push menu
+       catch { delete_from_menu $remote_m.push $name }
+}
diff --git a/git-gui/lib/remote_add.tcl b/git-gui/lib/remote_add.tcl
new file mode 100644 (file)
index 0000000..fb29422
--- /dev/null
@@ -0,0 +1,191 @@
+# git-gui remote adding support
+# Copyright (C) 2008 Petr Baudis
+
+class remote_add {
+
+field w              ; # widget path
+field w_name         ; # new remote name widget
+field w_loc          ; # new remote location widget
+
+field name         {}; # name of the remote the user has chosen
+field location     {}; # location of the remote the user has chosen
+
+field opt_action fetch; # action to do after registering the remote locally
+
+constructor dialog {} {
+       global repo_config
+
+       make_toplevel top w
+       wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]]
+       if {$top ne {.}} {
+               wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
+       }
+
+       label $w.header -text [mc "Add New Remote"] -font font_uibold
+       pack $w.header -side top -fill x
+
+       frame $w.buttons
+       button $w.buttons.create -text [mc Add] \
+               -default active \
+               -command [cb _add]
+       pack $w.buttons.create -side right
+       button $w.buttons.cancel -text [mc Cancel] \
+               -command [list destroy $w]
+       pack $w.buttons.cancel -side right -padx 5
+       pack $w.buttons -side bottom -fill x -pady 10 -padx 10
+
+       labelframe $w.desc -text [mc "Remote Details"]
+
+       label $w.desc.name_l -text [mc "Name:"]
+       set w_name $w.desc.name_t
+       entry $w_name \
+               -borderwidth 1 \
+               -relief sunken \
+               -width 40 \
+               -textvariable @name \
+               -validate key \
+               -validatecommand [cb _validate_name %d %S]
+       grid $w.desc.name_l $w_name -sticky we -padx {0 5}
+
+       label $w.desc.loc_l -text [mc "Location:"]
+       set w_loc $w.desc.loc_t
+       entry $w_loc \
+               -borderwidth 1 \
+               -relief sunken \
+               -width 40 \
+               -textvariable @location
+       grid $w.desc.loc_l $w_loc -sticky we -padx {0 5}
+
+       grid columnconfigure $w.desc 1 -weight 1
+       pack $w.desc -anchor nw -fill x -pady 5 -padx 5
+
+       labelframe $w.action -text [mc "Further Action"]
+
+       radiobutton $w.action.fetch \
+               -text [mc "Fetch Immediately"] \
+               -value fetch \
+               -variable @opt_action
+       pack $w.action.fetch -anchor nw
+
+       radiobutton $w.action.push \
+               -text [mc "Initialize Remote Repository and Push"] \
+               -value push \
+               -variable @opt_action
+       pack $w.action.push -anchor nw
+
+       radiobutton $w.action.none \
+               -text [mc "Do Nothing Else Now"] \
+               -value none \
+               -variable @opt_action
+       pack $w.action.none -anchor nw
+
+       grid columnconfigure $w.action 1 -weight 1
+       pack $w.action -anchor nw -fill x -pady 5 -padx 5
+
+       bind $w <Visibility> [cb _visible]
+       bind $w <Key-Escape> [list destroy $w]
+       bind $w <Key-Return> [cb _add]\;break
+       tkwait window $w
+}
+
+method _add {} {
+       global repo_config env
+       global M1B
+
+       if {$name eq {}} {
+               tk_messageBox \
+                       -icon error \
+                       -type ok \
+                       -title [wm title $w] \
+                       -parent $w \
+                       -message [mc "Please supply a remote name."]
+               focus $w_name
+               return
+       }
+
+       # XXX: We abuse check-ref-format here, but
+       # that should be ok.
+       if {[catch {git check-ref-format "remotes/$name"}]} {
+               tk_messageBox \
+                       -icon error \
+                       -type ok \
+                       -title [wm title $w] \
+                       -parent $w \
+                       -message [mc "'%s' is not an acceptable remote name." $name]
+               focus $w_name
+               return
+       }
+
+       if {[catch {add_single_remote $name $location}]} {
+               tk_messageBox \
+                       -icon error \
+                       -type ok \
+                       -title [wm title $w] \
+                       -parent $w \
+                       -message [mc "Failed to add remote '%s' of location '%s'." $name $location]
+               focus $w_name
+               return
+       }
+
+       switch -- $opt_action {
+       fetch {
+               set c [console::new \
+                       [mc "fetch %s" $name] \
+                       [mc "Fetching the %s" $name]]
+               console::exec $c [list git fetch $name]
+       }
+       push {
+               set cmds [list]
+
+               # Parse the location
+               if { [regexp {(?:git\+)?ssh://([^/]+)(/.+)} $location xx host path]
+                    || [regexp {([^:][^:]+):(.+)} $location xx host path]} {
+                       set ssh ssh
+                       if {[info exists env(GIT_SSH)]} {
+                               set ssh $env(GIT_SSH)
+                       }
+                       lappend cmds [list exec $ssh $host mkdir -p $location && git --git-dir=$path init --bare]
+               } elseif { ! [regexp {://} $location xx] } {
+                       lappend cmds [list exec mkdir -p $location]
+                       lappend cmds [list exec git --git-dir=$location init --bare]
+               } else {
+                       tk_messageBox \
+                               -icon error \
+                               -type ok \
+                               -title [wm title $w] \
+                               -parent $w \
+                               -message [mc "Do not know how to initialize repository at location '%s'." $location]
+                       destroy $w
+                       return
+               }
+
+               set c [console::new \
+                       [mc "push %s" $name] \
+                       [mc "Setting up the %s (at %s)" $name $location]]
+
+               lappend cmds [list exec git push -v --all $name]
+               console::chain $c $cmds
+       }
+       none {
+       }
+       }
+
+       destroy $w
+}
+
+method _validate_name {d S} {
+       if {$d == 1} {
+               if {[regexp {[~^:?*\[\0- ]} $S]} {
+                       return 0
+               }
+       }
+       return 1
+}
+
+method _visible {} {
+       grab $w
+       $w_name icursor end
+       focus $w_name
+}
+
+}
index c7b81486984d46a9dca59867c406a8e247d76313..89eb0f70f289e48e2b875e2cd49eb026a266ca0e 100644 (file)
@@ -26,12 +26,12 @@ constructor dialog {} {
        global all_remotes M1B
 
        make_toplevel top w
-       wm title $top [append "[appname] ([reponame]): " [mc "Delete Remote Branch"]]
+       wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]]
        if {$top ne {.}} {
                wm geometry $top "+[winfo rootx .]+[winfo rooty .]"
        }
 
-       label $w.header -text [mc "Delete Remote Branch"] -font font_uibold
+       label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold
        pack $w.header -side top -fill x
 
        frame $w.buttons
@@ -63,7 +63,7 @@ constructor dialog {} {
                set urltype url
        }
        radiobutton $w.dest.url_r \
-               -text [mc "Arbitrary URL:"] \
+               -text [mc "Arbitrary Location:"] \
                -value url \
                -variable @urltype
        entry $w.dest.url_t \
diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl
new file mode 100644 (file)
index 0000000..32c8656
--- /dev/null
@@ -0,0 +1,198 @@
+# incremental search panel
+# based on code from gitk, Copyright (C) Paul Mackerras
+
+class searchbar {
+
+field w
+field ctext
+
+field searchstring   {}
+field casesensitive  1
+field searchdirn     -forwards
+
+field smarktop
+field smarkbot
+
+constructor new {i_w i_text args} {
+       set w      $i_w
+       set ctext  $i_text
+
+       frame  $w
+       label  $w.l       -text [mc Find:]
+       entry  $w.ent -textvariable ${__this}::searchstring -background lightgreen
+       button $w.bn      -text [mc Next] -command [cb find_next]
+       button $w.bp      -text [mc Prev] -command [cb find_prev]
+       checkbutton $w.cs -text [mc Case-Sensitive] \
+               -variable ${__this}::casesensitive -command [cb _incrsearch]
+       pack   $w.l   -side left
+       pack   $w.cs  -side right
+       pack   $w.bp  -side right
+       pack   $w.bn  -side right
+       pack   $w.ent -side left -expand 1 -fill x
+
+       eval grid conf $w -sticky we $args
+       grid remove $w
+
+       trace add variable searchstring write [cb _incrsearch_cb]
+       
+       bind $w <Destroy> [cb delete_this]
+       return $this
+}
+
+method show {} {
+       if {![visible $this]} {
+               grid $w
+       }
+       focus -force $w.ent
+}
+
+method hide {} {
+       if {[visible $this]} {
+               focus $ctext
+               grid remove $w
+       }
+}
+
+method visible {} {
+       return [winfo ismapped $w]
+}
+
+method editor {} {
+       return $w.ent
+}
+
+method _get_new_anchor {} {
+       # use start of selection if it is visible,
+       # or the bounds of the visible area
+       set top    [$ctext index @0,0]
+       set bottom [$ctext index @0,[winfo height $ctext]]
+       set sel    [$ctext tag ranges sel]
+       if {$sel ne {}} {
+               set spos [lindex $sel 0]
+               if {[lindex $spos 0] >= [lindex $top 0] &&
+                   [lindex $spos 0] <= [lindex $bottom 0]} {
+                       return $spos
+               }
+       }
+       if {$searchdirn eq "-forwards"} {
+               return $top
+       } else {
+               return $bottom
+       }
+}
+
+method _get_wrap_anchor {dir} {
+       if {$dir eq "-forwards"} {
+               return 1.0
+       } else {
+               return end
+       }
+}
+
+method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} {
+       set cmd [list $ctext search]
+       if {$mlenvar ne {}} {
+               upvar $mlenvar mlen
+               lappend cmd -count mlen
+       }
+       if {!$casesensitive} {
+               lappend cmd -nocase
+       }
+       if {$dir eq {}} {
+               set dir $searchdirn
+       }
+       lappend cmd $dir -- $searchstring
+       if {$endbound ne {}} {
+               set here [eval $cmd [list $start] [list $endbound]]
+       } else {
+               set here [eval $cmd [list $start]]
+               if {$here eq {}} {
+                       set here [eval $cmd [_get_wrap_anchor $this $dir]]
+               }
+       }
+       return $here
+}
+
+method _incrsearch_cb {name ix op} {
+       after idle [cb _incrsearch]
+}
+
+method _incrsearch {} {
+       $ctext tag remove found 1.0 end
+       if {[catch {$ctext index anchor}]} {
+               $ctext mark set anchor [_get_new_anchor $this]
+       }
+       if {$searchstring ne {}} {
+               set here [_do_search $this anchor mlen]
+               if {$here ne {}} {
+                       $ctext see $here
+                       $ctext tag remove sel 1.0 end
+                       $ctext tag add sel $here "$here + $mlen c"
+                       $w.ent configure -background lightgreen
+                       _set_marks $this 1
+               } else {
+                       $w.ent configure -background lightpink
+               }
+       }
+}
+
+method find_prev {} {
+       find_next $this -backwards
+}
+
+method find_next {{dir -forwards}} {
+       focus $w.ent
+       $w.ent icursor end
+       set searchdirn $dir
+       $ctext mark unset anchor
+       if {$searchstring ne {}} {
+               set start [_get_new_anchor $this]
+               if {$dir eq "-forwards"} {
+                       set start "$start + 1c"
+               }
+               set match [_do_search $this $start mlen]
+               $ctext tag remove sel 1.0 end
+               if {$match ne {}} {
+                       $ctext see $match
+                       $ctext tag add sel $match "$match + $mlen c"
+               }
+       }
+}
+
+method _mark_range {first last} {
+       set mend $first.0
+       while {1} {
+               set match [_do_search $this $mend mlen -forwards $last.end]
+               if {$match eq {}} break
+               set mend "$match + $mlen c"
+               $ctext tag add found $match $mend
+       }
+}
+
+method _set_marks {doall} {
+       set topline [lindex [split [$ctext index @0,0] .] 0]
+       set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
+       if {$doall || $botline < $smarktop || $topline > $smarkbot} {
+               # no overlap with previous
+               _mark_range $this $topline $botline
+               set smarktop $topline
+               set smarkbot $botline
+       } else {
+               if {$topline < $smarktop} {
+                       _mark_range $this $topline [expr {$smarktop-1}]
+                       set smarktop $topline
+               }
+               if {$botline > $smarkbot} {
+                       _mark_range $this [expr {$smarkbot+1}] $botline
+                       set smarkbot $botline
+               }
+       }
+}
+
+method scrolled {} {
+       if {$searchstring ne {}} {
+               after idle [cb _set_marks 0]
+       }
+}
+
+}
\ No newline at end of file
index 78f344f08f34842c134b6b0e22b6eb8c49b1dbbd..e6120303e940ee6c849bf706415c1b450e68b57f 100644 (file)
@@ -80,7 +80,7 @@ method _connect {pipe_fd} {
                error_popup [strcat [mc "Unrecognized spell checker"] ":\n\n$s_version"]
                return
        }
-       set s_version [string range $s_version 5 end]
+       set s_version [string range [string trim $s_version] 5 end]
        regexp \
                {International Ispell Version .* \(but really (Aspell .*?)\)$} \
                $s_version _junk s_version
@@ -314,6 +314,7 @@ method _run {} {
 method _read {} {
        while {[gets $s_fd line] >= 0} {
                set lineno [lindex $s_pending 0 0]
+               set line [string trim $line]
 
                if {$s_clear} {
                        $w_text tag remove misspelled "$lineno.0" "$lineno.end"
diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl
new file mode 100644 (file)
index 0000000..82a1a80
--- /dev/null
@@ -0,0 +1,126 @@
+# git-gui about git-gui dialog
+# Copyright (C) 2006, 2007 Shawn Pearce
+
+proc find_ssh_key {} {
+       foreach name {~/.ssh/id_dsa.pub ~/.ssh/id_rsa.pub ~/.ssh/identity.pub} {
+               if {[file exists $name]} {
+                       set fh    [open $name r]
+                       set cont  [read $fh]
+                       close $fh
+                       return [list $name $cont]
+               }
+       }
+
+       return {}
+}
+
+proc do_ssh_key {} {
+       global sshkey_title have_tk85 sshkey_fd
+
+       set w .sshkey_dialog
+       if {[winfo exists $w]} {
+               raise $w
+               return
+       }
+
+       toplevel $w
+       wm transient $w .
+
+       set finfo [find_ssh_key]
+       if {$finfo eq {}} {
+               set sshkey_title [mc "No keys found."]
+               set gen_state   normal
+       } else {
+               set sshkey_title [mc "Found a public key in: %s" [lindex $finfo 0]]
+               set gen_state   disabled
+       }
+
+       frame $w.header -relief flat
+       label $w.header.lbl -textvariable sshkey_title -anchor w
+       button $w.header.gen -text [mc "Generate Key"] \
+               -command [list make_ssh_key $w] -state $gen_state
+       pack $w.header.lbl -side left -expand 1 -fill x
+       pack $w.header.gen -side right
+       pack $w.header -fill x -pady 5 -padx 5
+
+       text $w.contents -width 60 -height 10 -wrap char -relief sunken
+       pack $w.contents -fill both -expand 1
+       if {$have_tk85} {
+               $w.contents configure -inactiveselectbackground darkblue
+       }
+
+       frame $w.buttons
+       button $w.buttons.close -text [mc Close] \
+               -default active -command [list destroy $w]
+       pack $w.buttons.close -side right
+       button $w.buttons.copy -text [mc "Copy To Clipboard"] \
+               -command [list tk_textCopy $w.contents]
+       pack $w.buttons.copy -side left
+       pack $w.buttons -side bottom -fill x -pady 5 -padx 5
+
+       if {$finfo ne {}} {
+               $w.contents insert end [lindex $finfo 1] sel
+       }
+       $w.contents configure -state disabled
+
+       bind $w <Visibility> "grab $w; focus $w.buttons.close"
+       bind $w <Key-Escape> "destroy $w"
+       bind $w <Key-Return> "destroy $w"
+       bind $w <Destroy> kill_sshkey
+       wm title $w [mc "Your OpenSSH Public Key"]
+       tk::PlaceWindow $w widget .
+       tkwait window $w
+}
+
+proc make_ssh_key {w} {
+       global sshkey_title sshkey_output sshkey_fd
+
+       set sshkey_title [mc "Generating..."]
+       $w.header.gen configure -state disabled
+
+       set cmdline [list sh -c {echo | ssh-keygen -q -t rsa -f ~/.ssh/id_rsa 2>&1}]
+
+       if {[catch { set sshkey_fd [_open_stdout_stderr $cmdline] } err]} {
+               error_popup [mc "Could not start ssh-keygen:\n\n%s" $err]
+               return
+       }
+
+       set sshkey_output {}
+       fconfigure $sshkey_fd -blocking 0
+       fileevent $sshkey_fd readable [list read_sshkey_output $sshkey_fd $w]
+}
+
+proc kill_sshkey {} {
+       global sshkey_fd
+       if {![info exists sshkey_fd]} return
+       catch { kill_file_process $sshkey_fd }
+       catch { close $sshkey_fd }
+}
+
+proc read_sshkey_output {fd w} {
+       global sshkey_fd sshkey_output sshkey_title
+
+       set sshkey_output "$sshkey_output[read $fd]"
+       if {![eof $fd]} return
+
+       fconfigure $fd -blocking 1
+       unset sshkey_fd
+
+       $w.contents configure -state normal
+       if {[catch {close $fd} err]} {
+               set sshkey_title [mc "Generation failed."]
+               $w.contents insert end $err
+               $w.contents insert end "\n"
+               $w.contents insert end $sshkey_output
+       } else {
+               set finfo [find_ssh_key]
+               if {$finfo eq {}} {
+                       set sshkey_title [mc "Generation succeded, but no keys found."]
+                       $w.contents insert end $sshkey_output
+               } else {
+                       set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]]
+                       $w.contents insert end [lindex $finfo 1] sel
+               }
+       }
+       $w.contents configure -state disable
+}
index 8e6a9d0a6010cf5efee069a2d488124bcbdb54cd..e419d7810ae404e41a0a85080d16f4576d737d8a 100644 (file)
@@ -135,7 +135,7 @@ proc do_push_anywhere {} {
                set push_urltype url
        }
        radiobutton $w.dest.url_r \
-               -text [mc "Arbitrary URL:"] \
+               -text [mc "Arbitrary Location:"] \
                -value url \
                -variable push_urltype
        entry $w.dest.url_t \
index 793cca1e798640400453e74e2a4b64863f76a540..5c04812b1ab67c29fb68a4c3d7d4e97bb6483984 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-09-13 10:20+0200\n"
-"PO-Revision-Date: 2008-09-13 10:24+0200\n"
+"POT-Creation-Date: 2008-10-25 13:32+0200\n"
+"PO-Revision-Date: 2008-10-25 22:47+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
 "MIME-Version: 1.0\n"
@@ -86,7 +86,17 @@ msgstr "Dateistatus aktualisieren..."
 msgid "Scanning for modified files ..."
 msgstr "Nach geänderten Dateien suchen..."
 
-#: git-gui.sh:1324 lib/browser.tcl:246
+#: git-gui.sh:1325
+#, fuzzy
+msgid "Calling prepare-commit-msg hook..."
+msgstr "Aufrufen der Vor-Eintragen-Kontrolle..."
+
+#: git-gui.sh:1342
+#, fuzzy
+msgid "Commit declined by prepare-commit-msg hook."
+msgstr "Eintragen abgelehnt durch Vor-Eintragen-Kontrolle (»pre-commit hook«)."
+
+#: git-gui.sh:1502 lib/browser.tcl:246
 msgid "Ready."
 msgstr "Bereit."
 
@@ -170,7 +180,11 @@ msgstr "Zusammenführen"
 msgid "Remote"
 msgstr "Andere Archive"
 
-#: git-gui.sh:1879
+#: git-gui.sh:2242
+msgid "Explore Working Copy"
+msgstr "Arbeitskopie im Dateimanager"
+
+#: git-gui.sh:2247
 msgid "Browse Current Branch's Files"
 msgstr "Aktuellen Zweig durchblättern"
 
@@ -267,7 +281,15 @@ msgstr "Löschen..."
 msgid "Reset..."
 msgstr "Zurücksetzen..."
 
-#: git-gui.sh:2002 git-gui.sh:2389
+#: git-gui.sh:2372
+msgid "Done"
+msgstr "Fertig"
+
+#: git-gui.sh:2374
+msgid "Commit@@verb"
+msgstr "Eintragen"
+
+#: git-gui.sh:2383 git-gui.sh:2786
 msgid "New Commit"
 msgstr "Neue Version"
 
@@ -307,11 +329,7 @@ msgstr "Mehr Zeilen anzeigen"
 msgid "Sign Off"
 msgstr "Abzeichnen"
 
-#: git-gui.sh:2053 git-gui.sh:2372
-msgid "Commit@@verb"
-msgstr "Eintragen"
-
-#: git-gui.sh:2064
+#: git-gui.sh:2458
 msgid "Local Merge..."
 msgstr "Lokales Zusammenführen..."
 
@@ -319,11 +337,19 @@ msgstr "Lokales Zusammenführen..."
 msgid "Abort Merge..."
 msgstr "Zusammenführen abbrechen..."
 
-#: git-gui.sh:2081
+#: git-gui.sh:2475
+msgid "Add..."
+msgstr "Hinzufügen..."
+
+#: git-gui.sh:2479
 msgid "Push..."
 msgstr "Versenden..."
 
-#: git-gui.sh:2197 git-gui.sh:2219 lib/about.tcl:14
+#: git-gui.sh:2483
+msgid "Delete Branch..."
+msgstr "Zweig löschen..."
+
+#: git-gui.sh:2493 git-gui.sh:2515 lib/about.tcl:14
 #: lib/choose_repository.tcl:44 lib/choose_repository.tcl:50
 #, tcl-format
 msgid "About %s"
@@ -416,7 +442,11 @@ msgstr "Schriftgröße verkleinern"
 msgid "Increase Font Size"
 msgstr "Schriftgröße vergrößern"
 
-#: git-gui.sh:2870
+#: git-gui.sh:3033 lib/blame.tcl:281
+msgid "Encoding"
+msgstr "Zeichenkodierung"
+
+#: git-gui.sh:3044
 msgid "Apply/Reverse Hunk"
 msgstr "Kontext anwenden/umkehren"
 
@@ -440,11 +470,7 @@ msgstr "Lokale Version benutzen"
 msgid "Revert To Base"
 msgstr "Ursprüngliche Version benutzen"
 
-#: git-gui.sh:2906
-msgid "Stage Working Copy"
-msgstr "Arbeitskopie bereitstellen"
-
-#: git-gui.sh:2925
+#: git-gui.sh:3091
 msgid "Unstage Hunk From Commit"
 msgstr "Kontext aus Bereitstellung herausnehmen"
 
@@ -583,7 +609,12 @@ msgstr "Eintragender:"
 msgid "Original File:"
 msgstr "Ursprüngliche Datei:"
 
-#: lib/blame.tcl:990
+#: lib/blame.tcl:1013
+#, fuzzy
+msgid "Cannot find HEAD commit:"
+msgstr "Elternversion kann nicht gefunden werden:"
+
+#: lib/blame.tcl:1068
 msgid "Cannot find parent commit:"
 msgstr "Elternversion kann nicht gefunden werden:"
 
@@ -1041,11 +1072,15 @@ msgstr "Datei »%s« existiert bereits."
 msgid "Clone"
 msgstr "Klonen"
 
-#: lib/choose_repository.tcl:468
-msgid "URL:"
-msgstr "URL:"
+#: lib/choose_repository.tcl:467
+msgid "Source Location:"
+msgstr ""
+
+#: lib/choose_repository.tcl:478
+msgid "Target Directory:"
+msgstr "Zielverzeichnis:"
 
-#: lib/choose_repository.tcl:489
+#: lib/choose_repository.tcl:490
 msgid "Clone Type:"
 msgstr "Art des Klonens:"
 
@@ -1525,7 +1560,27 @@ msgstr ""
 msgid "Loading diff of %s..."
 msgstr "Vergleich von »%s« laden..."
 
-#: lib/diff.tcl:114 lib/diff.tcl:184
+#: lib/diff.tcl:120
+msgid ""
+"LOCAL: deleted\n"
+"REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:125
+msgid ""
+"REMOTE: deleted\n"
+"LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:132
+msgid "LOCAL:\n"
+msgstr ""
+
+#: lib/diff.tcl:135
+msgid "REMOTE:\n"
+msgstr ""
+
+#: lib/diff.tcl:197 lib/diff.tcl:296
 #, tcl-format
 msgid "Unable to display %s"
 msgstr "Datei »%s« kann nicht angezeigt werden"
@@ -1542,7 +1597,22 @@ msgstr "Git-Projektarchiv (Unterprojekt)"
 msgid "* Binary file (not showing content)."
 msgstr "* Binärdatei (Inhalt wird nicht angezeigt)"
 
-#: lib/diff.tcl:313
+#: lib/diff.tcl:222
+#, tcl-format
+msgid ""
+"* Untracked file is %d bytes.\n"
+"* Showing only first %d bytes.\n"
+msgstr ""
+
+#: lib/diff.tcl:228
+#, tcl-format
+msgid ""
+"\n"
+"* Untracked file clipped here by %s.\n"
+"* To see the entire file, use an external editor.\n"
+msgstr ""
+
+#: lib/diff.tcl:437
 msgid "Failed to unstage selected hunk."
 msgstr ""
 "Fehler beim Herausnehmen des gewählten Kontexts aus der Bereitstellung."
@@ -1559,6 +1629,19 @@ msgstr "Fehler beim Herausnehmen der gewählten Zeile aus der Bereitstellung."
 msgid "Failed to stage selected line."
 msgstr "Fehler beim Bereitstellen der gewählten Zeile."
 
+#: lib/encoding.tcl:443
+msgid "Default"
+msgstr "Voreinstellung"
+
+#: lib/encoding.tcl:448
+#, tcl-format
+msgid "System (%s)"
+msgstr "Systemweit (%s)"
+
+#: lib/encoding.tcl:459 lib/encoding.tcl:465
+msgid "Other"
+msgstr "Andere"
+
 #: lib/error.tcl:20 lib/error.tcl:114
 msgid "error"
 msgstr "Fehler"
@@ -1811,7 +1894,12 @@ msgstr ""
 "Diese Operation kann nur rückgängig gemacht werden, wenn die\n"
 "Zusammenführung erneut gestartet wird."
 
-#: lib/mergetool.tcl:32
+#: lib/mergetool.tcl:45
+#, tcl-format
+msgid "File %s seems to have unresolved conflicts, still stage?"
+msgstr "Datei »%s« hat nicht aufgelöste Konflikte. Trotzdem bereitstellen?"
+
+#: lib/mergetool.tcl:60
 #, tcl-format
 msgid "Adding resolution for %s"
 msgstr "Auflösung hinzugefügt für %s"
@@ -1868,12 +1956,17 @@ msgstr "Zusammenführungswerkzeug starten..."
 msgid "Merge tool failed."
 msgstr "Zusammenführungswerkzeug fehlgeschlagen."
 
-#: lib/mergetool.tcl:353
+#: lib/option.tcl:11
+#, tcl-format
+msgid "Invalid global encoding '%s'"
+msgstr "Ungültige globale Zeichenkodierung »%s«"
+
+#: lib/option.tcl:19
 #, tcl-format
-msgid "File %s unchanged, still accept as resolved?"
-msgstr "Datei »%s« unverändert. Trotzdem Konflikt als gelöst akzeptieren?"
+msgid "Invalid repo encoding '%s'"
+msgstr "Ungültige Archiv-Zeichenkodierung »%s«"
 
-#: lib/option.tcl:95
+#: lib/option.tcl:117
 msgid "Restore Defaults"
 msgstr "Voreinstellungen wiederherstellen"
 
@@ -1950,7 +2043,15 @@ msgstr "Textbreite der Versionsbeschreibung"
 msgid "New Branch Name Template"
 msgstr "Namensvorschlag für neue Zweige"
 
-#: lib/option.tcl:192
+#: lib/option.tcl:155
+msgid "Default File Contents Encoding"
+msgstr "Vorgestellte Zeichenkodierung"
+
+#: lib/option.tcl:203
+msgid "Change"
+msgstr "Ändern"
+
+#: lib/option.tcl:230
 msgid "Spelling Dictionary:"
 msgstr "Wörterbuch Rechtschreibprüfung:"
 
@@ -1975,9 +2076,85 @@ msgstr "Einstellungen"
 msgid "Failed to completely save options:"
 msgstr "Optionen konnten nicht gespeichert werden:"
 
+#: lib/remote_add.tcl:19
+msgid "Add Remote"
+msgstr "Anderes Archiv hinzufügen"
+
+#: lib/remote_add.tcl:24
+msgid "Add New Remote"
+msgstr "Neues anderes Archiv hinzufügen"
+
+#: lib/remote_add.tcl:28
+msgid "Add"
+msgstr "Hinzufügen"
+
+#: lib/remote_add.tcl:37
+msgid "Remote Details"
+msgstr "Einzelheiten des anderen Archivs"
+
+#: lib/remote_add.tcl:50
+msgid "Location:"
+msgstr "Adresse:"
+
+#: lib/remote_add.tcl:62
+msgid "Further Action"
+msgstr "Weitere Aktion jetzt"
+
+#: lib/remote_add.tcl:65
+msgid "Fetch Immediately"
+msgstr "Gleich anfordern"
+
+#: lib/remote_add.tcl:71
+msgid "Initialize Remote Repository and Push"
+msgstr "Anderes Archiv initialisieren und dahin versenden"
+
+#: lib/remote_add.tcl:77
+msgid "Do Nothing Else Now"
+msgstr "Nichts tun"
+
+#: lib/remote_add.tcl:101
+#, fuzzy
+msgid "Please supply a remote name."
+msgstr "Bitte geben Sie einen Zweignamen an."
+
+#: lib/remote_add.tcl:114
+#, fuzzy, tcl-format
+msgid "'%s' is not an acceptable remote name."
+msgstr "»%s« ist kein zulässiger Zweigname."
+
+#: lib/remote_add.tcl:125
+#, fuzzy, tcl-format
+msgid "Failed to add remote '%s' of location '%s'."
+msgstr "Fehler beim Umbenennen von »%s«."
+
+#: lib/remote_add.tcl:133 lib/transport.tcl:6
+#, tcl-format
+msgid "fetch %s"
+msgstr "»%s« anfordern"
+
+#: lib/remote_add.tcl:134
+#, fuzzy, tcl-format
+msgid "Fetching the %s"
+msgstr "Änderungen »%s« von »%s« anfordern"
+
+#: lib/remote_add.tcl:157
+#, tcl-format
+msgid "Do not know how to initialize repository at location '%s'."
+msgstr "Initialisieren eines anderen Archivs an Adresse »%s« ist nicht möglich."
+
+#: lib/remote_add.tcl:163 lib/transport.tcl:25 lib/transport.tcl:71
+#, tcl-format
+msgid "push %s"
+msgstr "»%s« versenden..."
+
+#: lib/remote_add.tcl:164
+#, tcl-format
+msgid "Setting up the %s (at %s)"
+msgstr "Einrichten von »%s« an »%s«"
+
 #: lib/remote_branch_delete.tcl:29 lib/remote_branch_delete.tcl:34
-msgid "Delete Remote Branch"
-msgstr "Zweig in anderem Projektarchiv löschen"
+msgid "Delete Branch Remotely"
+msgstr "Zweig in anderem Archiv löschen"
 
 #: lib/remote_branch_delete.tcl:47
 msgid "From Repository"
@@ -1988,8 +2165,8 @@ msgid "Remote:"
 msgstr "Anderes Archiv:"
 
 #: lib/remote_branch_delete.tcl:66 lib/transport.tcl:138
-msgid "Arbitrary URL:"
-msgstr "Archiv-URL:"
+msgid "Arbitrary Location:"
+msgstr "Adresse:"
 
 #: lib/remote_branch_delete.tcl:84
 msgid "Branches"
@@ -2061,7 +2238,11 @@ msgstr "Kein Projektarchiv ausgewählt."
 msgid "Scanning %s..."
 msgstr "»%s« laden..."
 
-#: lib/remote.tcl:165
+#: lib/remote.tcl:163
+msgid "Remove Remote"
+msgstr "Anderes Archiv entfernen"
+
+#: lib/remote.tcl:168
 msgid "Prune from"
 msgstr "Aufräumen von"
 
@@ -2073,6 +2254,22 @@ msgstr "Anfordern von"
 msgid "Push to"
 msgstr "Versenden nach"
 
+#: lib/search.tcl:21
+msgid "Find:"
+msgstr "Suchen:"
+
+#: lib/search.tcl:22
+msgid "Next"
+msgstr "Nächster"
+
+#: lib/search.tcl:23
+msgid "Prev"
+msgstr "Voriger"
+
+#: lib/search.tcl:24
+msgid "Case-Sensitive"
+msgstr ""
+
 #: lib/shortcut.tcl:20 lib/shortcut.tcl:61
 msgid "Cannot write shortcut:"
 msgstr "Fehler beim Schreiben der Verknüpfung:"
@@ -2123,11 +2320,6 @@ msgstr "Rechtschreibprüfung fehlgeschlagen"
 msgid "%s ... %*i of %*i %s (%3i%%)"
 msgstr "%s ... %*i von %*i %s (%3i%%)"
 
-#: lib/transport.tcl:6
-#, tcl-format
-msgid "fetch %s"
-msgstr "»%s« anfordern"
-
 #: lib/transport.tcl:7
 #, tcl-format
 msgid "Fetching new changes from %s"
@@ -2143,11 +2335,6 @@ msgstr "Aufräumen von »%s«"
 msgid "Pruning tracking branches deleted from %s"
 msgstr "Übernahmezweige aufräumen und entfernen, die in »%s« gelöscht wurden"
 
-#: lib/transport.tcl:25 lib/transport.tcl:71
-#, tcl-format
-msgid "push %s"
-msgstr "»%s« versenden..."
-
 #: lib/transport.tcl:26
 #, tcl-format
 msgid "Pushing changes to %s"
index 75c36100a2f858bcf2663d2b4560654787963175..664fe34419e9aeef1af8fac820cc423a641f5525 100755 (executable)
@@ -124,7 +124,7 @@ orig_head=$(git rev-parse --verify HEAD 2>/dev/null)
 git fetch --update-head-ok "$@" || exit 1
 
 curr_head=$(git rev-parse --verify HEAD 2>/dev/null)
-if test "$curr_head" != "$orig_head"
+if test -n "$orig_head" && test "$curr_head" != "$orig_head"
 then
        # The fetch involved updating the current branch.
 
@@ -172,7 +172,7 @@ esac
 
 if test -z "$orig_head"
 then
-       git update-ref -m "initial pull" HEAD $merge_head "" &&
+       git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
        git read-tree --reset -u HEAD || exit 1
        exit
 fi
index 124cb5846b07ee9aa72fac4cbb88098c94f5741a..1172e47571dfe1d6dd088381d63045fd5eae3db3 100755 (executable)
@@ -26,6 +26,7 @@ i,interactive      always used (no-op)
 continue           continue rebasing process
 abort              abort rebasing process and restore original branch
 skip               skip current patch and continue rebasing process
+no-verify          override pre-rebase hook from stopping the operation
 "
 
 . git-sh-setup
@@ -37,10 +38,12 @@ DONE="$DOTEST"/done
 MSG="$DOTEST"/message
 SQUASH_MSG="$DOTEST"/message-squash
 REWRITTEN="$DOTEST"/rewritten
+DROPPED="$DOTEST"/dropped
 PRESERVE_MERGES=
 STRATEGY=
 ONTO=
 VERBOSE=
+OK_TO_SKIP_PRE_REBASE=
 
 GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
 mark the corrected paths with 'git add <paths>', and
@@ -66,7 +69,8 @@ output () {
 }
 
 run_pre_rebase_hook () {
-       if test -x "$GIT_DIR/hooks/pre-rebase"
+       if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+          test -x "$GIT_DIR/hooks/pre-rebase"
        then
                "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
                        echo >&2 "The pre-rebase hook refused to rebase."
@@ -169,21 +173,39 @@ pick_one_preserving_merges () {
 
        if test -f "$DOTEST"/current-commit
        then
-               current_commit=$(cat "$DOTEST"/current-commit) &&
-               git rev-parse HEAD > "$REWRITTEN"/$current_commit &&
-               rm "$DOTEST"/current-commit ||
-               die "Cannot write current commit's replacement sha1"
+               if test "$fast_forward" = t
+               then
+                       cat "$DOTEST"/current-commit | while read current_commit
+                       do
+                               git rev-parse HEAD > "$REWRITTEN"/$current_commit
+                       done
+                       rm "$DOTEST"/current-commit ||
+                       die "Cannot write current commit's replacement sha1"
+               fi
        fi
 
-       echo $sha1 > "$DOTEST"/current-commit
+       echo $sha1 >> "$DOTEST"/current-commit
 
        # rewrite parents; if none were rewritten, we can fast-forward.
        new_parents=
-       for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
+       pend=" $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)"
+       while [ "$pend" != "" ]
        do
+               p=$(expr "$pend" : ' \([^ ]*\)')
+               pend="${pend# $p}"
+
                if test -f "$REWRITTEN"/$p
                then
                        new_p=$(cat "$REWRITTEN"/$p)
+
+                       # If the todo reordered commits, and our parent is marked for
+                       # rewriting, but hasn't been gotten to yet, assume the user meant to
+                       # drop it on top of the current HEAD
+                       if test -z "$new_p"
+                       then
+                               new_p=$(git rev-parse HEAD)
+                       fi
+
                        test $p != $new_p && fast_forward=f
                        case "$new_parents" in
                        *$new_p*)
@@ -193,7 +215,13 @@ pick_one_preserving_merges () {
                                ;;
                        esac
                else
-                       new_parents="$new_parents $p"
+                       if test -f "$DROPPED"/$p
+                       then
+                               fast_forward=f
+                               pend=" $(cat "$DROPPED"/$p)$pend"
+                       else
+                               new_parents="$new_parents $p"
+                       fi
                fi
        done
        case $fast_forward in
@@ -203,15 +231,19 @@ pick_one_preserving_merges () {
                        die "Cannot fast forward to $sha1"
                ;;
        f)
-               test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
-
                first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
-               # detach HEAD to current parent
-               output git checkout $first_parent 2> /dev/null ||
-                       die "Cannot move HEAD to $first_parent"
+
+               if [ "$1" != "-n" ]
+               then
+                       # detach HEAD to current parent
+                       output git checkout $first_parent 2> /dev/null ||
+                               die "Cannot move HEAD to $first_parent"
+               fi
 
                case "$new_parents" in
                ' '*' '*)
+                       test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
+
                        # redo merge
                        author_script=$(get_author_ident_from_commit $sha1)
                        eval "$author_script"
@@ -365,20 +397,7 @@ do_next () {
        HEADNAME=$(cat "$DOTEST"/head-name) &&
        OLDHEAD=$(cat "$DOTEST"/head) &&
        SHORTONTO=$(git rev-parse --short $(cat "$DOTEST"/onto)) &&
-       if test -d "$REWRITTEN"
-       then
-               test -f "$DOTEST"/current-commit &&
-                       current_commit=$(cat "$DOTEST"/current-commit) &&
-                       git rev-parse HEAD > "$REWRITTEN"/$current_commit
-               if test -f "$REWRITTEN"/$OLDHEAD
-               then
-                       NEWHEAD=$(cat "$REWRITTEN"/$OLDHEAD)
-               else
-                       NEWHEAD=$OLDHEAD
-               fi
-       else
-               NEWHEAD=$(git rev-parse HEAD)
-       fi &&
+       NEWHEAD=$(git rev-parse HEAD) &&
        case $HEADNAME in
        refs/*)
                message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
@@ -421,6 +440,11 @@ get_saved_options () {
 while test $# != 0
 do
        case "$1" in
+       --no-verify)
+               OK_TO_SKIP_PRE_REBASE=yes
+               ;;
+       --verify)
+               ;;
        --continue)
                is_standalone "$@" || usage
                get_saved_options
@@ -572,18 +596,69 @@ first and then run 'git rebase --continue' again."
                                echo $ONTO > "$REWRITTEN"/$c ||
                                        die "Could not init rewritten commits"
                        done
+                       # No cherry-pick because our first pass is to determine
+                       # parents to rewrite and skipping dropped commits would
+                       # prematurely end our probe
                        MERGES_OPTION=
+                       first_after_upstream="$(git rev-list --reverse --first-parent $UPSTREAM..$HEAD | head -n 1)"
                else
-                       MERGES_OPTION=--no-merges
+                       MERGES_OPTION="--no-merges --cherry-pick"
                fi
 
                SHORTUPSTREAM=$(git rev-parse --short $UPSTREAM)
                SHORTHEAD=$(git rev-parse --short $HEAD)
                SHORTONTO=$(git rev-parse --short $ONTO)
                git rev-list $MERGES_OPTION --pretty=oneline --abbrev-commit \
-                       --abbrev=7 --reverse --left-right --cherry-pick \
+                       --abbrev=7 --reverse --left-right --topo-order \
                        $UPSTREAM...$HEAD | \
-                       sed -n "s/^>/pick /p" > "$TODO"
+                       sed -n "s/^>//p" | while read shortsha1 rest
+               do
+                       if test t != "$PRESERVE_MERGES"
+                       then
+                               echo "pick $shortsha1 $rest" >> "$TODO"
+                       else
+                               sha1=$(git rev-parse $shortsha1)
+                               preserve=t
+                               for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
+                               do
+                                       if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
+                                       then
+                                               preserve=f
+                                       fi
+                               done
+                               if test f = "$preserve"
+                               then
+                                       touch "$REWRITTEN"/$sha1
+                                       echo "pick $shortsha1 $rest" >> "$TODO"
+                               fi
+                       fi
+               done
+
+               # Watch for commits that been dropped by --cherry-pick
+               if test t = "$PRESERVE_MERGES"
+               then
+                       mkdir "$DROPPED"
+                       # Save all non-cherry-picked changes
+                       git rev-list $UPSTREAM...$HEAD --left-right --cherry-pick | \
+                               sed -n "s/^>//p" > "$DOTEST"/not-cherry-picks
+                       # Now all commits and note which ones are missing in
+                       # not-cherry-picks and hence being dropped
+                       git rev-list $UPSTREAM..$HEAD |
+                       while read rev
+                       do
+                               if test -f "$REWRITTEN"/$rev -a "$(grep "$rev" "$DOTEST"/not-cherry-picks)" = ""
+                               then
+                                       # Use -f2 because if rev-list is telling us this commit is
+                                       # not worthwhile, we don't want to track its multiple heads,
+                                       # just the history of its first-parent for others that will
+                                       # be rebasing on top of it
+                                       git rev-list --parents -1 $rev | cut -d' ' -f2 > "$DROPPED"/$rev
+                                       short=$(git rev-list -1 --abbrev-commit --abbrev=7 $rev)
+                                       grep -v "^[a-z][a-z]* $short" <"$TODO" > "${TODO}2" ; mv "${TODO}2" "$TODO"
+                                       rm "$REWRITTEN"/$rev
+                               fi
+                       done
+               fi
                test -s "$TODO" || echo noop >> "$TODO"
                cat >> "$TODO" << EOF
 
index a30d40c0056dc32aa6123adc9856e649c469fcd0..023a6dc94a48f7abf2801359ad68d40909e9b6aa 100755 (executable)
@@ -34,6 +34,7 @@ set_reflog_action rebase
 require_work_tree
 cd_to_toplevel
 
+OK_TO_SKIP_PRE_REBASE=
 RESOLVEMSG="
 When you have resolved this problem run \"git rebase --continue\".
 If you would prefer to skip this patch, instead run \"git rebase --skip\".
@@ -138,14 +139,31 @@ finish_rb_merge () {
 }
 
 is_interactive () {
-       test -f "$dotest"/interactive ||
-       while :; do case $#,"$1" in 0,|*,-i|*,--interactive) break ;; esac
+       while test $# != 0
+       do
+               case "$1" in
+                       -i|--interactive)
+                               interactive_rebase=explicit
+                               break
+                       ;;
+                       -p|--preserve-merges)
+                               interactive_rebase=implied
+                       ;;
+               esac
                shift
-       done && test -n "$1"
+       done
+
+       if [ "$interactive_rebase" = implied ]; then
+               GIT_EDITOR=:
+               export GIT_EDITOR
+       fi
+
+       test -n "$interactive_rebase" || test -f "$dotest"/interactive
 }
 
 run_pre_rebase_hook () {
-       if test -x "$GIT_DIR/hooks/pre-rebase"
+       if test -z "$OK_TO_SKIP_PRE_REBASE" &&
+          test -x "$GIT_DIR/hooks/pre-rebase"
        then
                "$GIT_DIR/hooks/pre-rebase" ${1+"$@"} || {
                        echo >&2 "The pre-rebase hook refused to rebase."
@@ -170,6 +188,9 @@ fi
 while test $# != 0
 do
        case "$1" in
+       --no-verify)
+               OK_TO_SKIP_PRE_REBASE=yes
+               ;;
        --continue)
                test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
                        die "No rebase in progress?"
index d39eb6cea6174ac65f1cc13c98e4abeab3a90903..4d313d136e64678b99179b2e1dce7a1beaa36d04 100755 (executable)
@@ -71,19 +71,16 @@ case ",$all_into_one," in
                                existing="$existing $e"
                        fi
                done
-       fi
-       if test -z "$args"
-       then
-               args='--unpacked --incremental'
-       elif test -n "$unpack_unreachable"
-       then
-               args="$args $unpack_unreachable"
+               if test -n "$args" -a -n "$unpack_unreachable"
+               then
+                       args="$args $unpack_unreachable"
+               fi
        fi
        ;;
 esac
 
 args="$args $local $quiet $no_reuse$extra"
-names=$(git pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+names=$(git pack-objects --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
        exit 1
 if [ -z "$names" ]; then
        if test -z "$quiet"; then
index bdbfac66256ffc29e26f5c83531c77a18a263808..94ca5c89ad086bac754fb6b6b907fd4e968e8024 100755 (executable)
@@ -374,10 +374,9 @@ sub read_config {
 
                push @files, grep { -f $_ } map { +$f . "/" . $_ }
                                sort readdir(DH);
-
+               closedir(DH);
        } elsif (-f $f or -p $f) {
                push @files, $f;
-
        } else {
                print STDERR "Skipping $f - not found.\n";
        }
index 65178ae8e32bb20c65c42eb9668fb0ffec749007..220d94ec0c53c9d6c4535d1e50f5d1cb9dc99689 100755 (executable)
@@ -323,7 +323,7 @@ cmd_update()
                        # Only mention uninitialized submodules when its
                        # path have been specified
                        test "$#" != "0" &&
-                       say "Submodule path '$path' not initialized"
+                       say "Submodule path '$path' not initialized" &&
                        say "Maybe you want to use 'update --init'?"
                        continue
                fi
@@ -424,7 +424,7 @@ cmd_summary() {
        cd_to_toplevel
        # Get modified modules cared by user
        modules=$(git diff-index $cached --raw $head -- "$@" |
-               grep -e '^:160000' -e '^:[0-7]* 160000' |
+               egrep '^:([0-7]* )?160000' |
                while read mod_src mod_dst sha1_src sha1_dst status name
                do
                        # Always show modules deleted or type-changed (blob<->module)
@@ -438,7 +438,7 @@ cmd_summary() {
        test -z "$modules" && return
 
        git diff-index $cached --raw $head -- $modules |
-       grep -e '^:160000' -e '^:[0-7]* 160000' |
+       egrep '^:([0-7]* )?160000' |
        cut -c2- |
        while read mod_src mod_dst sha1_src sha1_dst status name
        do
index ef6d773df1143b32f0bc421b59421f7e0eab60bd..5702b100f17d32c4d370d2b29374390ad0f03520 100755 (executable)
@@ -1191,7 +1191,7 @@ sub read_repo_config {
                my $v = $opts->{$o};
                my ($key) = ($o =~ /^([a-zA-Z\-]+)/);
                $key =~ s/-//g;
-               my $arg = 'git-config';
+               my $arg = 'git config';
                $arg .= ' --int' if ($o =~ /[:=]i$/);
                $arg .= ' --bool' if ($o !~ /[:=][sfi]$/);
                if (ref $v eq 'ARRAY') {
@@ -2267,7 +2267,7 @@ sub do_git_commit {
        }
        die "Tree is not a valid sha1: $tree\n" if $tree !~ /^$::sha1$/o;
 
-       my @exec = ('git-commit-tree', $tree);
+       my @exec = ('git', 'commit-tree', $tree);
        foreach ($self->get_commit_parents($log_entry)) {
                push @exec, '-p', $_;
        }
index 2eaa2ae7d6f692f6063ebbd211eaab30212c2eae..bcebc87f7d3800c20108ab1ae9aec0577e7c2cc7 100644 (file)
@@ -269,7 +269,7 @@ proc parseviewrevs {view revs} {
                lappend badrev $line
            }
        }                   
-       error_popup "Error parsing revisions: $err"
+       error_popup "[mc "Error parsing revisions:"] $err"
        return {}
     }
     set ret {}
@@ -307,7 +307,7 @@ proc start_rev_list {view} {
     global startmsecs commitidx viewcomplete curview
     global tclencoding
     global viewargs viewargscmd viewfiles vfilelimit
-    global showlocalchanges commitinterest
+    global showlocalchanges
     global viewactive viewinstances vmergeonly
     global mainheadid
     global vcanopt vflags vrevs vorigargs
@@ -324,7 +324,7 @@ proc start_rev_list {view} {
        if {[catch {
            set str [exec sh -c $viewargscmd($view)]
        } err]} {
-           error_popup "Error executing --argscmd command: $err"
+           error_popup "[mc "Error executing --argscmd command:"] $err"
            return 0
        }
        set args [concat $args [split $str "\n"]]
@@ -368,7 +368,7 @@ proc start_rev_list {view} {
     set i [reg_instance $fd]
     set viewinstances($view) [list $i]
     if {$showlocalchanges && $mainheadid ne {}} {
-       lappend commitinterest($mainheadid) {dodiffindex}
+       interestedin $mainheadid dodiffindex
     }
     fconfigure $fd -blocking 0 -translation lf -eofchar {}
     if {$tclencoding != {}} {
@@ -500,7 +500,7 @@ proc updatecommits {} {
        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"
+       error_popup "[mc "Error executing git log:"] $err"
        return
     }
     if {$viewactive($view) == 0} {
@@ -1231,7 +1231,7 @@ proc commitonrow {row} {
 
 proc closevarcs {v} {
     global varctok varccommits varcid parents children
-    global cmitlisted commitidx commitinterest vtokmod
+    global cmitlisted commitidx vtokmod
 
     set missing_parents 0
     set scripts {}
@@ -1256,12 +1256,7 @@ proc closevarcs {v} {
            }
            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)
-           }
+           set scripts [check_interest $p $scripts]
        }
     }
     if {$missing_parents > 0} {
@@ -1297,8 +1292,41 @@ proc rewrite_commit {v id rwid} {
     }
 }
 
+# Mechanism for registering a command to be executed when we come
+# across a particular commit.  To handle the case when only the
+# prefix of the commit is known, the commitinterest array is now
+# indexed by the first 4 characters of the ID.  Each element is a
+# list of id, cmd pairs.
+proc interestedin {id cmd} {
+    global commitinterest
+
+    lappend commitinterest([string range $id 0 3]) $id $cmd
+}
+
+proc check_interest {id scripts} {
+    global commitinterest
+
+    set prefix [string range $id 0 3]
+    if {[info exists commitinterest($prefix)]} {
+       set newlist {}
+       foreach {i script} $commitinterest($prefix) {
+           if {[string match "$i*" $id]} {
+               lappend scripts [string map [list "%I" $id "%P" $i] $script]
+           } else {
+               lappend newlist $i $script
+           }
+       }
+       if {$newlist ne {}} {
+           set commitinterest($prefix) $newlist
+       } else {
+           unset commitinterest($prefix)
+       }
+    }
+    return $scripts
+}
+
 proc getcommitlines {fd inst view updating}  {
-    global cmitlisted commitinterest leftover
+    global cmitlisted leftover
     global commitidx commitdata vdatemode
     global parents children curview hlview
     global idpending ordertok
@@ -1474,12 +1502,7 @@ proc getcommitlines {fd inst view updating}  {
            incr i
        }
 
-       if {[info exists commitinterest($id)]} {
-           foreach script $commitinterest($id) {
-               lappend scripts [string map [list "%I" $id] $script]
-           }
-           unset commitinterest($id)
-       }
+       set scripts [check_interest $id $scripts]
        set gotsome 1
     }
     if {$gotsome} {
@@ -1608,6 +1631,19 @@ proc getcommit {id} {
     return 1
 }
 
+# Expand an abbreviated commit ID to a list of full 40-char IDs that match
+# and are present in the current view.
+# This is fairly slow...
+proc longid {prefix} {
+    global varcid curview
+
+    set ids {}
+    foreach match [array names varcid "$curview,$prefix*"] {
+       lappend ids [lindex [split $match ","] 1]
+    }
+    return $ids
+}
+
 proc readrefs {} {
     global tagids idtags headids idheads tagobjid
     global otherrefids idotherrefs mainhead mainheadid
@@ -1750,6 +1786,53 @@ proc setoptions {} {
     option add *Entry.font uifont startupFile
 }
 
+# Make a menu and submenus.
+# m is the window name for the menu, items is the list of menu items to add.
+# Each item is a list {mc label type description options...}
+# mc is ignored; it's so we can put mc there to alert xgettext
+# label is the string that appears in the menu
+# type is cascade, command or radiobutton (should add checkbutton)
+# description depends on type; it's the sublist for cascade, the
+# command to invoke for command, or {variable value} for radiobutton
+proc makemenu {m items} {
+    menu $m
+    foreach i $items {
+       set name [mc [lindex $i 1]]
+       set type [lindex $i 2]
+       set thing [lindex $i 3]
+       set params [list $type]
+       if {$name ne {}} {
+           set u [string first "&" [string map {&& x} $name]]
+           lappend params -label [string map {&& & & {}} $name]
+           if {$u >= 0} {
+               lappend params -underline $u
+           }
+       }
+       switch -- $type {
+           "cascade" {
+               set submenu [string tolower [string map {& ""} [lindex $i 1]]]
+               lappend params -menu $m.$submenu
+           }
+           "command" {
+               lappend params -command $thing
+           }
+           "radiobutton" {
+               lappend params -variable [lindex $thing 0] \
+                   -value [lindex $thing 1]
+           }
+       }
+       eval $m add $params [lrange $i 4 end]
+       if {$type eq "cascade"} {
+           makemenu $m.$submenu $thing
+       }
+    }
+}
+
+# translate string and remove ampersands
+proc mca {str} {
+    return [string map {&& & & {}} [mc $str]]
+}
+
 proc makewindow {} {
     global canv canv2 canv3 linespc charspc ctext cflist cscroll
     global tabstop
@@ -1767,33 +1850,31 @@ proc makewindow {} {
     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
-    menu .bar.edit
-    .bar add cascade -label [mc "Edit"] -menu .bar.edit
-    .bar.edit add command -label [mc "Preferences"] -command doprefs
-
-    menu .bar.view
-    .bar add cascade -label [mc "View"] -menu .bar.view
-    .bar.view add command -label [mc "New view..."] -command {newview 0}
-    .bar.view add command -label [mc "Edit view..."] -command editview \
-       -state disabled
-    .bar.view add command -label [mc "Delete view"] -command delview -state disabled
-    .bar.view add separator
-    .bar.view add radiobutton -label [mc "All files"] -command {showview 0} \
-       -variable selectedview -value 0
-
-    menu .bar.help
-    .bar add cascade -label [mc "Help"] -menu .bar.help
-    .bar.help add command -label [mc "About gitk"] -command about
-    .bar.help add command -label [mc "Key bindings"] -command keys
-    .bar.help configure
+    # The "mc" arguments here are purely so that xgettext
+    # sees the following string as needing to be translated
+    makemenu .bar {
+       {mc "File" cascade {
+           {mc "Update" command updatecommits -accelerator F5}
+           {mc "Reload" command reloadcommits}
+           {mc "Reread references" command rereadrefs}
+           {mc "List references" command showrefs}
+           {mc "Quit" command doquit}
+       }}
+       {mc "Edit" cascade {
+           {mc "Preferences" command doprefs}
+       }}
+       {mc "View" cascade {
+           {mc "New view..." command {newview 0}}
+           {mc "Edit view..." command editview -state disabled}
+           {mc "Delete view" command delview -state disabled}
+           {xx "" separator}
+           {mc "All files" radiobutton {selectedview 0} -command {showview 0}}
+       }}
+       {mc "Help" cascade {
+           {mc "About gitk" command about}
+           {mc "Key bindings" command keys}
+       }}
+    }
     . configure -menu .bar
 
     # the gui has upper and lower half, parts of a paned window.
@@ -2161,61 +2242,55 @@ proc makewindow {} {
     bind . <Destroy> {stop_backends}
     bind . <Button-1> "click %W"
     bind $fstring <Key-Return> {dofind 1 1}
-    bind $sha1entry <Key-Return> gotocommit
+    bind $sha1entry <Key-Return> {gotocommit; break}
     bind $sha1entry <<PasteSelection>> clearsha1
     bind $cflist <1> {sel_flist %W %x %y; break}
     bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
     bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
-    bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
+    global ctxbut
+    bind $cflist $ctxbut {pop_flist_menu %W %X %Y %x %y}
 
     set maincursor [. cget -cursor]
     set textcursor [$ctext cget -cursor]
     set curtextcursor $textcursor
 
     set rowctxmenu .rowctxmenu
-    menu $rowctxmenu -tearoff 0
-    $rowctxmenu add command -label [mc "Diff this -> selected"] \
-       -command {diffvssel 0}
-    $rowctxmenu add command -label [mc "Diff selected -> this"] \
-       -command {diffvssel 1}
-    $rowctxmenu add command -label [mc "Make patch"] -command mkpatch
-    $rowctxmenu add command -label [mc "Create tag"] -command mktag
-    $rowctxmenu add command -label [mc "Write commit to file"] -command writecommit
-    $rowctxmenu add command -label [mc "Create new branch"] -command mkbranch
-    $rowctxmenu add command -label [mc "Cherry-pick this commit"] \
-       -command cherrypick
-    $rowctxmenu add command -label [mc "Reset HEAD branch to here"] \
-       -command resethead
+    makemenu $rowctxmenu {
+       {mc "Diff this -> selected" command {diffvssel 0}}
+       {mc "Diff selected -> this" command {diffvssel 1}}
+       {mc "Make patch" command mkpatch}
+       {mc "Create tag" command mktag}
+       {mc "Write commit to file" command writecommit}
+       {mc "Create new branch" command mkbranch}
+       {mc "Cherry-pick this commit" command cherrypick}
+       {mc "Reset HEAD branch to here" command resethead}
+    }
+    $rowctxmenu configure -tearoff 0
 
     set fakerowmenu .fakerowmenu
-    menu $fakerowmenu -tearoff 0
-    $fakerowmenu add command -label [mc "Diff this -> selected"] \
-       -command {diffvssel 0}
-    $fakerowmenu add command -label [mc "Diff selected -> this"] \
-       -command {diffvssel 1}
-    $fakerowmenu add command -label [mc "Make patch"] -command mkpatch
-#    $fakerowmenu add command -label [mc "Commit"] -command {mkcommit 0}
-#    $fakerowmenu add command -label [mc "Commit all"] -command {mkcommit 1}
-#    $fakerowmenu add command -label [mc "Revert local changes"] -command revertlocal
+    makemenu $fakerowmenu {
+       {mc "Diff this -> selected" command {diffvssel 0}}
+       {mc "Diff selected -> this" command {diffvssel 1}}
+       {mc "Make patch" command mkpatch}
+    }
+    $fakerowmenu configure -tearoff 0
 
     set headctxmenu .headctxmenu
-    menu $headctxmenu -tearoff 0
-    $headctxmenu add command -label [mc "Check out this branch"] \
-       -command cobranch
-    $headctxmenu add command -label [mc "Remove this branch"] \
-       -command rmbranch
+    makemenu $headctxmenu {
+       {mc "Check out this branch" command cobranch}
+       {mc "Remove this branch" command rmbranch}
+    }
+    $headctxmenu configure -tearoff 0
 
     global flist_menu
     set flist_menu .flistctxmenu
-    menu $flist_menu -tearoff 0
-    $flist_menu add command -label [mc "Highlight this too"] \
-       -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}
-    $flist_menu add command -label [mc "Blame parent commit"] \
-        -command {external_blame 1}
+    makemenu $flist_menu {
+       {mc "Highlight this too" command {flist_hl 0}}
+       {mc "Highlight this only" command {flist_hl 1}}
+       {mc "External diff" command {external_diff}}
+       {mc "Blame parent commit" command {external_blame 1}}
+    }
+    $flist_menu configure -tearoff 0
 }
 
 # Windows sends all mouse wheel events to the current focused window, not
@@ -2331,7 +2406,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 extdifftool
+    global autoselect extdifftool perfile_attrs
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -2358,6 +2433,7 @@ proc savestuff {w} {
        puts $f [list set diffcontext $diffcontext]
        puts $f [list set selectbgcolor $selectbgcolor]
        puts $f [list set extdifftool $extdifftool]
+       puts $f [list set perfile_attrs $perfile_attrs]
 
        puts $f "set geometry(main) [wm geometry .]"
        puts $f "set geometry(topwidth) [winfo width .tf]"
@@ -2705,7 +2781,7 @@ proc treeopendir {w dir} {
            $w insert e:$ix $e [highlight_tag $de]
        }
     }
-    $w mark gravity e:$ix left
+    $w mark gravity e:$ix right
     $w conf -state disabled
     set treediropen($dir) 1
     set top [lindex [split [$w index @0,0] .] 0]
@@ -2936,7 +3012,7 @@ proc save_file_from_commit {filename output what} {
        if {[string match "fatal: bad revision *" $err]} {
            return $nullfile
        }
-       error_popup "Error getting \"$filename\" from $what: $err"
+       error_popup "[mc "Error getting \"%s\" from %s:" $filename $what] $err"
        return {}
     }
     return $output
@@ -2993,7 +3069,7 @@ proc external_diff {} {
        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"
+           error_popup "[mc "Error creating temporary directory %s:" $gitktmpdir] $err"
            unset gitktmpdir
            return
        }
@@ -3002,7 +3078,7 @@ proc external_diff {} {
     incr diffnum
     set diffdir [file join $gitktmpdir $diffnum]
     if {[catch {file mkdir $diffdir} err]} {
-       error_popup "Error creating temporary directory $diffdir: $err"
+       error_popup "[mc "Error creating temporary directory %s:" $diffdir] $err"
        return
     }
 
@@ -3015,7 +3091,7 @@ proc external_diff {} {
                     [list $difffromfile $difftofile]]
         if {[catch {set fl [open $cmd r]} err]} {
             file delete -force $diffdir
-            error_popup [mc "$extdifftool: command failed: $err"]
+            error_popup "$extdifftool: [mc "command failed:"] $err"
         } else {
             fconfigure $fl -blocking 0
             filerun $fl [list delete_at_eof $fl $diffdir]
@@ -3040,7 +3116,7 @@ proc external_blame {parent_idx} {
     }
 
     if {[catch {exec git gui blame $base_commit $flist_menu_file &} err]} {
-       error_popup [mc "git gui blame: command failed: $err"]
+       error_popup "[mc "git gui blame: command failed:"] $err"
     }
 }
 
@@ -3049,7 +3125,7 @@ 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"
+           error_popup "[mc "External diff viewer failed:"] $err"
        }
        file delete -force $dir
        return 0
@@ -3374,8 +3450,8 @@ proc showview {n} {
 
     set curview $n
     set selectedview $n
-    .bar.view entryconf [mc "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
-    .bar.view entryconf [mc "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf [mca "Edit view..."] -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf [mca "Delete view"] -state [expr {$n == 0? "disabled": "normal"}]
 
     run refill_reflist
     if {![info exists viewcomplete($n)]} {
@@ -4079,7 +4155,7 @@ proc visiblerows {} {
 proc layoutmore {} {
     global commitidx viewcomplete curview
     global numcommits pending_select curview
-    global lastscrollset lastscrollrows commitinterest
+    global lastscrollset lastscrollrows
 
     if {$lastscrollrows < 100 || $viewcomplete($curview) ||
        [clock clicks -milliseconds] - $lastscrollset > 500} {
@@ -4100,7 +4176,7 @@ proc doshowlocalchanges {} {
     if {[commitinview $mainheadid $curview]} {
        dodiffindex
     } else {
-       lappend commitinterest($mainheadid) {dodiffindex}
+       interestedin $mainheadid dodiffindex
     }
 }
 
@@ -4919,7 +4995,7 @@ proc drawcmittext {id row col} {
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag selectedline
     global canvxmax boldrows boldnamerows fgcolor
-    global mainheadid nullid nullid2 circleitem circlecolors
+    global mainheadid nullid nullid2 circleitem circlecolors ctxbut
 
     # listed is 0 for boundary, 1 for normal, 2 for negative, 3 for left, 4 for right
     set listed $cmitlisted($curview,$id)
@@ -4992,7 +5068,7 @@ proc drawcmittext {id row col} {
     }
     set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
                            -text $headline -font $font -tags text]
-    $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
+    $canv bind $linehtag($row) $ctxbut "rowmenu %X %Y $id"
     set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
                            -text $name -font $nfont -tags text]
     set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
@@ -5334,7 +5410,7 @@ proc bindline {t id} {
 proc drawtags {id x xt y1} {
     global idtags idheads idotherrefs mainhead
     global linespc lthickness
-    global canv rowtextx curview fgcolor bgcolor
+    global canv rowtextx curview fgcolor bgcolor ctxbut
 
     set marks {}
     set ntags 0
@@ -5412,7 +5488,7 @@ proc drawtags {id x xt y1} {
        if {$ntags >= 0} {
            $canv bind $t <1> [list showtag $tag 1]
        } elseif {$nheads >= 0} {
-           $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
+           $canv bind $t $ctxbut [list headmenu %X %Y $id $tag]
        }
     }
     return $xt
@@ -5755,11 +5831,11 @@ 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 linknum curview pendinglinks
+    global ctext linknum curview
 
     set start [$ctext index "end - 1c"]
     $ctext insert end $text $tags
-    set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
+    set links [regexp -indices -all -inline {\m[0-9a-f]{6,40}\M} $text]
     foreach l $links {
        set s [lindex $l 0]
        set e [lindex $l 1]
@@ -5773,16 +5849,27 @@ proc appendwithlinks {text tags} {
 }
 
 proc setlink {id lk} {
-    global curview ctext pendinglinks commitinterest
+    global curview ctext pendinglinks
 
-    if {[commitinview $id $curview]} {
+    set known 0
+    if {[string length $id] < 40} {
+       set matches [longid $id]
+       if {[llength $matches] > 0} {
+           if {[llength $matches] > 1} return
+           set known 1
+           set id [lindex $matches 0]
+       }
+    } else {
+       set known [commitinview $id $curview]
+    }
+    if {$known} {
        $ctext tag conf $lk -foreground blue -underline 1
-       $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
+       $ctext tag bind $lk <1> [list selbyid $id]
        $ctext tag bind $lk <Enter> {linkcursor %W 1}
        $ctext tag bind $lk <Leave> {linkcursor %W -1}
     } else {
        lappend pendinglinks($id) $lk
-       lappend commitinterest($id) {makelink %I}
+       interestedin $id {makelink %P}
     }
 }
 
@@ -6228,7 +6315,7 @@ proc gettree {id} {
            set treepending $id
            set treefilelist($id) {}
            set treeidlist($id) {}
-           fconfigure $gtf -blocking 0
+           fconfigure $gtf -blocking 0 -encoding binary
            filerun $gtf [list gettreeline $gtf $id]
        }
     } else {
@@ -6250,11 +6337,12 @@ proc gettreeline {gtf id} {
            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]
-           }
            lappend treeidlist($id) $sha1
        }
+       if {[string index $fname 0] eq "\""} {
+           set fname [lindex $fname 0]
+       }
+       set fname [encoding convertfrom $fname]
        lappend treefilelist($id) $fname
     }
     if {![eof $gtf]} {
@@ -6295,7 +6383,7 @@ proc showfile {f} {
            return
        }
     }
-    fconfigure $bf -blocking 0
+    fconfigure $bf -blocking 0 -encoding [get_path_encoding $f]
     filerun $bf [list getblobline $bf $diffids]
     $ctext config -state normal
     clear_ctext $commentend
@@ -6333,6 +6421,7 @@ proc mergediff {id} {
     global diffids
     global parents
     global diffcontext
+    global diffencoding
     global limitdiffs vfilelimit curview
 
     set diffmergeid $id
@@ -6346,9 +6435,10 @@ proc mergediff {id} {
        error_popup "[mc "Error getting merge diffs:"] $err"
        return
     }
-    fconfigure $mdf -blocking 0
+    fconfigure $mdf -blocking 0 -encoding binary
     set mdifffd($id) $mdf
     set np [llength $parents($curview,$id)]
+    set diffencoding [get_path_encoding {}]
     settabs $np
     filerun $mdf [list getmergediffline $mdf $id $np]
 }
@@ -6356,6 +6446,7 @@ proc mergediff {id} {
 proc getmergediffline {mdf id np} {
     global diffmergeid ctext cflist mergemax
     global difffilestart mdifffd
+    global diffencoding
 
     $ctext conf -state normal
     set nr 0
@@ -6367,18 +6458,22 @@ proc getmergediffline {mdf id np} {
        }
        if {[regexp {^diff --cc (.*)} $line match fname]} {
            # start of a new file
+           set fname [encoding convertfrom $fname]
            $ctext insert end "\n"
            set here [$ctext index "end - 1c"]
            lappend difffilestart $here
            add_flist [list $fname]
+           set diffencoding [get_path_encoding $fname]
            set l [expr {(78 - [string length $fname]) / 2}]
            set pad [string range "----------------------------------------" 1 $l]
            $ctext insert end "$pad $fname $pad\n" filesep
        } elseif {[regexp {^@@} $line]} {
+           set line [encoding convertfrom $diffencoding $line]
            $ctext insert end "$line\n" hunksep
        } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
            # do nothing
        } else {
+           set line [encoding convertfrom $diffencoding $line]
            # parse the prefix - one ' ', '-' or '+' for each parent
            set spaces {}
            set minuses {}
@@ -6513,27 +6608,42 @@ proc gettreediffs {ids} {
 
     set treepending $ids
     set treediff {}
-    fconfigure $gdtf -blocking 0
+    fconfigure $gdtf -blocking 0 -encoding binary
     filerun $gdtf [list gettreediffline $gdtf $ids]
 }
 
 proc gettreediffline {gdtf ids} {
     global treediff treediffs treepending diffids diffmergeid
-    global cmitmode vfilelimit curview limitdiffs
+    global cmitmode vfilelimit curview limitdiffs perfile_attrs
 
     set nr 0
-    while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
+    set sublist {}
+    set max 1000
+    if {$perfile_attrs} {
+       # cache_gitattr is slow, and even slower on win32 where we
+       # have to invoke it for only about 30 paths at a time
+       set max 500
+       if {[tk windowingsystem] == "win32"} {
+           set max 120
+       }
+    }
+    while {[incr nr] <= $max && [gets $gdtf line] >= 0} {
        set i [string first "\t" $line]
        if {$i >= 0} {
            set file [string range $line [expr {$i+1}] end]
            if {[string index $file 0] eq "\""} {
                set file [lindex $file 0]
            }
+           set file [encoding convertfrom $file]
            lappend treediff $file
+           lappend sublist $file
        }
     }
+    if {$perfile_attrs} {
+       cache_gitattr encoding $sublist
+    }
     if {![eof $gdtf]} {
-       return [expr {$nr >= 1000? 2: 1}]
+       return [expr {$nr >= $max? 2: 1}]
     }
     close $gdtf
     if {$limitdiffs && $vfilelimit($curview) ne {}} {
@@ -6586,6 +6696,7 @@ proc getblobdiffs {ids} {
     global diffcontext
     global ignorespace
     global limitdiffs vfilelimit curview
+    global diffencoding
 
     set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
     if {$ignorespace} {
@@ -6599,7 +6710,8 @@ proc getblobdiffs {ids} {
        return
     }
     set diffinhdr 0
-    fconfigure $bdf -blocking 0
+    set diffencoding [get_path_encoding {}]
+    fconfigure $bdf -blocking 0 -encoding binary
     set blobdifffd($ids) $bdf
     filerun $bdf [list getblobdiffline $bdf $diffids]
 }
@@ -6633,6 +6745,7 @@ proc getblobdiffline {bdf ids} {
     global diffids blobdifffd ctext curdiffstart
     global diffnexthead diffnextnote difffilestart
     global diffinhdr treediffs
+    global diffencoding
 
     set nr 0
     $ctext conf -state normal
@@ -6670,10 +6783,13 @@ proc getblobdiffline {bdf ids} {
            } else {
                set fname [string range $line 2 [expr {$i - 1}]]
            }
+           set fname [encoding convertfrom $fname]
+           set diffencoding [get_path_encoding $fname]
            makediffhdr $fname $ids
 
        } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
                       $line match f1l f1c f2l f2c rest]} {
+           set line [encoding convertfrom $diffencoding $line]
            $ctext insert end "$line\n" hunksep
            set diffinhdr 0
 
@@ -6683,6 +6799,7 @@ proc getblobdiffline {bdf ids} {
                if {[string index $fname 0] eq "\""} {
                    set fname [lindex $fname 0]
                }
+               set fname [encoding convertfrom $fname]
                set i [lsearch -exact $treediffs($ids) $fname]
                if {$i >= 0} {
                    setinlist difffilestart $i $curdiffstart
@@ -6693,6 +6810,8 @@ proc getblobdiffline {bdf ids} {
                if {[string index $fname 0] eq "\""} {
                    set fname [lindex $fname 0]
                }
+               set fname [encoding convertfrom $fname]
+               set diffencoding [get_path_encoding $fname]
                makediffhdr $fname $ids
            } elseif {[string compare -length 3 $line "---"] == 0} {
                # do nothing
@@ -6704,6 +6823,7 @@ proc getblobdiffline {bdf ids} {
            $ctext insert end "$line\n" filesep
 
        } else {
+           set line [encoding convertfrom $diffencoding $line]
            set x [string range $line 0 0]
            if {$x == "-" || $x == "+"} {
                set tag [expr {$x == "+"}]
@@ -7065,13 +7185,13 @@ proc gotocommit {} {
     } else {
        set id [string tolower $sha1string]
        if {[regexp {^[0-9a-f]{4,39}$} $id]} {
-           set matches [array names varcid "$curview,$id*"]
+           set matches [longid $id]
            if {$matches ne {}} {
                if {[llength $matches] > 1} {
                    error_popup [mc "Short SHA1 id %s is ambiguous" $id]
                    return
                }
-               set id [lindex [split [lindex $matches 0] ","] 1]
+               set id [lindex $matches 0]
            }
        }
     }
@@ -7288,9 +7408,9 @@ proc rowmenu {x y id} {
     } else {
        set menu $fakerowmenu
     }
-    $menu entryconfigure [mc "Diff this -> selected"] -state $state
-    $menu entryconfigure [mc "Diff selected -> this"] -state $state
-    $menu entryconfigure [mc "Make patch"] -state $state
+    $menu entryconfigure [mca "Diff this -> selected"] -state $state
+    $menu entryconfigure [mca "Diff selected -> this"] -state $state
+    $menu entryconfigure [mca "Make patch"] -state $state
     tk_popup $menu $x $y
 }
 
@@ -7590,6 +7710,7 @@ proc mkbranch {} {
     grid $top.id $top.sha1 -sticky w
     label $top.nlab -text [mc "Name:"]
     entry $top.name -width 40
+    bind $top.name <Key-Return> "[list mkbrgo $top]"
     grid $top.nlab $top.name -sticky w
     frame $top.buts
     button $top.buts.go -text [mc "Create"] -command [list mkbrgo $top]
@@ -7918,7 +8039,7 @@ proc reflistfilter_change {n1 n2 op} {
 
 proc refill_reflist {} {
     global reflist reflistfilter showrefstop headids tagids otherrefids
-    global curview commitinterest
+    global curview
 
     if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
     set refs {}
@@ -7927,7 +8048,7 @@ proc refill_reflist {} {
            if {[commitinview $headids($n) $curview]} {
                lappend refs [list $n H]
            } else {
-               set commitinterest($headids($n)) {run refill_reflist}
+               interestedin $headids($n) {run refill_reflist}
            }
        }
     }
@@ -7936,7 +8057,7 @@ proc refill_reflist {} {
            if {[commitinview $tagids($n) $curview]} {
                lappend refs [list $n T]
            } else {
-               set commitinterest($tagids($n)) {run refill_reflist}
+               interestedin $tagids($n) {run refill_reflist}
            }
        }
     }
@@ -7945,7 +8066,7 @@ proc refill_reflist {} {
            if {[commitinview $otherrefids($n) $curview]} {
                lappend refs [list $n o]
            } else {
-               set commitinterest($otherrefids($n)) {run refill_reflist}
+               interestedin $otherrefids($n) {run refill_reflist}
            }
        }
     }
@@ -9295,7 +9416,7 @@ proc doprefs {} {
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global bgcolor fgcolor ctext diffcolors selectbgcolor
-    global tabstop limitdiffs autoselect extdifftool
+    global tabstop limitdiffs autoselect extdifftool perfile_attrs
 
     set top .gitkprefs
     set prefstop $top
@@ -9304,7 +9425,7 @@ proc doprefs {} {
        return
     }
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-                  limitdiffs tabstop} {
+                  limitdiffs tabstop perfile_attrs} {
        set oldprefs($v) [set $v]
     }
     toplevel $top
@@ -9346,6 +9467,11 @@ proc doprefs {} {
     checkbutton $top.ldiff.b -variable limitdiffs
     pack $top.ldiff.b $top.ldiff.l -side left
     grid x $top.ldiff -sticky w
+    frame $top.lattr
+    label $top.lattr.l -text [mc "Support per-file encodings"] -font optionfont
+    checkbutton $top.lattr.b -variable perfile_attrs
+    pack $top.lattr.b $top.lattr.l -side left
+    grid x $top.lattr -sticky w
 
     entry $top.extdifft -textvariable extdifftool
     frame $top.extdifff
@@ -9455,7 +9581,7 @@ proc prefscan {} {
     global oldprefs prefstop
 
     foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
-                  limitdiffs tabstop} {
+                  limitdiffs tabstop perfile_attrs} {
        global $v
        set $v $oldprefs($v)
     }
@@ -9468,7 +9594,7 @@ proc prefsok {} {
     global maxwidth maxgraphpct
     global oldprefs prefstop showneartags showlocalchanges
     global fontpref mainfont textfont uifont
-    global limitdiffs treediffs
+    global limitdiffs treediffs perfile_attrs
 
     catch {destroy $prefstop}
     unset prefstop
@@ -9501,8 +9627,10 @@ proc prefsok {} {
            dohidelocalchanges
        }
     }
-    if {$limitdiffs != $oldprefs(limitdiffs)} {
-       # treediffs elements are limited by path
+    if {$limitdiffs != $oldprefs(limitdiffs) ||
+       ($perfile_attrs && !$oldprefs(perfile_attrs))} {
+       # treediffs elements are limited by path;
+       # won't have encodings cached if perfile_attrs was just turned on
        catch {unset treediffs}
     }
     if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
@@ -9726,7 +9854,7 @@ set encoding_aliases {
     { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
     { GBK CP936 MS936 windows-936 }
     { JIS_Encoding csJISEncoding }
-    { Shift_JIS MS_Kanji csShiftJIS }
+    { Shift_JIS MS_Kanji csShiftJIS ShiftJIS Shift-JIS }
     { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
       EUC-JP }
     { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
@@ -9761,14 +9889,17 @@ set encoding_aliases {
 }
 
 proc tcl_encoding {enc} {
-    global encoding_aliases
+    global encoding_aliases tcl_encoding_cache
+    if {[info exists tcl_encoding_cache($enc)]} {
+       return $tcl_encoding_cache($enc)
+    }
     set names [encoding names]
     set lcnames [string tolower $names]
     set enc [string tolower $enc]
     set i [lsearch -exact $lcnames $enc]
     if {$i < 0} {
        # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
-       if {[regsub {^iso[-_]} $enc iso encx]} {
+       if {[regsub {^(iso|cp|ibm|jis)[-_]} $enc {\1} encx]} {
            set i [lsearch -exact $lcnames $encx]
        }
     }
@@ -9780,7 +9911,7 @@ proc tcl_encoding {enc} {
            foreach e $ll {
                set i [lsearch -exact $lcnames $e]
                if {$i < 0} {
-                   if {[regsub {^iso[-_]} $e iso ex]} {
+                   if {[regsub {^(iso|cp|ibm|jis)[-_]} $e {\1} ex]} {
                        set i [lsearch -exact $lcnames $ex]
                    }
                }
@@ -9789,10 +9920,70 @@ proc tcl_encoding {enc} {
            break
        }
     }
+    set tclenc {}
     if {$i >= 0} {
-       return [lindex $names $i]
+       set tclenc [lindex $names $i]
     }
-    return {}
+    set tcl_encoding_cache($enc) $tclenc
+    return $tclenc
+}
+
+proc gitattr {path attr default} {
+    global path_attr_cache
+    if {[info exists path_attr_cache($attr,$path)]} {
+       set r $path_attr_cache($attr,$path)
+    } else {
+       set r "unspecified"
+       if {![catch {set line [exec git check-attr $attr -- $path]}]} {
+           regexp "(.*): encoding: (.*)" $line m f r
+       }
+       set path_attr_cache($attr,$path) $r
+    }
+    if {$r eq "unspecified"} {
+       return $default
+    }
+    return $r
+}
+
+proc cache_gitattr {attr pathlist} {
+    global path_attr_cache
+    set newlist {}
+    foreach path $pathlist {
+       if {![info exists path_attr_cache($attr,$path)]} {
+           lappend newlist $path
+       }
+    }
+    set lim 1000
+    if {[tk windowingsystem] == "win32"} {
+       # windows has a 32k limit on the arguments to a command...
+       set lim 30
+    }
+    while {$newlist ne {}} {
+       set head [lrange $newlist 0 [expr {$lim - 1}]]
+       set newlist [lrange $newlist $lim end]
+       if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
+           foreach row [split $rlist "\n"] {
+               if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+                   if {[string index $path 0] eq "\""} {
+                       set path [encoding convertfrom [lindex $path 0]]
+                   }
+                   set path_attr_cache($attr,$path) $value
+               }
+           }
+       }
+    }
+}
+
+proc get_path_encoding {path} {
+    global gui_encoding perfile_attrs
+    set tcl_enc $gui_encoding
+    if {$path ne {} && $perfile_attrs} {
+       set enc2 [tcl_encoding [gitattr $path encoding $tcl_enc]]
+       if {$enc2 ne {}} {
+           set tcl_enc $enc2
+       }
+    }
+    return $tcl_enc
 }
 
 # First check that Tcl/Tk is recent enough
@@ -9817,6 +10008,19 @@ if {$tclencoding == {}} {
     puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
 }
 
+set gui_encoding [encoding system]
+catch {
+    set enc [exec git config --get gui.encoding]
+    if {$enc ne {}} {
+       set tclenc [tcl_encoding $enc]
+       if {$tclenc ne {}} {
+           set gui_encoding $tclenc
+       } else {
+           puts stderr "Warning: encoding $enc is not supported by Tcl/Tk"
+       }
+    }
+}
+
 set mainfont {Helvetica 9}
 set textfont {Courier 9}
 set uifont {Helvetica 9 bold}
@@ -9838,6 +10042,7 @@ set showlocalchanges 1
 set limitdiffs 1
 set datetimeformat "%Y-%m-%d %H:%M:%S"
 set autoselect 1
+set perfile_attrs 0
 
 set extdifftool "meld"
 
@@ -9851,6 +10056,13 @@ set selectbgcolor gray85
 
 set circlecolors {white blue gray blue blue}
 
+# button for popping up context menus
+if {[tk windowingsystem] eq "aqua"} {
+    set ctxbut <Button-2>
+} else {
+    set ctxbut <Button-3>
+}
+
 ## For msgcat loading, first locate the installation location.
 if { [info exists ::env(GITK_MSGSDIR)] } {
     ## Msgsdir was manually set in the environment.
@@ -10019,8 +10231,8 @@ if {$cmdline_files ne {} || $revtreeargs ne {} || $revtreeargscmd ne {}} {
     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
+    .bar.view entryconf [mca "Edit view..."] -state normal
+    .bar.view entryconf [mca "Delete view"] -state normal
 }
 
 if {[info exists permviews]} {
index 04ee5709951b4ea4c63b495fd222fcf4b5397afc..c86cc2df5ea8b771cd56233e4790ff57a47d6e49 100644 (file)
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: git-gui\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-05-24 22:32+0200\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-05-24 22:40+0200\n"
 "Last-Translator: Christian Stimming <stimming@tuhh.de>\n"
 "Language-Team: German\n"
@@ -15,17 +15,17 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:102
+#: gitk:113
 msgid "Couldn't get list of unmerged files:"
 msgstr "Liste der nicht-zusammengeführten Dateien nicht gefunden:"
 
-#: gitk:329
+#: gitk:340
 msgid "No files selected: --merge specified but no files are unmerged."
 msgstr ""
 "Keine Dateien ausgewählt: --merge angegeben, es existieren aber keine nicht-"
 "zusammengeführten Dateien."
 
-#: gitk:332
+#: gitk:343
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -33,257 +33,261 @@ msgstr ""
 "Keine Dateien ausgewähle: --merge angegeben, aber keine nicht-"
 "zusammengeführten Dateien sind in der Dateiauswahl."
 
-#: gitk:354
+#: gitk:365 gitk:503
 msgid "Error executing git log:"
 msgstr "Fehler beim Ausführen von git-log:"
 
-#: gitk:369
+#: gitk:378
 msgid "Reading"
 msgstr "Lesen"
 
-#: gitk:151 gitk:2191
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Versionen lesen..."
 
-#: gitk:275
-msgid "Can't parse git log output:"
-msgstr "Ausgabe von git-log kann nicht erkannt werden:"
-
-#: gitk:386 gitk:2195
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "Keine Versionen ausgewählt."
 
-#: gitk:500
+#: gitk:1399
+msgid "Can't parse git log output:"
+msgstr "Ausgabe von git-log kann nicht erkannt werden:"
+
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Keine Versionsinformation verfügbar"
 
-#: gitk:599 gitk:621 gitk:1955 gitk:6424 gitk:7924 gitk:8083
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "Ok"
 
-#: gitk:623 gitk:1956 gitk:6108 gitk:6179 gitk:6276 gitk:6322 gitk:6426
-#: gitk:7925 gitk:8084
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Abbrechen"
 
-#: gitk:661
-msgid "File"
-msgstr "Datei"
-
-#: gitk:663
+#: gitk:1811
 msgid "Update"
 msgstr "Aktualisieren"
 
-#: gitk:1722
+#: gitk:1812
 msgid "Reload"
 msgstr "Neu laden"
 
-#: gitk:1723
+#: gitk:1813
 msgid "Reread references"
 msgstr "Zweige neu laden"
 
-#: gitk:665
+#: gitk:1814
 msgid "List references"
 msgstr "Zweige/Markierungen auflisten"
 
-#: gitk:666
+#: gitk:1815
 msgid "Quit"
 msgstr "Beenden"
 
-#: gitk:668
-msgid "Edit"
-msgstr "Bearbeiten"
+#: gitk:1810
+msgid "File"
+msgstr "Datei"
 
-#: gitk:669
+#: gitk:1818
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: gitk:672 gitk:1892
-msgid "View"
-msgstr "Ansicht"
+#: gitk:1817
+msgid "Edit"
+msgstr "Bearbeiten"
 
-#: gitk:673
+#: gitk:1821
 msgid "New view..."
 msgstr "Neue Ansicht..."
 
-#: gitk:674 gitk:2133 gitk:8723
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Ansicht bearbeiten..."
 
-#: gitk:676 gitk:2134 gitk:8724
+#: gitk:1823
 msgid "Delete view"
 msgstr "Ansicht löschen"
 
-#: gitk:678
+#: gitk:1825
 msgid "All files"
 msgstr "Alle Dateien"
 
-#: gitk:682
-msgid "Help"
-msgstr "Hilfe"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Ansicht"
 
-#: gitk:683 gitk:1317
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Über gitk"
 
-#: gitk:684
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Tastenkürzel"
 
-#: gitk:741
+#: gitk:1827
+msgid "Help"
+msgstr "Hilfe"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1:"
 
-#: gitk:1831
+#: gitk:1918
 msgid "Row"
 msgstr "Zeile"
 
-#: gitk:1862
+#: gitk:1949
 msgid "Find"
 msgstr "Suche"
 
-#: gitk:792
+#: gitk:1950
 msgid "next"
 msgstr "nächste"
 
-#: gitk:793
+#: gitk:1951
 msgid "prev"
 msgstr "vorige"
 
-#: gitk:794
+#: gitk:1952
 msgid "commit"
 msgstr "Version nach"
 
-#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "Beschreibung:"
 
-#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "Dateien:"
 
-#: gitk:801 gitk:2436
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "Änderungen:"
 
-#: gitk:810 gitk:812
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:812 gitk:2514 gitk:4274
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr "Kein Groß/Klein"
 
-#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr "Regexp"
 
-#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Alle Felder"
 
-#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Überschrift"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Beschreibung"
 
-#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5957
-#: gitk:5972
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr "Eintragender"
 
-#: gitk:845
+#: gitk:2003
 msgid "Search"
 msgstr "Suche"
 
-#: gitk:852
+#: gitk:2010
 msgid "Diff"
 msgstr "Vergleich"
 
-#: gitk:854
+#: gitk:2012
 msgid "Old version"
 msgstr "Alte Version"
 
-#: gitk:856
+#: gitk:2014
 msgid "New version"
 msgstr "Neue Version"
 
-#: gitk:858
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Kontextzeilen"
 
-#: gitk:868
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Leerzeichenänderungen ignorieren"
 
-#: gitk:926
+#: gitk:2084
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:928
+#: gitk:2086
 msgid "Tree"
 msgstr "Baum"
 
-#: gitk:1053 gitk:1068 gitk:6023
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Vergleich diese -> gewählte"
 
-#: gitk:1055 gitk:1070 gitk:6024
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Vergleich gewählte -> diese"
 
-#: gitk:1057 gitk:1072 gitk:6025
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Patch erstellen"
 
-#: gitk:1058 gitk:6163
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Markierung erstellen"
 
-#: gitk:1059 gitk:6256
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Version in Datei schreiben"
 
-#: gitk:1060 gitk:6310
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Neuen Zweig erstellen"
 
-#: gitk:1061
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Diese Version pflücken"
 
-#: gitk:1063
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "HEAD-Zweig auf diese Version zurücksetzen"
 
-#: gitk:1079
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Auf diesen Zweig umstellen"
 
-#: gitk:1081
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Zweig löschen"
 
-#: gitk:1087
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Diesen auch hervorheben"
 
-#: gitk:1089
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Nur diesen hervorheben"
 
-#: gitk:2162
+#: gitk:2244
 msgid "External diff"
 msgstr "Externer Vergleich"
 
-#: gitk:2403
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -297,431 +301,431 @@ msgstr ""
 "\n"
 "Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
-"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public License"
+"Benutzung und Weiterverbreitung gemäß den Bedingungen der GNU General Public "
+"License"
 
-#: gitk:1326 gitk:1387 gitk:6582
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Schließen"
 
-#: gitk:1345
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Gitk Tastaturbelegung"
 
-#: gitk:1347
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Gitk Tastaturbelegung:"
 
-#: gitk:1349
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tBeenden"
 
-#: gitk:1350
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Pos1>\t\tZur neuesten Version springen"
 
-#: gitk:1351
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<Ende>\t\tZur ältesten Version springen"
 
-#: gitk:1352
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Hoch>, p, i\tNächste neuere Version"
 
-#: gitk:1353
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Runter>, n, k\tNächste ältere Version"
 
-#: gitk:1354
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Links>, z, j\tEine Version zurückgehen"
 
-#: gitk:1355
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Rechts>, x, l\tEine Version weitergehen"
 
-#: gitk:1356
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<BildHoch>\tEine Seite nach oben blättern"
 
-#: gitk:1357
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<BildRunter>\tEine Seite nach unten blättern"
 
-#: gitk:1358
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Pos1>\tZum oberen Ende der Versionsliste blättern"
 
-#: gitk:1359
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-Ende>\tZum unteren Ende der Versionsliste blättern"
 
-#: gitk:1360
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Hoch>\tVersionsliste eine Zeile nach oben blättern"
 
-#: gitk:1361
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Runter>\tVersionsliste eine Zeile nach unten blättern"
 
-#: gitk:1362
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-BildHoch>\tVersionsliste eine Seite hoch blättern"
 
-#: gitk:1363
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-BildRunter>\tVersionsliste eine Seite nach unten blättern"
 
-#: gitk:1364
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Umschalt-Hoch>\tRückwärts suchen (nach oben; neuere Versionen)"
 
-#: gitk:1365
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Umschalt-Runter> Suchen (nach unten; ältere Versionen)"
 
-#: gitk:1366
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Entf>, b\t\tVergleich eine Seite nach oben blättern"
 
-#: gitk:1367
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Löschtaste>\tVergleich eine Seite nach oben blättern"
 
-#: gitk:1368
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Leertaste>\tVergleich eine Seite nach unten blättern"
 
-#: gitk:1369
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tVergleich um 18 Zeilen nach oben (»up«) blättern"
 
-#: gitk:1370
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tVergleich um 18 Zeilen nach unten (»down«) blättern"
 
-#: gitk:1371
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSuchen"
 
-#: gitk:1372
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tWeitersuchen"
 
-#: gitk:1373
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Eingabetaste>\tWeitersuchen"
 
-#: gitk:1374
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tWeitersuchen oder neue Suche beginnen"
 
-#: gitk:1375
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tRückwärts weitersuchen"
 
-#: gitk:1376
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tVergleich zur nächsten Datei (»file«) blättern"
 
-#: gitk:1377
+#: gitk:2547
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tWeitersuchen im Vergleich"
 
-#: gitk:1378
+#: gitk:2548
 #, 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
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Nummerblock-Plus>\tSchriftgröße vergrößern"
 
-#: gitk:1380
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-Plus>\tSchriftgröße vergrößern"
 
-#: gitk:1381
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Nummernblock-> Schriftgröße verkleinern"
 
-#: gitk:1382
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-Minus>\tSchriftgröße verkleinern"
 
-#: gitk:1383
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAktualisieren"
 
-#: gitk:1896
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Gitk Ansichten"
 
-#: gitk:1921
+#: gitk:3225
 msgid "Name"
 msgstr "Name"
 
-#: gitk:1924
+#: gitk:3228
 msgid "Remember this view"
 msgstr "Diese Ansicht speichern"
 
-#: gitk:3126
+#: gitk:3232
 msgid "Commits to include (arguments to git log):"
 msgstr "Versionen anzeigen (Argumente von git-log):"
 
-#: gitk:3133
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Versionsliste durch folgendes Kommando erzeugen lassen:"
 
-#: gitk:1942
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Folgende Dateien und Verzeichnisse anzeigen (eine pro Zeile):"
 
-#: gitk:1989
+#: gitk:3293
 msgid "Error in commit selection arguments:"
 msgstr "Fehler in den ausgewählten Versionen:"
 
-#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8689 gitk:8690
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Keine"
 
-#: gitk:2531 gitk:4336 gitk:5959 gitk:5974
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:2531 gitk:4336
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr "Eintragedatum"
 
-#: gitk:2680 gitk:2685
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Abkömmling"
 
-#: gitk:2681
+#: gitk:3940
 msgid "Not descendant"
 msgstr "Nicht Abkömmling"
 
-#: gitk:2688 gitk:2693
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Vorgänger"
 
-#: gitk:2689
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "Nicht Vorgänger"
 
-#: gitk:2924
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokale Änderungen bereitgestellt, aber nicht eingetragen"
 
-#: gitk:2954
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokale Änderungen, nicht bereitgestellt"
 
-#: gitk:4305
+#: gitk:5549
 msgid "Searching"
 msgstr "Suchen"
 
-#: gitk:4767
+#: gitk:6049
 msgid "Tags:"
 msgstr "Markierungen:"
 
-#: gitk:4784 gitk:4790 gitk:5952
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Eltern"
 
-#: gitk:4795
+#: gitk:6077
 msgid "Child"
 msgstr "Kind"
 
-#: gitk:4804
+#: gitk:6086
 msgid "Branch"
 msgstr "Zweig"
 
-#: gitk:4807
+#: gitk:6089
 msgid "Follows"
 msgstr "Folgt auf"
 
-#: gitk:4810
+#: gitk:6092
 msgid "Precedes"
 msgstr "Vorgänger von"
 
-#: gitk:5094
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Fehler beim Laden des Vergleichs:"
 
-#: gitk:5779
+#: gitk:7113
 msgid "Goto:"
 msgstr "Gehe zu:"
 
-#: gitk:5781
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1-Hashwert:"
 
-#: gitk:5806
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Kurzer SHA1-Hashwert »%s« ist mehrdeutig"
 
-#: gitk:5818
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA1-Hashwert »%s« unbekannt"
 
-#: gitk:5820
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Markierung/Zweig »%s« ist unbekannt"
 
-#: gitk:5962
+#: gitk:7290
 msgid "Children"
 msgstr "Kinder"
 
-#: gitk:6019
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Zweig »%s« hierher zurücksetzen"
 
-#: gitk:7204
+#: gitk:7349
 msgid "Detached head: can't reset"
 msgstr "Zweigspitze ist abgetrennt: Zurücksetzen nicht möglich"
 
-#: gitk:7236
+#: gitk:7381
 msgid "Top"
 msgstr "Oben"
 
-#: gitk:6051
+#: gitk:7382
 msgid "From"
 msgstr "Von"
 
-#: gitk:6056
+#: gitk:7387
 msgid "To"
 msgstr "bis"
 
-#: gitk:6079
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Patch erstellen"
 
-#: gitk:6081
+#: gitk:7412
 msgid "From:"
 msgstr "Von:"
 
-#: gitk:6090
+#: gitk:7421
 msgid "To:"
 msgstr "bis:"
 
-#: gitk:6099
+#: gitk:7430
 msgid "Reverse"
 msgstr "Umgekehrt"
 
-#: gitk:6101 gitk:6270
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Ausgabedatei:"
 
-#: gitk:6107
+#: gitk:7438
 msgid "Generate"
 msgstr "Erzeugen"
 
-#: gitk:6143
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Fehler beim Patch erzeugen:"
 
-#: gitk:6165 gitk:6258 gitk:6312
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6174
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Markierungsname:"
 
-#: gitk:6178 gitk:6321
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Erstellen"
 
-#: gitk:6193
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "Kein Markierungsname angegeben"
 
-#: gitk:6197
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Markierung »%s« existiert bereits."
 
-#: gitk:6203
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Fehler bei Markierung erstellen:"
 
-#: gitk:6267
+#: gitk:7604
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:6275
+#: gitk:7612
 msgid "Write"
 msgstr "Schreiben"
 
-#: gitk:6291
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Fehler beim Schreiben der Version:"
 
-#: gitk:6317
+#: gitk:7654
 msgid "Name:"
 msgstr "Name:"
 
-#: gitk:6336
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Bitte geben Sie einen Namen für den neuen Zweig an."
 
-#: gitk:6365
+#: gitk:7703
 #, 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?"
+"Version »%s« ist bereits im Zweig »%s« enthalten -- trotzdem erneut eintragen?"
 
-#: gitk:6370
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr "Version pflücken"
 
-#: gitk:6382
+#: gitk:7720
 msgid "No changes committed"
 msgstr "Keine Änderungen eingetragen"
 
-#: gitk:6405
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Zurücksetzen bestätigen"
 
-#: gitk:6407
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Zweig »%s« auf »%s« zurücksetzen?"
 
-#: gitk:6411
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Art des Zurücksetzens:"
 
-#: gitk:6415
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Harmlos: Arbeitskopie und Bereitstellung unverändert"
 
-#: gitk:6418
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr ""
 "Gemischt: Arbeitskopie unverändert,\n"
 "Bereitstellung zurückgesetzt"
 
-#: gitk:6421
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -729,21 +733,21 @@ msgstr ""
 "Hart: Arbeitskopie und Bereitstellung\n"
 "(Alle lokalen Änderungen werden gelöscht)"
 
-#: gitk:6437
+#: gitk:7777
 msgid "Resetting"
 msgstr "Zurücksetzen"
 
-#: gitk:6494
+#: gitk:7834
 msgid "Checking out"
 msgstr "Umstellen"
 
-#: gitk:6524
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr ""
 "Der Zweig, auf den die Arbeitskopie momentan umgestellt ist, kann nicht "
 "gelöscht werden."
 
-#: gitk:6530
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -752,16 +756,16 @@ msgstr ""
 "Die Versionen auf Zweig »%s« existieren auf keinem anderen Zweig.\n"
 "Zweig »%s« trotzdem löschen?"
 
-#: gitk:6561
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Markierungen und Zweige: %s"
 
-#: gitk:6575
+#: gitk:7936
 msgid "Filter"
 msgstr "Filtern"
 
-#: gitk:6869
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -769,125 +773,129 @@ msgstr ""
 "Fehler beim Lesen der Strukturinformationen; Zweige und Vorgänger/Nachfolger "
 "Informationen werden unvollständig sein."
 
-#: gitk:7853
+#: gitk:9216
 msgid "Tag"
 msgstr "Markierung"
 
-#: gitk:7853
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7893
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Gitk Schriften wählen"
 
-#: gitk:7910
+#: gitk:9279
 msgid "B"
 msgstr "F"
 
-#: gitk:7913
+#: gitk:9282
 msgid "I"
 msgstr "K"
 
-#: gitk:8006
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Gitk Einstellungen"
 
-#: gitk:8007
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Anzeige Versionsliste"
 
-#: gitk:8010
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Maximale Graphenbreite (Zeilen)"
 
-#: gitk:8014
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximale Graphenbreite (% des Fensters)"
 
-#: gitk:8019
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Lokale Änderungen anzeigen"
 
-#: gitk:8024
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "SHA1-Hashwert automatisch markieren"
 
-#: gitk:8029
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Anzeige Vergleich"
 
-#: gitk:8031
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Tabulatorbreite"
 
-#: gitk:8035
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Naheliegende Überschriften anzeigen"
 
-#: gitk:8040
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Vergleich nur für angezeigte Pfade"
 
-#: gitk:9264
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
 msgid "External diff tool"
 msgstr "Externes Vergleich-(Diff-)Programm"
 
-#: gitk:9266
+#: gitk:9423
 msgid "Choose..."
 msgstr "Wählen..."
 
-#: gitk:9271
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Farben: Klicken zum Wählen"
 
-#: gitk:8048
+#: gitk:9431
 msgid "Background"
 msgstr "Hintergrund"
 
-#: gitk:8052
+#: gitk:9435
 msgid "Foreground"
 msgstr "Vordergrund"
 
-#: gitk:8056
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Vergleich: Alte Zeilen"
 
-#: gitk:8061
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Vergleich: Neue Zeilen"
 
-#: gitk:8066
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Vergleich: Änderungstitel"
 
-#: gitk:8072
+#: gitk:9455
 msgid "Select bg"
 msgstr "Hintergrundfarbe Auswählen"
 
-#: gitk:8076
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Schriftart: Klicken zum Wählen"
 
-#: gitk:8078
+#: gitk:9461
 msgid "Main font"
 msgstr "Programmschriftart"
 
-#: gitk:8079
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Vergleich"
 
-#: gitk:8080
+#: gitk:9463
 msgid "User interface font"
 msgstr "Beschriftungen"
 
-#: gitk:8096
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: Farbe wählen für %s"
 
-#: gitk:8477
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -895,24 +903,24 @@ msgstr ""
 "Gitk läuft nicht mit dieser Version von Tcl/Tk.\n"
 "Gitk benötigt mindestens Tcl/Tk 8.4."
 
-#: gitk:8566
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "Kein Git-Projektarchiv gefunden."
 
-#: gitk:8570
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Git-Verzeichnis »%s« wurde nicht gefunden."
 
-#: gitk:8613
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Mehrdeutige Angabe »%s«: Sowohl Version als auch Dateiname existiert."
 
-#: gitk:8625
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Falsche Kommandozeilen-Parameter für gitk:"
 
-#: gitk:9915
+#: gitk:10170
 msgid "Command line"
 msgstr "Kommandozeile"
index 2cb148624723adf82fd7f4a361133dbc8909db93..0e19b5eae27ed10e6cae524e9dc69cca1b1f428f 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gitk\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-03-25 11:20+0100\n"
 "Last-Translator: Santiago Gala <santiago.gala@gmail.com>\n"
 "Language-Team: Spanish\n"
@@ -16,676 +16,702 @@ msgstr ""
 "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:113
+msgid "Couldn't get list of unmerged files:"
+msgstr "Imposible obtener la lista de archivos pendientes de fusión:"
 
-#: gitk:124
+#: gitk:340
+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:343
+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:378
 msgid "Reading"
 msgstr "Leyendo"
 
-#: gitk:151 gitk:2191
+#: gitk:438 gitk:3462
 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
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "No se seleccionaron revisiones"
 
-#: gitk:500
+#: gitk:1399
+msgid "Can't parse git log output:"
+msgstr "Error analizando la salida de git log:"
+
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Falta información sobre las revisiones"
 
-#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "Aceptar"
 
-#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
-#: gitk:7924 gitk:8083
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Cancelar"
 
-#: gitk:661
-msgid "File"
-msgstr "Archivo"
-
-#: gitk:663
+#: gitk:1811
 msgid "Update"
 msgstr "Actualizar"
 
-#: gitk:664
+#: gitk:1813
 msgid "Reread references"
 msgstr "Releer referencias"
 
-#: gitk:665
+#: gitk:1814
 msgid "List references"
 msgstr "Lista de referencias"
 
-#: gitk:666
+#: gitk:1815
 msgid "Quit"
 msgstr "Salir"
 
-#: gitk:668
-msgid "Edit"
-msgstr "Editar"
+#: gitk:1810
+msgid "File"
+msgstr "Archivo"
 
-#: gitk:669
+#: gitk:1818
 msgid "Preferences"
 msgstr "Preferencias"
 
-#: gitk:672 gitk:1892
-msgid "View"
-msgstr "Vista"
+#: gitk:1817
+msgid "Edit"
+msgstr "Editar"
 
-#: gitk:673
+#: gitk:1821
 msgid "New view..."
 msgstr "Nueva vista..."
 
-#: gitk:674 gitk:2133 gitk:8722
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Modificar vista..."
 
-#: gitk:676 gitk:2134 gitk:8723
+#: gitk:1823
 msgid "Delete view"
 msgstr "Eliminar vista"
 
-#: gitk:678
+#: gitk:1825
 msgid "All files"
 msgstr "Todos los archivos"
 
-#: gitk:682
-msgid "Help"
-msgstr "Ayuda"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Vista"
 
-#: gitk:683 gitk:1317
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Acerca de gitk"
 
-#: gitk:684
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Combinaciones de teclas"
 
-#: gitk:741
+#: gitk:1827
+msgid "Help"
+msgstr "Ayuda"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1 ID: "
 
-#: gitk:791
+#: gitk:1918
+msgid "Row"
+msgstr ""
+
+#: gitk:1949
 msgid "Find"
 msgstr "Buscar"
 
-#: gitk:792
+#: gitk:1950
 msgid "next"
 msgstr "<<"
 
-#: gitk:793
+#: gitk:1951
 msgid "prev"
 msgstr ">>"
 
-#: gitk:794
+#: gitk:1952
 msgid "commit"
 msgstr "revisión"
 
-#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "que contiene:"
 
-#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "que modifica la ruta:"
 
-#: gitk:801 gitk:2436
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "que añade/elimina cadena:"
 
-#: gitk:810 gitk:812
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Exacto"
 
-#: gitk:812 gitk:2514 gitk:4274
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr "NoMayús"
 
-#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr "Regex"
 
-#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Todos los campos"
 
-#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Título"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Comentarios"
 
-#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
-#: gitk:5971
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Autor"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr ""
 
-#: gitk:845
+#: gitk:2003
 msgid "Search"
 msgstr "Buscar"
 
-#: gitk:852
+#: gitk:2010
 msgid "Diff"
 msgstr "Diferencia"
 
-#: gitk:854
+#: gitk:2012
 msgid "Old version"
 msgstr "Versión antigua"
 
-#: gitk:856
+#: gitk:2014
 msgid "New version"
 msgstr "Versión nueva"
 
-#: gitk:858
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Líneas de contexto"
 
-#: gitk:868
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Ignora cambios de espaciado"
 
-#: gitk:926
+#: gitk:2084
 msgid "Patch"
 msgstr "Parche"
 
-#: gitk:928
+#: gitk:2086
 msgid "Tree"
 msgstr "Árbol"
 
-#: gitk:1053 gitk:1068 gitk:6022
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Diferencia de esta -> seleccionada"
 
-#: gitk:1055 gitk:1070 gitk:6023
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Diferencia de seleccionada -> esta"
 
-#: gitk:1057 gitk:1072 gitk:6024
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Crear patch"
 
-#: gitk:1058 gitk:6162
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Crear etiqueta"
 
-#: gitk:1059 gitk:6255
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Escribir revisiones a archivo"
 
-#: gitk:1060 gitk:6309
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Crear nueva rama"
 
-#: gitk:1061
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Añadir esta revisión a la rama actual (cherry-pick)"
 
-#: gitk:1063
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "Traer la rama HEAD aquí"
 
-#: gitk:1079
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Cambiar a esta rama"
 
-#: gitk:1081
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Eliminar esta rama"
 
-#: gitk:1087
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Seleccionar también"
 
-#: gitk:1089
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Seleccionar sólo"
 
-#: gitk:1318
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 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"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
-"Uso y redistribución permitidos según los términos de la Licencia Pública General de "
-"GNU (GNU GPL)"
+"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
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Cerrar"
 
-#: gitk:1345
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Combinaciones de tecla de Gitk"
 
-#: gitk:1347
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Combinaciones de tecla de Gitk:"
 
-#: gitk:1349
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tSalir"
 
-#: gitk:1350
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tIr a la primera revisión"
 
-#: gitk:1351
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tIr a la última revisión"
 
-#: gitk:1352
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Up>, p, i\tSubir una revisión"
 
-#: gitk:1353
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Down>, n, k\tBajar una revisión"
 
-#: gitk:1354
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Left>, z, j\tRetroceder en la historia"
 
-#: gitk:1355
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Right>, x, l\tAvanzar en la historia"
 
-#: gitk:1356
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tSubir una página en la lista de revisiones"
 
-#: gitk:1357
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tBajar una página en la lista de revisiones"
 
-#: gitk:1358
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tDesplazarse al inicio de la lista de revisiones"
 
-#: gitk:1359
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tDesplazarse al final de la lista de revisiones"
 
-#: gitk:1360
+#: gitk:2530
 #, 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
+#: gitk:2531
 #, 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
+#: gitk:2532
 #, 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
+#: gitk:2533
 #, 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
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Shift-Up>\tBuscar hacia atrás (arriba, revisiones siguientes)"
 
-#: gitk:1365
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Shift-Down>\tBuscar hacia adelante (abajo, revisiones anteriores)"
 
-#: gitk:1366
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tDesplaza hacia arriba una página la vista de diferencias"
 
-#: gitk:1367
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Backspace>\tDesplaza hacia arriba una página la vista de diferencias"
 
-#: gitk:1368
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Space>\t\tDesplaza hacia abajo una página la vista de diferencias"
 
-#: gitk:1369
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tDesplaza hacia arriba 18 líneas la vista de diferencias"
 
-#: gitk:1370
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tDesplaza hacia abajo 18 líneas la vista de diferencias"
 
-#: gitk:1371
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tBuscar"
 
-#: gitk:1372
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tBuscar el siguiente"
 
-#: gitk:1373
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\tBuscar el siguiente"
 
-#: gitk:1374
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tBuscar el siguiente, o reiniciar la búsqueda"
 
-#: gitk:1375
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tBuscar el anterior"
 
-#: gitk:1376
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tDesplazar la vista de diferencias al archivo siguiente"
 
-#: gitk:1377
+#: gitk:2547
 #, 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
+#: gitk:2548
 #, 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
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-KP+>\tAumentar tamaño del texto"
 
-#: gitk:1380
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tAumentar tamaño del texto"
 
-#: gitk:1381
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-KP->\tDisminuir tamaño del texto"
 
-#: gitk:1382
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tDisminuir tamaño del texto"
 
-#: gitk:1383
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tActualizar"
 
-#: gitk:1896
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Definición de vistas de Gitk"
 
-#: gitk:1921
+#: gitk:3225
 msgid "Name"
 msgstr "Nombre"
 
-#: gitk:1924
+#: gitk:3228
 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:3232
+msgid "Commits to include (arguments to git log):"
+msgstr "Revisiones a incluir (argumentos a git log):"
 
-#: gitk:1935
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Comando que genera más revisiones a incluir:"
 
-#: gitk:1942
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Introducir archivos y directorios a incluir, uno por línea:"
 
-#: gitk:1989
+#: gitk:3293
 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
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Ninguno"
 
-#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Fecha"
 
-#: gitk:2531 gitk:4336
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr "Fecha de creación"
 
-#: gitk:2680 gitk:2685
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Descendiente"
 
-#: gitk:2681
+#: gitk:3940
 msgid "Not descendant"
 msgstr "No descendiente"
 
-#: gitk:2688 gitk:2693
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Antepasado"
 
-#: gitk:2689
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "No antepasado"
 
-#: gitk:2924
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Cambios locales añadidos al índice pero sin completar revisión"
 
-#: gitk:2954
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Cambios locales sin añadir al índice"
 
-#: gitk:4305
+#: gitk:5549
 msgid "Searching"
 msgstr "Buscando"
 
-#: gitk:4767
+#: gitk:6049
 msgid "Tags:"
 msgstr "Etiquetas:"
 
-#: gitk:4784 gitk:4790 gitk:5951
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Padre"
 
-#: gitk:4795
+#: gitk:6077
 msgid "Child"
 msgstr "Hija"
 
-#: gitk:4804
+#: gitk:6086
 msgid "Branch"
 msgstr "Rama"
 
-#: gitk:4807
+#: gitk:6089
 msgid "Follows"
 msgstr "Sigue-a"
 
-#: gitk:4810
+#: gitk:6092
 msgid "Precedes"
 msgstr "Precede-a"
 
-#: gitk:5093
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Error al leer las diferencias de fusión:"
 
-#: gitk:5778
+#: gitk:7113
 msgid "Goto:"
 msgstr "Ir a:"
 
-#: gitk:5780
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1 ID:"
 
-#: gitk:5805
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "La id SHA1 abreviada %s es ambigua"
 
-#: gitk:5817
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "La id SHA1 %s es desconocida"
 
-#: gitk:5819
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "La etiqueta/rama %s es deconocida"
 
-#: gitk:5961
+#: gitk:7290
 msgid "Children"
 msgstr "Hijas"
 
-#: gitk:6018
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Poner la rama %s en esta revisión"
 
-#: gitk:6049
+#: gitk:7349
+msgid "Detached head: can't reset"
+msgstr ""
+
+#: gitk:7381
 msgid "Top"
 msgstr "Origen"
 
-#: gitk:6050
+#: gitk:7382
 msgid "From"
 msgstr "De"
 
-#: gitk:6055
+#: gitk:7387
 msgid "To"
 msgstr "A"
 
-#: gitk:6078
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Generar parche"
 
-#: gitk:6080
+#: gitk:7412
 msgid "From:"
 msgstr "De:"
 
-#: gitk:6089
+#: gitk:7421
 msgid "To:"
 msgstr "Para:"
 
-#: gitk:6098
+#: gitk:7430
 msgid "Reverse"
 msgstr "Invertir"
 
-#: gitk:6100 gitk:6269
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Escribir a archivo:"
 
-#: gitk:6106
+#: gitk:7438
 msgid "Generate"
 msgstr "Generar"
 
-#: gitk:6142
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Error en la creación del parche:"
 
-#: gitk:6164 gitk:6257 gitk:6311
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6173
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Nombre de etiqueta:"
 
-#: gitk:6177 gitk:6320
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Crear"
 
-#: gitk:6192
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "No se ha especificado etiqueta"
 
-#: gitk:6196
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "La etiqueta \"%s\" ya existe"
 
-#: gitk:6202
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Error al crear la etiqueta:"
 
-#: gitk:6266
+#: gitk:7604
 msgid "Command:"
 msgstr "Comando:"
 
-#: gitk:6274
+#: gitk:7612
 msgid "Write"
 msgstr "Escribir"
 
-#: gitk:6290
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Error al escribir revisión:"
 
-#: gitk:6316
+#: gitk:7654
 msgid "Name:"
 msgstr "Nombre:"
 
-#: gitk:6335
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Especifique un nombre para la nueva rama"
 
-#: gitk:6364
+#: gitk:7703
 #, 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
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr "Eligiendo revisiones (cherry-picking)"
 
-#: gitk:6381
+#: gitk:7720
 msgid "No changes committed"
 msgstr "No se han guardado cambios"
 
-#: gitk:6404
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Confirmar git reset"
 
-#: gitk:6406
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "¿Reponer la rama %s a %s?"
 
-#: gitk:6410
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Tipo de reposición:"
 
-#: gitk:6414
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Suave: No altera la copia de trabajo ni el índice"
 
-#: gitk:6417
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Mixta: Actualiza el índice, no altera la copia de trabajo"
 
-#: gitk:6420
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -693,19 +719,19 @@ msgstr ""
 "Dura: Actualiza el índice y la copia de trabajo\n"
 "(abandona TODAS las modificaciones locales)"
 
-#: gitk:6436
+#: gitk:7777
 msgid "Resetting"
 msgstr "Reponiendo"
 
-#: gitk:6493
+#: gitk:7834
 msgid "Checking out"
 msgstr "Creando copia de trabajo"
 
-#: gitk:6523
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr "No se puede borrar la rama actual"
 
-#: gitk:6529
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -714,134 +740,146 @@ msgstr ""
 "Las revisiones de la rama %s no están presentes en otras ramas.\n"
 "¿Borrar la rama %s?"
 
-#: gitk:6560
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Etiquetas y ramas: %s"
 
-#: gitk:6574
+#: gitk:7936
 msgid "Filter"
 msgstr "Filtro"
 
-#: gitk:6868
+#: gitk:8230
 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."
+"Error al leer la topología de revisiones: la información sobre las ramas y "
+"etiquetas precedentes y siguientes será incompleta."
 
-#: gitk:7852
+#: gitk:9216
 msgid "Tag"
 msgstr "Etiqueta"
 
-#: gitk:7852
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7892
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Selector de tipografías gitk"
 
-#: gitk:7909
+#: gitk:9279
 msgid "B"
 msgstr "B"
 
-#: gitk:7912
+#: gitk:9282
 msgid "I"
 msgstr "I"
 
-#: gitk:8005
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Preferencias de gitk"
 
-#: gitk:8006
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Opciones de visualización de la lista de revisiones"
 
-#: gitk:8009
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Ancho máximo del gráfico (en líneas)"
 
-#: gitk:8013
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Ancho máximo del gráfico (en % del panel)"
 
-#: gitk:8018
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Mostrar cambios locales"
 
-#: gitk:8023
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "Seleccionar automáticamente SHA1 hash"
 
-#: gitk:8028
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Opciones de visualización de diferencias"
 
-#: gitk:8030
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Espaciado de tabulador"
 
-#: gitk:8034
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Mostrar etiquetas cercanas"
 
-#: gitk:8039
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Limitar las diferencias a las rutas seleccionadas"
 
-#: gitk:8044
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
+msgid "External diff tool"
+msgstr ""
+
+#: gitk:9423
+msgid "Choose..."
+msgstr ""
+
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Colores: pulse para seleccionar"
 
-#: gitk:8047
+#: gitk:9431
 msgid "Background"
 msgstr "Fondo"
 
-#: gitk:8051
+#: gitk:9435
 msgid "Foreground"
 msgstr "Primer plano"
 
-#: gitk:8055
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Diff: líneas viejas"
 
-#: gitk:8060
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Diff: líneas nuevas"
 
-#: gitk:8065
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Diff: cabecera de fragmento"
 
-#: gitk:8071
+#: gitk:9455
 msgid "Select bg"
 msgstr "Color de fondo de la selección"
 
-#: gitk:8075
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Tipografías: pulse para elegir"
 
-#: gitk:8077
+#: gitk:9461
 msgid "Main font"
 msgstr "Tipografía principal"
 
-#: gitk:8078
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Tipografía para diferencias"
 
-#: gitk:8079
+#: gitk:9463
 msgid "User interface font"
 msgstr "Tipografía para interfaz de usuario"
 
-#: gitk:8095
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: elegir color para %s"
 
-#: gitk:8476
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -849,42 +887,25 @@ msgstr ""
 "Esta versión de Tcl/Tk es demasiado antigua.\n"
 " Gitk requiere Tcl/Tk versión 8.4 o superior."
 
-#: gitk:8565
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "No hay un repositorio git aquí."
 
-#: gitk:8569
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "No hay directorio git \"%s\"."
 
-#: gitk:8612
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
-msgstr "Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo"
+msgstr ""
+"Argumento ambiguo: '%s' es tanto una revisión como un nombre de archivo"
 
-#: gitk:8624
+#: gitk:10110
 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
+#: gitk:10170
 msgid "Command line"
 msgstr "Línea de comandos"
index d0f4c2e19ac15b10c01d6df968f823914cdbba4a..e89c95702cf71d259543f6bb6713e69f624b9b53 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gitk\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-03-13 17:29+0100\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-03-13 17:34+0100\n"
 "Last-Translator: Michele Ballabio <barra_cuda@katamail.com>\n"
 "Language-Team: Italian\n"
@@ -16,676 +16,706 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:111
-msgid "Error executing git rev-list:"
-msgstr "Errore nell'esecuzione di git rev-list:"
+#: gitk:113
+msgid "Couldn't get list of unmerged files:"
+msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
+
+#: gitk:340
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
+"sono file in attesa di fusione."
+
+#: gitk:343
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr ""
+"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
+"specificati non sono in attesa di fusione."
 
-#: gitk:124
+#: gitk:365 gitk:503
+msgid "Error executing git log:"
+msgstr "Errore nell'esecuzione di git log:"
+
+#: gitk:378
 msgid "Reading"
 msgstr "Lettura in corso"
 
-#: gitk:151 gitk:2191
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Lettura delle revisioni in corso..."
 
-#: gitk:275
-msgid "Can't parse git log output:"
-msgstr "Impossibile elaborare i dati di git log:"
-
-#: gitk:386 gitk:2195
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "Nessuna revisione selezionata"
 
-#: gitk:500
+#: gitk:1399
+msgid "Can't parse git log output:"
+msgstr "Impossibile elaborare i dati di git log:"
+
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Nessuna informazione disponibile sulle revisioni"
 
-#: gitk:599 gitk:621 gitk:1955 gitk:6423 gitk:7923 gitk:8082
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "OK"
 
-#: gitk:623 gitk:1956 gitk:6107 gitk:6178 gitk:6275 gitk:6321 gitk:6425
-#: gitk:7924 gitk:8083
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Annulla"
 
-#: gitk:661
-msgid "File"
-msgstr "File"
-
-#: gitk:663
+#: gitk:1811
 msgid "Update"
 msgstr "Aggiorna"
 
-#: gitk:664
+#: gitk:1813
 msgid "Reread references"
 msgstr "Rileggi riferimenti"
 
-#: gitk:665
+#: gitk:1814
 msgid "List references"
 msgstr "Elenca riferimenti"
 
-#: gitk:666
+#: gitk:1815
 msgid "Quit"
 msgstr "Esci"
 
-#: gitk:668
-msgid "Edit"
-msgstr "Modifica"
+#: gitk:1810
+msgid "File"
+msgstr "File"
 
-#: gitk:669
+#: gitk:1818
 msgid "Preferences"
 msgstr "Preferenze"
 
-#: gitk:672 gitk:1892
-msgid "View"
-msgstr "Vista"
+#: gitk:1817
+msgid "Edit"
+msgstr "Modifica"
 
-#: gitk:673
+#: gitk:1821
 msgid "New view..."
 msgstr "Nuova vista..."
 
-#: gitk:674 gitk:2133 gitk:8722
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Modifica vista..."
 
-#: gitk:676 gitk:2134 gitk:8723
+#: gitk:1823
 msgid "Delete view"
 msgstr "Elimina vista"
 
-#: gitk:678
+#: gitk:1825
 msgid "All files"
 msgstr "Tutti i file"
 
-#: gitk:682
-msgid "Help"
-msgstr "Aiuto"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Vista"
 
-#: gitk:683 gitk:1317
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Informazioni su gitk"
 
-#: gitk:684
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Scorciatoie da tastiera"
 
-#: gitk:741
+#: gitk:1827
+msgid "Help"
+msgstr "Aiuto"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1 ID: "
 
-#: gitk:791
+#: gitk:1918
+msgid "Row"
+msgstr ""
+
+#: gitk:1949
 msgid "Find"
 msgstr "Trova"
 
-#: gitk:792
+#: gitk:1950
 msgid "next"
 msgstr "succ"
 
-#: gitk:793
+#: gitk:1951
 msgid "prev"
 msgstr "prec"
 
-#: gitk:794
+#: gitk:1952
 msgid "commit"
 msgstr "revisione"
 
-#: gitk:797 gitk:799 gitk:2356 gitk:2379 gitk:2403 gitk:4306 gitk:4369
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "contenente:"
 
-#: gitk:800 gitk:1778 gitk:1783 gitk:2431
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "che riguarda i percorsi:"
 
-#: gitk:801 gitk:2436
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "che aggiunge/rimuove la stringa:"
 
-#: gitk:810 gitk:812
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Esatto"
 
-#: gitk:812 gitk:2514 gitk:4274
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr ""
 
-#: gitk:812 gitk:2405 gitk:2512 gitk:4270
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr ""
 
-#: gitk:814 gitk:815 gitk:2533 gitk:2563 gitk:2570 gitk:4380 gitk:4436
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Tutti i campi"
 
-#: gitk:815 gitk:2531 gitk:2563 gitk:4336
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Titolo"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4436 gitk:4827
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Commenti"
 
-#: gitk:816 gitk:2531 gitk:2535 gitk:2570 gitk:4336 gitk:4763 gitk:5956
-#: gitk:5971
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Autore"
 
-#: gitk:816 gitk:2531 gitk:4336 gitk:4765
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr "Revisione creata da"
 
-#: gitk:845
+#: gitk:2003
 msgid "Search"
 msgstr "Cerca"
 
-#: gitk:852
+#: gitk:2010
 msgid "Diff"
 msgstr ""
 
-#: gitk:854
+#: gitk:2012
 msgid "Old version"
 msgstr "Vecchia versione"
 
-#: gitk:856
+#: gitk:2014
 msgid "New version"
 msgstr "Nuova versione"
 
-#: gitk:858
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Linee di contesto"
 
-#: gitk:868
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Ignora modifiche agli spazi"
 
-#: gitk:926
+#: gitk:2084
 msgid "Patch"
 msgstr "Modifiche"
 
-#: gitk:928
+#: gitk:2086
 msgid "Tree"
 msgstr "Directory"
 
-#: gitk:1053 gitk:1068 gitk:6022
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Diff questo -> selezionato"
 
-#: gitk:1055 gitk:1070 gitk:6023
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Diff selezionato -> questo"
 
-#: gitk:1057 gitk:1072 gitk:6024
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Crea patch"
 
-#: gitk:1058 gitk:6162
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Crea etichetta"
 
-#: gitk:1059 gitk:6255
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Scrivi revisione in un file"
 
-#: gitk:1060 gitk:6309
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Crea un nuovo ramo"
 
-#: gitk:1061
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Porta questa revisione in cima al ramo attuale"
 
-#: gitk:1063
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "Aggiorna il ramo HEAD a questa revisione"
 
-#: gitk:1079
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Attiva questo ramo"
 
-#: gitk:1081
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Elimina questo ramo"
 
-#: gitk:1087
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Evidenzia anche questo"
 
-#: gitk:1089
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Evidenzia solo questo"
 
-#: gitk:1318
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
 "Use and redistribute under the terms of the GNU General Public License"
 msgstr ""
 "\n"
 "Gitk - un visualizzatore di revisioni per git\n"
 "\n"
-"Copyright © 2005-2006 Paul Mackerras\n"
+"Copyright © 2005-2008 Paul Mackerras\n"
 "\n"
 "Utilizzo e redistribuzione permessi sotto i termini della GNU General Public "
 "License"
 
-#: gitk:1326 gitk:1387 gitk:6581
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Chiudi"
 
-#: gitk:1345
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Scorciatoie da tastiera di Gitk"
 
-#: gitk:1347
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Scorciatoie da tastiera di Gitk:"
 
-#: gitk:1349
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tEsci"
 
-#: gitk:1350
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tVai alla prima revisione"
 
-#: gitk:1351
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tVai all'ultima revisione"
 
-#: gitk:1352
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Up>, p, i\tVai più in alto di una revisione"
 
-#: gitk:1353
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Down>, n, k\tVai più in basso di una revisione"
 
-#: gitk:1354
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Left>, z, j\tTorna indietro nella cronologia"
 
-#: gitk:1355
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Right>, x, l\tVai avanti nella cronologia"
 
-#: gitk:1356
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tVai più in alto di una pagina nella lista delle revisioni"
 
-#: gitk:1357
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tVai più in basso di una pagina nella lista delle revisioni"
 
-#: gitk:1358
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tScorri alla cima della lista delle revisioni"
 
-#: gitk:1359
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tScorri alla fine della lista delle revisioni"
 
-#: gitk:1360
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Up>\tScorri la lista delle revisioni in alto di una riga"
 
-#: gitk:1361
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Down>\tScorri la lista delle revisioni in basso di una riga"
 
-#: gitk:1362
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tScorri la lista delle revisioni in alto di una pagina"
 
-#: gitk:1363
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tScorri la lista delle revisioni in basso di una pagina"
 
-#: gitk:1364
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Shift-Up>\tTrova all'indietro (verso l'alto, revisioni successive)"
 
-#: gitk:1365
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Shift-Down>\tTrova in avanti (verso il basso, revisioni precedenti)"
 
-#: gitk:1366
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tScorri la vista delle differenze in alto di una pagina"
 
-#: gitk:1367
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Backspace>\tScorri la vista delle differenze in alto di una pagina"
 
-#: gitk:1368
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Space>\t\tScorri la vista delle differenze in basso di una pagina"
 
-#: gitk:1369
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tScorri la vista delle differenze in alto di 18 linee"
 
-#: gitk:1370
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tScorri la vista delle differenze in basso di 18 linee"
 
-#: gitk:1371
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tTrova"
 
-#: gitk:1372
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tTrova in avanti"
 
-#: gitk:1373
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\tTrova in avanti"
 
-#: gitk:1374
+#: gitk:2544
 msgid "/\t\tMove to next find hit, or redo find"
 msgstr "/\t\tTrova in avanti, o cerca di nuovo"
 
-#: gitk:1375
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tTrova all'indietro"
 
-#: gitk:1376
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tScorri la vista delle differenze al file successivo"
 
-#: gitk:1377
+#: gitk:2547
 #, tcl-format
 msgid "<%s-S>\t\tSearch for next hit in diff view"
 msgstr "<%s-S>\t\tCerca in avanti nella vista delle differenze"
 
-#: gitk:1378
+#: gitk:2548
 #, tcl-format
 msgid "<%s-R>\t\tSearch for previous hit in diff view"
 msgstr "<%s-R>\t\tCerca all'indietro nella vista delle differenze"
 
-#: gitk:1379
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-KP+>\tAumenta grandezza carattere"
 
-#: gitk:1380
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tAumenta grandezza carattere"
 
-#: gitk:1381
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-KP->\tDiminuisci grandezza carattere"
 
-#: gitk:1382
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tDiminuisci grandezza carattere"
 
-#: gitk:1383
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tAggiorna"
 
-#: gitk:1896
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Scelta vista Gitk"
 
-#: gitk:1921
+#: gitk:3225
 msgid "Name"
 msgstr "Nome"
 
-#: gitk:1924
+#: gitk:3228
 msgid "Remember this view"
 msgstr "Ricorda questa vista"
 
-#: gitk:1928
-msgid "Commits to include (arguments to git rev-list):"
-msgstr "Revisioni da includere (argomenti di git rev-list):"
+#: gitk:3232
+msgid "Commits to include (arguments to git log):"
+msgstr "Revisioni da includere (argomenti di git log):"
 
-#: gitk:1935
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Comando che genera altre revisioni da visualizzare:"
 
-#: gitk:1942
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Inserire file e directory da includere, uno per riga:"
 
-#: gitk:1989
+#: gitk:3293
 msgid "Error in commit selection arguments:"
 msgstr "Errore negli argomenti di selezione delle revisioni:"
 
-#: gitk:2043 gitk:2127 gitk:2583 gitk:2597 gitk:3781 gitk:8688 gitk:8689
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Nessuno"
 
-#: gitk:2531 gitk:4336 gitk:5958 gitk:5973
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Data"
 
-#: gitk:2531 gitk:4336
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr ""
 
-#: gitk:2680 gitk:2685
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Discendente"
 
-#: gitk:2681
+#: gitk:3940
 msgid "Not descendant"
 msgstr "Non discendente"
 
-#: gitk:2688 gitk:2693
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Ascendente"
 
-#: gitk:2689
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "Non ascendente"
 
-#: gitk:2924
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Modifiche locali presenti nell'indice ma non nell'archivio"
 
-#: gitk:2954
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Modifiche locali non presenti né nell'archivio né nell'indice"
 
-#: gitk:4305
+#: gitk:5549
 msgid "Searching"
 msgstr "Ricerca in corso"
 
-#: gitk:4767
+#: gitk:6049
 msgid "Tags:"
 msgstr "Etichette:"
 
-#: gitk:4784 gitk:4790 gitk:5951
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Genitore"
 
-#: gitk:4795
+#: gitk:6077
 msgid "Child"
 msgstr "Figlio"
 
-#: gitk:4804
+#: gitk:6086
 msgid "Branch"
 msgstr "Ramo"
 
-#: gitk:4807
+#: gitk:6089
 msgid "Follows"
 msgstr "Segue"
 
-#: gitk:4810
+#: gitk:6092
 msgid "Precedes"
 msgstr "Precede"
 
-#: gitk:5093
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Errore nella lettura delle differenze di fusione:"
 
-#: gitk:5778
+#: gitk:7113
 msgid "Goto:"
 msgstr "Vai a:"
 
-#: gitk:5780
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1 ID:"
 
-#: gitk:5805
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "La SHA1 id abbreviata %s è ambigua"
 
-#: gitk:5817
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "La SHA1 id %s è sconosciuta"
 
-#: gitk:5819
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "L'etichetta/ramo %s è sconosciuto"
 
-#: gitk:5961
+#: gitk:7290
 msgid "Children"
 msgstr "Figli"
 
-#: gitk:6018
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Aggiorna il ramo %s a questa revisione"
 
-#: gitk:6049
+#: gitk:7349
+msgid "Detached head: can't reset"
+msgstr ""
+
+#: gitk:7381
 msgid "Top"
 msgstr "Inizio"
 
-#: gitk:6050
+#: gitk:7382
 msgid "From"
 msgstr "Da"
 
-#: gitk:6055
+#: gitk:7387
 msgid "To"
 msgstr "A"
 
-#: gitk:6078
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Genera patch"
 
-#: gitk:6080
+#: gitk:7412
 msgid "From:"
 msgstr "Da:"
 
-#: gitk:6089
+#: gitk:7421
 msgid "To:"
 msgstr "A:"
 
-#: gitk:6098
+#: gitk:7430
 msgid "Reverse"
 msgstr "Inverti"
 
-#: gitk:6100 gitk:6269
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Scrivi sul file:"
 
-#: gitk:6106
+#: gitk:7438
 msgid "Generate"
 msgstr "Genera"
 
-#: gitk:6142
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Errore nella creazione della patch:"
 
-#: gitk:6164 gitk:6257 gitk:6311
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "ID:"
 
-#: gitk:6173
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Nome etichetta:"
 
-#: gitk:6177 gitk:6320
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Crea"
 
-#: gitk:6192
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "Nessuna etichetta specificata"
 
-#: gitk:6196
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "L'etichetta \"%s\" esiste già"
 
-#: gitk:6202
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Errore nella creazione dell'etichetta:"
 
-#: gitk:6266
+#: gitk:7604
 msgid "Command:"
 msgstr "Comando:"
 
-#: gitk:6274
+#: gitk:7612
 msgid "Write"
 msgstr "Scrivi"
 
-#: gitk:6290
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Errore nella scrittura della revisione:"
 
-#: gitk:6316
+#: gitk:7654
 msgid "Name:"
 msgstr "Nome:"
 
-#: gitk:6335
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Specificare un nome per il nuovo ramo"
 
-#: gitk:6364
+#: gitk:7703
 #, tcl-format
 msgid "Commit %s is already included in branch %s -- really re-apply it?"
 msgstr "La revisione %s è già inclusa nel ramo %s -- applicarla di nuovo?"
 
-#: gitk:6369
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr ""
 
-#: gitk:6381
+#: gitk:7720
 msgid "No changes committed"
 msgstr "Nessuna modifica archiviata"
 
-#: gitk:6404
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Conferma git reset"
 
-#: gitk:6406
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Aggiornare il ramo %s a %s?"
 
-#: gitk:6410
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Tipo di aggiornamento:"
 
-#: gitk:6414
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Soft: Lascia la direcory di lavoro e l'indice come sono"
 
-#: gitk:6417
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Mixed: Lascia la directory di lavoro come è, aggiorna l'indice"
 
-#: gitk:6420
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -693,19 +723,19 @@ msgstr ""
 "Hard: Aggiorna la directory di lavoro e l'indice\n"
 "(abbandona TUTTE le modifiche locali)"
 
-#: gitk:6436
+#: gitk:7777
 msgid "Resetting"
 msgstr "git reset in corso"
 
-#: gitk:6493
+#: gitk:7834
 msgid "Checking out"
 msgstr "Attivazione in corso"
 
-#: gitk:6523
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Impossibile cancellare il ramo attualmente attivo"
 
-#: gitk:6529
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -714,16 +744,16 @@ msgstr ""
 "Le revisioni nel ramo %s non sono presenti su altri rami.\n"
 "Cancellare il ramo %s?"
 
-#: gitk:6560
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Etichette e rami: %s"
 
-#: gitk:6574
+#: gitk:7936
 msgid "Filter"
 msgstr "Filtro"
 
-#: gitk:6868
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -731,117 +761,129 @@ msgstr ""
 "Errore nella lettura della topologia delle revisioni: le informazioni sul "
 "ramo e le etichette precedenti e seguenti saranno incomplete."
 
-#: gitk:7852
+#: gitk:9216
 msgid "Tag"
 msgstr "Etichetta"
 
-#: gitk:7852
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:7892
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Scelta caratteri gitk"
 
-#: gitk:7909
+#: gitk:9279
 msgid "B"
 msgstr "B"
 
-#: gitk:7912
+#: gitk:9282
 msgid "I"
 msgstr "I"
 
-#: gitk:8005
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Preferenze gitk"
 
-#: gitk:8006
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Opzioni visualizzazione dell'elenco revisioni"
 
-#: gitk:8009
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Larghezza massima del grafico (in linee)"
 
-#: gitk:8013
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Larghezza massima del grafico (% del pannello)"
 
-#: gitk:8018
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Mostra modifiche locali"
 
-#: gitk:8023
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "Seleziona automaticamente SHA1 hash"
 
-#: gitk:8028
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Opzioni di visualizzazione delle differenze"
 
-#: gitk:8030
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Spaziatura tabulazioni"
 
-#: gitk:8034
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Mostra etichette vicine"
 
-#: gitk:8039
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Limita le differenze ai percorsi elencati"
 
-#: gitk:8044
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
+msgid "External diff tool"
+msgstr ""
+
+#: gitk:9423
+msgid "Choose..."
+msgstr ""
+
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Colori: premere per scegliere"
 
-#: gitk:8047
+#: gitk:9431
 msgid "Background"
 msgstr "Sfondo"
 
-#: gitk:8051
+#: gitk:9435
 msgid "Foreground"
 msgstr "Primo piano"
 
-#: gitk:8055
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Diff: vecchie linee"
 
-#: gitk:8060
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Diff: nuove linee"
 
-#: gitk:8065
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Diff: intestazione della sezione"
 
-#: gitk:8071
+#: gitk:9455
 msgid "Select bg"
 msgstr "Sfondo selezione"
 
-#: gitk:8075
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Carattere: premere per scegliere"
 
-#: gitk:8077
+#: gitk:9461
 msgid "Main font"
 msgstr "Carattere principale"
 
-#: gitk:8078
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Carattere per differenze"
 
-#: gitk:8079
+#: gitk:9463
 msgid "User interface font"
 msgstr "Carattere per interfaccia utente"
 
-#: gitk:8095
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: scegliere un colore per %s"
 
-#: gitk:8476
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -849,42 +891,24 @@ msgstr ""
 "Questa versione di Tcl/Tk non può avviare gitk.\n"
 " Gitk richiede Tcl/Tk versione 8.4 o superiore."
 
-#: gitk:8565
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "Archivio git non trovato."
 
-#: gitk:8569
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Directory git \"%s\" non trovata."
 
-#: gitk:8612
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Argomento ambiguo: '%s' è sia revisione che nome di file"
 
-#: gitk:8624
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Gitk: argomenti errati:"
 
-#: gitk:8636
-msgid "Couldn't get list of unmerged files:"
-msgstr "Impossibile ottenere l'elenco dei file in attesa di fusione:"
-
-#: gitk:8652
-msgid "No files selected: --merge specified but no files are unmerged."
-msgstr ""
-"Nessun file selezionato: è stata specificata l'opzione --merge ma non ci "
-"sono file in attesa di fusione."
-
-#: gitk:8655
-msgid ""
-"No files selected: --merge specified but no unmerged files are within file "
-"limit."
-msgstr ""
-"Nessun file selezionato: è stata specificata l'opzione --merge ma i file "
-"specificati non sono in attesa di fusione."
-
-#: gitk:8716
+#: gitk:10170
 msgid "Command line"
 msgstr "Linea di comando"
index e1ecfb75861b9135aecdfc4773bca5a8c41d9626..947b53f6b0d76bc169e4aee2e8a030963fdc131f 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: sv\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2008-08-03 18:58+0200\n"
+"POT-Creation-Date: 2008-10-18 22:03+1100\n"
 "PO-Revision-Date: 2008-08-03 19:03+0200\n"
 "Last-Translator: Mikael Magnusson <mikachu@gmail.com>\n"
 "Language-Team: Swedish <sv@li.org>\n"
@@ -16,17 +16,17 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: gitk:102
+#: gitk:113
 msgid "Couldn't get list of unmerged files:"
 msgstr "Kunde inta hämta lista över ej sammanslagna filer:"
 
-#: gitk:329
+#: gitk:340
 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:332
+#: gitk:343
 msgid ""
 "No files selected: --merge specified but no unmerged files are within file "
 "limit."
@@ -34,257 +34,261 @@ msgstr ""
 "Inga filer valdes: --merge angavs men det finns inga filer inom "
 "filbegränsningen."
 
-#: gitk:354
+#: gitk:365 gitk:503
 msgid "Error executing git log:"
 msgstr "Fel vid körning av git log:"
 
-#: gitk:369
+#: gitk:378
 msgid "Reading"
 msgstr "Läser"
 
-#: gitk:400 gitk:3356
+#: gitk:438 gitk:3462
 msgid "Reading commits..."
 msgstr "Läser incheckningar..."
 
-#: gitk:403 gitk:1480 gitk:3359
+#: gitk:441 gitk:1528 gitk:3465
 msgid "No commits selected"
 msgstr "Inga incheckningar markerade"
 
-#: gitk:1358
+#: gitk:1399
 msgid "Can't parse git log output:"
 msgstr "Kan inte tolka utdata från git log:"
 
-#: gitk:1557
+#: gitk:1605
 msgid "No commit information available"
 msgstr "Ingen incheckningsinformation är tillgänglig"
 
-#: gitk:1654 gitk:1676 gitk:3150 gitk:7620 gitk:9149 gitk:9317
+#: gitk:1709 gitk:1731 gitk:3259 gitk:7764 gitk:9293 gitk:9466
 msgid "OK"
 msgstr "OK"
 
-#: gitk:1678 gitk:3151 gitk:7296 gitk:7367 gitk:7470 gitk:7516 gitk:7622
-#: gitk:9150 gitk:9318
+#: gitk:1733 gitk:3260 gitk:7439 gitk:7510 gitk:7613 gitk:7660 gitk:7766
+#: gitk:9294 gitk:9467
 msgid "Cancel"
 msgstr "Avbryt"
 
-#: gitk:1716
-msgid "File"
-msgstr "Arkiv"
-
-#: gitk:1718
+#: gitk:1811
 msgid "Update"
 msgstr "Uppdatera"
 
-#: gitk:1719
+#: gitk:1812
 msgid "Reload"
 msgstr "Ladda om"
 
-#: gitk:1720
+#: gitk:1813
 msgid "Reread references"
 msgstr "Läs om referenser"
 
-#: gitk:1721
+#: gitk:1814
 msgid "List references"
 msgstr "Visa referenser"
 
-#: gitk:1722
+#: gitk:1815
 msgid "Quit"
 msgstr "Avsluta"
 
-#: gitk:1724
-msgid "Edit"
-msgstr "Redigera"
+#: gitk:1810
+msgid "File"
+msgstr "Arkiv"
 
-#: gitk:1725
+#: gitk:1818
 msgid "Preferences"
 msgstr "Inställningar"
 
-#: gitk:1728 gitk:3087
-msgid "View"
-msgstr "Visa"
+#: gitk:1817
+msgid "Edit"
+msgstr "Redigera"
 
-#: gitk:1729
+#: gitk:1821
 msgid "New view..."
 msgstr "Ny vy..."
 
-#: gitk:1730 gitk:3298 gitk:9932
+#: gitk:1822
 msgid "Edit view..."
 msgstr "Ändra vy..."
 
-#: gitk:1732 gitk:3299 gitk:9933
+#: gitk:1823
 msgid "Delete view"
 msgstr "Ta bort vy"
 
-#: gitk:1734
+#: gitk:1825
 msgid "All files"
 msgstr "Alla filer"
 
-#: gitk:1738
-msgid "Help"
-msgstr "Hjälp"
+#: gitk:1820 gitk:3196
+msgid "View"
+msgstr "Visa"
 
-#: gitk:1739 gitk:2399
+#: gitk:1828 gitk:2487
 msgid "About gitk"
 msgstr "Om gitk"
 
-#: gitk:1740
+#: gitk:1829
 msgid "Key bindings"
 msgstr "Tangentbordsbindningar"
 
-#: gitk:1797
+#: gitk:1827
+msgid "Help"
+msgstr "Hjälp"
+
+#: gitk:1887
 msgid "SHA1 ID: "
 msgstr "SHA1-id: "
 
-#: gitk:1828
+#: gitk:1918
 msgid "Row"
 msgstr "Rad"
 
-#: gitk:1859
+#: gitk:1949
 msgid "Find"
 msgstr "Sök"
 
-#: gitk:1860
+#: gitk:1950
 msgid "next"
 msgstr "nästa"
 
-#: gitk:1861
+#: gitk:1951
 msgid "prev"
 msgstr "föreg"
 
-#: gitk:1862
+#: gitk:1952
 msgid "commit"
 msgstr "incheckning"
 
-#: gitk:1865 gitk:1867 gitk:3511 gitk:3534 gitk:3558 gitk:5441 gitk:5512
+#: gitk:1955 gitk:1957 gitk:3617 gitk:3640 gitk:3664 gitk:5550 gitk:5621
 msgid "containing:"
 msgstr "som innehåller:"
 
-#: gitk:1868 gitk:2866 gitk:2871 gitk:3586
+#: gitk:1958 gitk:2954 gitk:2959 gitk:3692
 msgid "touching paths:"
 msgstr "som rör sökväg:"
 
-#: gitk:1869 gitk:3591
+#: gitk:1959 gitk:3697
 msgid "adding/removing string:"
 msgstr "som lägger/till tar bort sträng:"
 
-#: gitk:1878 gitk:1880
+#: gitk:1968 gitk:1970
 msgid "Exact"
 msgstr "Exakt"
 
-#: gitk:1880 gitk:3667 gitk:5409
+#: gitk:1970 gitk:3773 gitk:5518
 msgid "IgnCase"
 msgstr "IgnVersaler"
 
-#: gitk:1880 gitk:3560 gitk:3665 gitk:5405
+#: gitk:1970 gitk:3666 gitk:3771 gitk:5514
 msgid "Regexp"
 msgstr "Reg.uttr."
 
-#: gitk:1882 gitk:1883 gitk:3686 gitk:3716 gitk:3723 gitk:5532 gitk:5599
+#: gitk:1972 gitk:1973 gitk:3792 gitk:3822 gitk:3829 gitk:5641 gitk:5708
 msgid "All fields"
 msgstr "Alla fält"
 
-#: gitk:1883 gitk:3684 gitk:3716 gitk:5471
+#: gitk:1973 gitk:3790 gitk:3822 gitk:5580
 msgid "Headline"
 msgstr "Rubrik"
 
-#: gitk:1884 gitk:3684 gitk:5471 gitk:5599 gitk:6000
+#: gitk:1974 gitk:3790 gitk:5580 gitk:5708 gitk:6109
 msgid "Comments"
 msgstr "Kommentarer"
 
-#: gitk:1884 gitk:3684 gitk:3688 gitk:3723 gitk:5471 gitk:5936 gitk:7142
-#: gitk:7157
+#: gitk:1974 gitk:3790 gitk:3794 gitk:3829 gitk:5580 gitk:6045 gitk:7285
+#: gitk:7300
 msgid "Author"
 msgstr "Författare"
 
-#: gitk:1884 gitk:3684 gitk:5471 gitk:5938
+#: gitk:1974 gitk:3790 gitk:5580 gitk:6047
 msgid "Committer"
 msgstr "Incheckare"
 
-#: gitk:1913
+#: gitk:2003
 msgid "Search"
 msgstr "Sök"
 
-#: gitk:1920
+#: gitk:2010
 msgid "Diff"
 msgstr "Diff"
 
-#: gitk:1922
+#: gitk:2012
 msgid "Old version"
 msgstr "Gammal version"
 
-#: gitk:1924
+#: gitk:2014
 msgid "New version"
 msgstr "Ny version"
 
-#: gitk:1926
+#: gitk:2016
 msgid "Lines of context"
 msgstr "Rader sammanhang"
 
-#: gitk:1936
+#: gitk:2026
 msgid "Ignore space change"
 msgstr "Ignorera ändringar i blanksteg"
 
-#: gitk:1994
+#: gitk:2084
 msgid "Patch"
 msgstr "Patch"
 
-#: gitk:1996
+#: gitk:2086
 msgid "Tree"
 msgstr "Träd"
 
-#: gitk:2121 gitk:2136 gitk:7211
+#: gitk:2213 gitk:2226
 msgid "Diff this -> selected"
 msgstr "Diff denna -> markerad"
 
-#: gitk:2123 gitk:2138 gitk:7212
+#: gitk:2214 gitk:2227
 msgid "Diff selected -> this"
 msgstr "Diff markerad -> denna"
 
-#: gitk:2125 gitk:2140 gitk:7213
+#: gitk:2215 gitk:2228
 msgid "Make patch"
 msgstr "Skapa patch"
 
-#: gitk:2126 gitk:7351
+#: gitk:2216 gitk:7494
 msgid "Create tag"
 msgstr "Skapa tagg"
 
-#: gitk:2127 gitk:7450
+#: gitk:2217 gitk:7593
 msgid "Write commit to file"
 msgstr "Skriv incheckning till fil"
 
-#: gitk:2128 gitk:7504
+#: gitk:2218 gitk:7647
 msgid "Create new branch"
 msgstr "Skapa ny gren"
 
-#: gitk:2129
+#: gitk:2219
 msgid "Cherry-pick this commit"
 msgstr "Plocka denna incheckning"
 
-#: gitk:2131
+#: gitk:2220
 msgid "Reset HEAD branch to here"
 msgstr "Återställ HEAD-grenen hit"
 
-#: gitk:2147
+#: gitk:2234
 msgid "Check out this branch"
 msgstr "Checka ut denna gren"
 
-#: gitk:2149
+#: gitk:2235
 msgid "Remove this branch"
 msgstr "Ta bort denna gren"
 
-#: gitk:2155
+#: gitk:2242
 msgid "Highlight this too"
 msgstr "Markera även detta"
 
-#: gitk:2157
+#: gitk:2243
 msgid "Highlight this only"
 msgstr "Markera bara detta"
 
-#: gitk:2159
+#: gitk:2244
 msgid "External diff"
 msgstr "Extern diff"
 
-#: gitk:2400
+#: gitk:2245
+msgid "Blame parent commit"
+msgstr ""
+
+#: gitk:2488
 msgid ""
 "\n"
 "Gitk - a commit viewer for git\n"
@@ -300,427 +304,427 @@ msgstr ""
 "\n"
 "Använd och vidareförmedla enligt villkoren i GNU General Public License"
 
-#: gitk:2408 gitk:2469 gitk:7799
+#: gitk:2496 gitk:2557 gitk:7943
 msgid "Close"
 msgstr "Stäng"
 
-#: gitk:2427
+#: gitk:2515
 msgid "Gitk key bindings"
 msgstr "Tangentbordsbindningar för Gitk"
 
-#: gitk:2429
+#: gitk:2517
 msgid "Gitk key bindings:"
 msgstr "Tangentbordsbindningar för Gitk:"
 
-#: gitk:2431
+#: gitk:2519
 #, tcl-format
 msgid "<%s-Q>\t\tQuit"
 msgstr "<%s-Q>\t\tAvsluta"
 
-#: gitk:2432
+#: gitk:2520
 msgid "<Home>\t\tMove to first commit"
 msgstr "<Home>\t\tGå till första incheckning"
 
-#: gitk:2433
+#: gitk:2521
 msgid "<End>\t\tMove to last commit"
 msgstr "<End>\t\tGå till sista incheckning"
 
-#: gitk:2434
+#: gitk:2522
 msgid "<Up>, p, i\tMove up one commit"
 msgstr "<Upp>, p, i\tGå en incheckning upp"
 
-#: gitk:2435
+#: gitk:2523
 msgid "<Down>, n, k\tMove down one commit"
 msgstr "<Ned>, n, k\tGå en incheckning ned"
 
-#: gitk:2436
+#: gitk:2524
 msgid "<Left>, z, j\tGo back in history list"
 msgstr "<Vänster>, z, j\tGå bakåt i historiken"
 
-#: gitk:2437
+#: gitk:2525
 msgid "<Right>, x, l\tGo forward in history list"
 msgstr "<Höger>, x, l\tGå framåt i historiken"
 
-#: gitk:2438
+#: gitk:2526
 msgid "<PageUp>\tMove up one page in commit list"
 msgstr "<PageUp>\tGå upp en sida i incheckningslistan"
 
-#: gitk:2439
+#: gitk:2527
 msgid "<PageDown>\tMove down one page in commit list"
 msgstr "<PageDown>\tGå ned en sida i incheckningslistan"
 
-#: gitk:2440
+#: gitk:2528
 #, tcl-format
 msgid "<%s-Home>\tScroll to top of commit list"
 msgstr "<%s-Home>\tRulla till början av incheckningslistan"
 
-#: gitk:2441
+#: gitk:2529
 #, tcl-format
 msgid "<%s-End>\tScroll to bottom of commit list"
 msgstr "<%s-End>\tRulla till slutet av incheckningslistan"
 
-#: gitk:2442
+#: gitk:2530
 #, tcl-format
 msgid "<%s-Up>\tScroll commit list up one line"
 msgstr "<%s-Upp>\tRulla incheckningslistan upp ett steg"
 
-#: gitk:2443
+#: gitk:2531
 #, tcl-format
 msgid "<%s-Down>\tScroll commit list down one line"
 msgstr "<%s-Ned>\tRulla incheckningslistan ned ett steg"
 
-#: gitk:2444
+#: gitk:2532
 #, tcl-format
 msgid "<%s-PageUp>\tScroll commit list up one page"
 msgstr "<%s-PageUp>\tRulla incheckningslistan upp en sida"
 
-#: gitk:2445
+#: gitk:2533
 #, tcl-format
 msgid "<%s-PageDown>\tScroll commit list down one page"
 msgstr "<%s-PageDown>\tRulla incheckningslistan ned en sida"
 
-#: gitk:2446
+#: gitk:2534
 msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
 msgstr "<Skift-Upp>\tSök bakåt (uppåt, senare incheckningar)"
 
-#: gitk:2447
+#: gitk:2535
 msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
 msgstr "<Skift-Ned>\tSök framåt (nedåt, tidigare incheckningar)"
 
-#: gitk:2448
+#: gitk:2536
 msgid "<Delete>, b\tScroll diff view up one page"
 msgstr "<Delete>, b\tRulla diffvisningen upp en sida"
 
-#: gitk:2449
+#: gitk:2537
 msgid "<Backspace>\tScroll diff view up one page"
 msgstr "<Baksteg>\tRulla diffvisningen upp en sida"
 
-#: gitk:2450
+#: gitk:2538
 msgid "<Space>\t\tScroll diff view down one page"
 msgstr "<Blanksteg>\tRulla diffvisningen ned en sida"
 
-#: gitk:2451
+#: gitk:2539
 msgid "u\t\tScroll diff view up 18 lines"
 msgstr "u\t\tRulla diffvisningen upp 18 rader"
 
-#: gitk:2452
+#: gitk:2540
 msgid "d\t\tScroll diff view down 18 lines"
 msgstr "d\t\tRulla diffvisningen ned 18 rader"
 
-#: gitk:2453
+#: gitk:2541
 #, tcl-format
 msgid "<%s-F>\t\tFind"
 msgstr "<%s-F>\t\tSök"
 
-#: gitk:2454
+#: gitk:2542
 #, tcl-format
 msgid "<%s-G>\t\tMove to next find hit"
 msgstr "<%s-G>\t\tGå till nästa sökträff"
 
-#: gitk:2455
+#: gitk:2543
 msgid "<Return>\tMove to next find hit"
 msgstr "<Return>\t\tGå till nästa sökträff"
 
-#: gitk:2456
+#: gitk:2544
 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:2457
+#: gitk:2545
 msgid "?\t\tMove to previous find hit"
 msgstr "?\t\tGå till föregående sökträff"
 
-#: gitk:2458
+#: gitk:2546
 msgid "f\t\tScroll diff view to next file"
 msgstr "f\t\tRulla diffvisningen till nästa fil"
 
-#: gitk:2459
+#: gitk:2547
 #, 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:2460
+#: gitk:2548
 #, 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:2461
+#: gitk:2549
 #, tcl-format
 msgid "<%s-KP+>\tIncrease font size"
 msgstr "<%s-Num+>\tÖka teckenstorlek"
 
-#: gitk:2462
+#: gitk:2550
 #, tcl-format
 msgid "<%s-plus>\tIncrease font size"
 msgstr "<%s-plus>\tÖka teckenstorlek"
 
-#: gitk:2463
+#: gitk:2551
 #, tcl-format
 msgid "<%s-KP->\tDecrease font size"
 msgstr "<%s-Num->\tMinska teckenstorlek"
 
-#: gitk:2464
+#: gitk:2552
 #, tcl-format
 msgid "<%s-minus>\tDecrease font size"
 msgstr "<%s-minus>\tMinska teckenstorlek"
 
-#: gitk:2465
+#: gitk:2553
 msgid "<F5>\t\tUpdate"
 msgstr "<F5>\t\tUppdatera"
 
-#: gitk:3091
+#: gitk:3200
 msgid "Gitk view definition"
 msgstr "Definition av Gitk-vy"
 
-#: gitk:3116
+#: gitk:3225
 msgid "Name"
 msgstr "Namn"
 
-#: gitk:3119
+#: gitk:3228
 msgid "Remember this view"
 msgstr "Spara denna vy"
 
-#: gitk:3123
+#: gitk:3232
 msgid "Commits to include (arguments to git log):"
 msgstr "Incheckningar att ta med (argument till git log):"
 
-#: gitk:3130
+#: gitk:3239
 msgid "Command to generate more commits to include:"
 msgstr "Kommando för att generera fler incheckningar att ta med:"
 
-#: gitk:3137
+#: gitk:3246
 msgid "Enter files and directories to include, one per line:"
 msgstr "Ange filer och kataloger att ta med, en per rad:"
 
-#: gitk:3184
+#: gitk:3293
 msgid "Error in commit selection arguments:"
 msgstr "Fel i argument för val av incheckningar:"
 
-#: gitk:3238 gitk:3290 gitk:3736 gitk:3750 gitk:4951 gitk:9896 gitk:9897
+#: gitk:3347 gitk:3399 gitk:3842 gitk:3856 gitk:5060 gitk:10141 gitk:10142
 msgid "None"
 msgstr "Inget"
 
-#: gitk:3684 gitk:5471 gitk:7144 gitk:7159
+#: gitk:3790 gitk:5580 gitk:7287 gitk:7302
 msgid "Date"
 msgstr "Datum"
 
-#: gitk:3684 gitk:5471
+#: gitk:3790 gitk:5580
 msgid "CDate"
 msgstr "Skapat datum"
 
-#: gitk:3833 gitk:3838
+#: gitk:3939 gitk:3944
 msgid "Descendant"
 msgstr "Avkomling"
 
-#: gitk:3834
+#: gitk:3940
 msgid "Not descendant"
 msgstr "Inte avkomling"
 
-#: gitk:3841 gitk:3846
+#: gitk:3947 gitk:3952
 msgid "Ancestor"
 msgstr "Förfader"
 
-#: gitk:3842
+#: gitk:3948
 msgid "Not ancestor"
 msgstr "Inte förfader"
 
-#: gitk:4078
+#: gitk:4187
 msgid "Local changes checked in to index but not committed"
 msgstr "Lokala ändringar sparade i indexet men inte incheckade"
 
-#: gitk:4111
+#: gitk:4220
 msgid "Local uncommitted changes, not checked in to index"
 msgstr "Lokala ändringar, ej sparade i indexet"
 
-#: gitk:5440
+#: gitk:5549
 msgid "Searching"
 msgstr "Söker"
 
-#: gitk:5940
+#: gitk:6049
 msgid "Tags:"
 msgstr "Taggar:"
 
-#: gitk:5957 gitk:5963 gitk:7137
+#: gitk:6066 gitk:6072 gitk:7280
 msgid "Parent"
 msgstr "Förälder"
 
-#: gitk:5968
+#: gitk:6077
 msgid "Child"
 msgstr "Barn"
 
-#: gitk:5977
+#: gitk:6086
 msgid "Branch"
 msgstr "Gren"
 
-#: gitk:5980
+#: gitk:6089
 msgid "Follows"
 msgstr "Följer"
 
-#: gitk:5983
+#: gitk:6092
 msgid "Precedes"
 msgstr "Föregår"
 
-#: gitk:6267
+#: gitk:6378
 msgid "Error getting merge diffs:"
 msgstr "Fel vid hämtning av sammanslagningsdiff:"
 
-#: gitk:6970
+#: gitk:7113
 msgid "Goto:"
 msgstr "Gå till:"
 
-#: gitk:6972
+#: gitk:7115
 msgid "SHA1 ID:"
 msgstr "SHA1-id:"
 
-#: gitk:6991
+#: gitk:7134
 #, tcl-format
 msgid "Short SHA1 id %s is ambiguous"
 msgstr "Förkortat SHA1-id %s är tvetydigt"
 
-#: gitk:7003
+#: gitk:7146
 #, tcl-format
 msgid "SHA1 id %s is not known"
 msgstr "SHA-id:t %s är inte känt"
 
-#: gitk:7005
+#: gitk:7148
 #, tcl-format
 msgid "Tag/Head %s is not known"
 msgstr "Tagg/huvud %s är okänt"
 
-#: gitk:7147
+#: gitk:7290
 msgid "Children"
 msgstr "Barn"
 
-#: gitk:7204
+#: gitk:7347
 #, tcl-format
 msgid "Reset %s branch to here"
 msgstr "Återställ grenen %s hit"
 
-#: gitk:7206
+#: gitk:7349
 msgid "Detached head: can't reset"
 msgstr "Frånkopplad head: kan inte återställa"
 
-#: gitk:7238
+#: gitk:7381
 msgid "Top"
 msgstr "Topp"
 
-#: gitk:7239
+#: gitk:7382
 msgid "From"
 msgstr "Från"
 
-#: gitk:7244
+#: gitk:7387
 msgid "To"
 msgstr "Till"
 
-#: gitk:7267
+#: gitk:7410
 msgid "Generate patch"
 msgstr "Generera patch"
 
-#: gitk:7269
+#: gitk:7412
 msgid "From:"
 msgstr "Från:"
 
-#: gitk:7278
+#: gitk:7421
 msgid "To:"
 msgstr "Till:"
 
-#: gitk:7287
+#: gitk:7430
 msgid "Reverse"
 msgstr "Vänd"
 
-#: gitk:7289 gitk:7464
+#: gitk:7432 gitk:7607
 msgid "Output file:"
 msgstr "Utdatafil:"
 
-#: gitk:7295
+#: gitk:7438
 msgid "Generate"
 msgstr "Generera"
 
-#: gitk:7331
+#: gitk:7474
 msgid "Error creating patch:"
 msgstr "Fel vid generering av patch:"
 
-#: gitk:7353 gitk:7452 gitk:7506
+#: gitk:7496 gitk:7595 gitk:7649
 msgid "ID:"
 msgstr "Id:"
 
-#: gitk:7362
+#: gitk:7505
 msgid "Tag name:"
 msgstr "Taggnamn:"
 
-#: gitk:7366 gitk:7515
+#: gitk:7509 gitk:7659
 msgid "Create"
 msgstr "Skapa"
 
-#: gitk:7381
+#: gitk:7524
 msgid "No tag name specified"
 msgstr "Inget taggnamn angavs"
 
-#: gitk:7385
+#: gitk:7528
 #, tcl-format
 msgid "Tag \"%s\" already exists"
 msgstr "Taggen \"%s\" finns redan"
 
-#: gitk:7391
+#: gitk:7534
 msgid "Error creating tag:"
 msgstr "Fel vid skapande av tagg:"
 
-#: gitk:7461
+#: gitk:7604
 msgid "Command:"
 msgstr "Kommando:"
 
-#: gitk:7469
+#: gitk:7612
 msgid "Write"
 msgstr "Skriv"
 
-#: gitk:7485
+#: gitk:7628
 msgid "Error writing commit:"
 msgstr "Fel vid skrivning av incheckning:"
 
-#: gitk:7511
+#: gitk:7654
 msgid "Name:"
 msgstr "Namn:"
 
-#: gitk:7530
+#: gitk:7674
 msgid "Please specify a name for the new branch"
 msgstr "Ange ett namn för den nya grenen"
 
-#: gitk:7559
+#: gitk:7703
 #, 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:7564
+#: gitk:7708
 msgid "Cherry-picking"
 msgstr "Plockar"
 
-#: gitk:7576
+#: gitk:7720
 msgid "No changes committed"
 msgstr "Inga ändringar incheckade"
 
-#: gitk:7601
+#: gitk:7745
 msgid "Confirm reset"
 msgstr "Bekräfta återställning"
 
-#: gitk:7603
+#: gitk:7747
 #, tcl-format
 msgid "Reset branch %s to %s?"
 msgstr "Återställa grenen %s till %s?"
 
-#: gitk:7607
+#: gitk:7751
 msgid "Reset type:"
 msgstr "Typ av återställning:"
 
-#: gitk:7611
+#: gitk:7755
 msgid "Soft: Leave working tree and index untouched"
 msgstr "Mjuk: Rör inte utcheckning och index"
 
-#: gitk:7614
+#: gitk:7758
 msgid "Mixed: Leave working tree untouched, reset index"
 msgstr "Blandad: Rör inte utcheckning, återställ index"
 
-#: gitk:7617
+#: gitk:7761
 msgid ""
 "Hard: Reset working tree and index\n"
 "(discard ALL local changes)"
@@ -728,19 +732,19 @@ msgstr ""
 "Hård: Återställ utcheckning och index\n"
 "(förkastar ALLA lokala ändringar)"
 
-#: gitk:7633
+#: gitk:7777
 msgid "Resetting"
 msgstr "Återställer"
 
-#: gitk:7690
+#: gitk:7834
 msgid "Checking out"
 msgstr "Checkar ut"
 
-#: gitk:7741
+#: gitk:7885
 msgid "Cannot delete the currently checked-out branch"
 msgstr "Kan inte ta bort den just nu utcheckade grenen"
 
-#: gitk:7747
+#: gitk:7891
 #, tcl-format
 msgid ""
 "The commits on branch %s aren't on any other branch.\n"
@@ -749,16 +753,16 @@ msgstr ""
 "Incheckningarna på grenen %s existerar inte på någon annan gren.\n"
 "Vill du verkligen ta bort grenen %s?"
 
-#: gitk:7778
+#: gitk:7922
 #, tcl-format
 msgid "Tags and heads: %s"
 msgstr "Taggar och huvuden: %s"
 
-#: gitk:7792
+#: gitk:7936
 msgid "Filter"
 msgstr "Filter"
 
-#: gitk:8086
+#: gitk:8230
 msgid ""
 "Error reading commit topology information; branch and preceding/following "
 "tag information will be incomplete."
@@ -766,125 +770,129 @@ msgstr ""
 "Fel vid läsning av information om incheckningstopologi; information om "
 "grenar och föregående/senare taggar kommer inte vara komplett."
 
-#: gitk:9072
+#: gitk:9216
 msgid "Tag"
 msgstr "Tagg"
 
-#: gitk:9072
+#: gitk:9216
 msgid "Id"
 msgstr "Id"
 
-#: gitk:9118
+#: gitk:9262
 msgid "Gitk font chooser"
 msgstr "Teckensnittsväljare för Gitk"
 
-#: gitk:9135
+#: gitk:9279
 msgid "B"
 msgstr "F"
 
-#: gitk:9138
+#: gitk:9282
 msgid "I"
 msgstr "K"
 
-#: gitk:9231
+#: gitk:9375
 msgid "Gitk preferences"
 msgstr "Inställningar för Gitk"
 
-#: gitk:9232
+#: gitk:9376
 msgid "Commit list display options"
 msgstr "Alternativ för incheckningslistvy"
 
-#: gitk:9235
+#: gitk:9379
 msgid "Maximum graph width (lines)"
 msgstr "Maximal grafbredd (rader)"
 
-#: gitk:9239
+#: gitk:9383
 #, tcl-format
 msgid "Maximum graph width (% of pane)"
 msgstr "Maximal grafbredd (% av ruta)"
 
-#: gitk:9244
+#: gitk:9388
 msgid "Show local changes"
 msgstr "Visa lokala ändringar"
 
-#: gitk:9249
+#: gitk:9393
 msgid "Auto-select SHA1"
 msgstr "Välj SHA1 automatiskt"
 
-#: gitk:9254
+#: gitk:9398
 msgid "Diff display options"
 msgstr "Alternativ för diffvy"
 
-#: gitk:9256
+#: gitk:9400
 msgid "Tab spacing"
 msgstr "Blanksteg för tabulatortecken"
 
-#: gitk:9260
+#: gitk:9404
 msgid "Display nearby tags"
 msgstr "Visa närliggande taggar"
 
-#: gitk:9265
+#: gitk:9409
 msgid "Limit diffs to listed paths"
 msgstr "Begränsa diff till listade sökvägar"
 
-#: gitk:9272
+#: gitk:9414
+msgid "Support per-file encodings"
+msgstr ""
+
+#: gitk:9421
 msgid "External diff tool"
 msgstr "Externt diff-verktyg"
 
-#: gitk:9274
+#: gitk:9423
 msgid "Choose..."
 msgstr "Välj..."
 
-#: gitk:9279
+#: gitk:9428
 msgid "Colors: press to choose"
 msgstr "Färger: tryck för att välja"
 
-#: gitk:9282
+#: gitk:9431
 msgid "Background"
 msgstr "Bakgrund"
 
-#: gitk:9286
+#: gitk:9435
 msgid "Foreground"
 msgstr "Förgrund"
 
-#: gitk:9290
+#: gitk:9439
 msgid "Diff: old lines"
 msgstr "Diff: gamla rader"
 
-#: gitk:9295
+#: gitk:9444
 msgid "Diff: new lines"
 msgstr "Diff: nya rader"
 
-#: gitk:9300
+#: gitk:9449
 msgid "Diff: hunk header"
 msgstr "Diff: delhuvud"
 
-#: gitk:9306
+#: gitk:9455
 msgid "Select bg"
 msgstr "Markerad bakgrund"
 
-#: gitk:9310
+#: gitk:9459
 msgid "Fonts: press to choose"
 msgstr "Teckensnitt: tryck för att välja"
 
-#: gitk:9312
+#: gitk:9461
 msgid "Main font"
 msgstr "Huvudteckensnitt"
 
-#: gitk:9313
+#: gitk:9462
 msgid "Diff display font"
 msgstr "Teckensnitt för diffvisning"
 
-#: gitk:9314
+#: gitk:9463
 msgid "User interface font"
 msgstr "Teckensnitt för användargränssnitt"
 
-#: gitk:9339
+#: gitk:9488
 #, tcl-format
 msgid "Gitk: choose color for %s"
 msgstr "Gitk: välj färg för %s"
 
-#: gitk:9720
+#: gitk:9934
 msgid ""
 "Sorry, gitk cannot run with this version of Tcl/Tk.\n"
 " Gitk requires at least Tcl/Tk 8.4."
@@ -892,24 +900,24 @@ msgstr ""
 "Gitk kan tyvärr inte köra med denna version av Tcl/Tk.\n"
 " Gitk kräver åtminstone Tcl/Tk 8.4."
 
-#: gitk:9812
+#: gitk:10047
 msgid "Cannot find a git repository here."
 msgstr "Hittar inget gitk-arkiv här."
 
-#: gitk:9816
+#: gitk:10051
 #, tcl-format
 msgid "Cannot find the git directory \"%s\"."
 msgstr "Hittar inte git-katalogen \"%s\"."
 
-#: gitk:9853
+#: gitk:10098
 #, tcl-format
 msgid "Ambiguous argument '%s': both revision and filename"
 msgstr "Tvetydigt argument \"%s\": både revision och filnamn"
 
-#: gitk:9865
+#: gitk:10110
 msgid "Bad arguments to gitk:"
 msgstr "Felaktiga argument till gitk:"
 
-#: gitk:9925
+#: gitk:10170
 msgid "Command line"
 msgstr "Kommandorad"
index 26967e201aca8ea1c799e6b21cad468484753779..18c9ce35e8fc6566663ad76dd04bd1aa70035c25 100644 (file)
@@ -166,6 +166,27 @@ Gitweb repositories
   shows repositories only if this file exists in its object database
   (if directory has the magic file named $export_ok).
 
+- Finally, it is possible to specify an arbitrary perl subroutine that
+  will be called for each project to determine if it can be exported.
+  The subroutine receives an absolute path to the project as its only
+  parameter.
+
+  For example, if you use mod_perl to run the script, and have dumb
+  http protocol authentication configured for your repositories, you
+  can use the following hook to allow access only if the user is
+  authorized to read the files:
+
+    $export_auth_hook = sub {
+        use Apache2::SubRequest ();
+        use Apache2::Const -compile => qw(HTTP_OK);
+        my $path = "$_[0]/HEAD";
+        my $r    = Apache2::RequestUtil->request;
+        my $sub  = $r->lookup_file($path);
+        return $sub->filename eq $path
+            && $sub->status == Apache2::Const::HTTP_OK;
+    };
+
+
 Generating projects list using gitweb
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index cc6edbede88809a43eda285de4e9a4d36d87a78d..933e137386fe561d5ded885e62ebc82e27c08179 100755 (executable)
@@ -29,7 +29,9 @@ BEGIN
 
 # if we're called with PATH_INFO, we have to strip that
 # from the URL to find our real URL
-if (my $path_info = $ENV{"PATH_INFO"}) {
+# we make $path_info global because it's also used later on
+our $path_info = $ENV{"PATH_INFO"};
+if ($path_info) {
        $my_url =~ s,\Q$path_info\E$,,;
        $my_uri =~ s,\Q$path_info\E$,,;
 }
@@ -93,6 +95,11 @@ BEGIN
 # (only effective if this variable evaluates to true)
 our $export_ok = "++GITWEB_EXPORT_OK++";
 
+# show repository only if this subroutine returns true
+# when given the path to the project, for example:
+#    sub { return -e "$_[0]/git-daemon-export-ok"; }
+our $export_auth_hook = undef;
+
 # only allow viewing of repositories also shown on the overview page
 our $strict_export = "++GITWEB_STRICT_EXPORT++";
 
@@ -398,7 +405,8 @@ sub check_head_link {
 sub check_export_ok {
        my ($dir) = @_;
        return (check_head_link($dir) &&
-               (!$export_ok || -e "$dir/$export_ok"));
+               (!$export_ok || -e "$dir/$export_ok") &&
+               (!$export_auth_hook || $export_auth_hook->($dir)));
 }
 
 # process alternate names for backward compatibility
@@ -428,34 +436,258 @@ sub filter_snapshot_fmts {
 
 # ======================================================================
 # input validation and dispatch
-our $action = $cgi->param('a');
+
+# input parameters can be collected from a variety of sources (presently, CGI
+# and PATH_INFO), so we define an %input_params hash that collects them all
+# together during validation: this allows subsequent uses (e.g. href()) to be
+# agnostic of the parameter origin
+
+our %input_params = ();
+
+# input parameters are stored with the long parameter name as key. This will
+# also be used in the href subroutine to convert parameters to their CGI
+# equivalent, and since the href() usage is the most frequent one, we store
+# the name -> CGI key mapping here, instead of the reverse.
+#
+# XXX: Warning: If you touch this, check the search form for updating,
+# too.
+
+our @cgi_param_mapping = (
+       project => "p",
+       action => "a",
+       file_name => "f",
+       file_parent => "fp",
+       hash => "h",
+       hash_parent => "hp",
+       hash_base => "hb",
+       hash_parent_base => "hpb",
+       page => "pg",
+       order => "o",
+       searchtext => "s",
+       searchtype => "st",
+       snapshot_format => "sf",
+       extra_options => "opt",
+       search_use_regexp => "sr",
+);
+our %cgi_param_mapping = @cgi_param_mapping;
+
+# we will also need to know the possible actions, for validation
+our %actions = (
+       "blame" => \&git_blame,
+       "blobdiff" => \&git_blobdiff,
+       "blobdiff_plain" => \&git_blobdiff_plain,
+       "blob" => \&git_blob,
+       "blob_plain" => \&git_blob_plain,
+       "commitdiff" => \&git_commitdiff,
+       "commitdiff_plain" => \&git_commitdiff_plain,
+       "commit" => \&git_commit,
+       "forks" => \&git_forks,
+       "heads" => \&git_heads,
+       "history" => \&git_history,
+       "log" => \&git_log,
+       "rss" => \&git_rss,
+       "atom" => \&git_atom,
+       "search" => \&git_search,
+       "search_help" => \&git_search_help,
+       "shortlog" => \&git_shortlog,
+       "summary" => \&git_summary,
+       "tag" => \&git_tag,
+       "tags" => \&git_tags,
+       "tree" => \&git_tree,
+       "snapshot" => \&git_snapshot,
+       "object" => \&git_object,
+       # those below don't need $project
+       "opml" => \&git_opml,
+       "project_list" => \&git_project_list,
+       "project_index" => \&git_project_index,
+);
+
+# finally, we have the hash of allowed extra_options for the commands that
+# allow them
+our %allowed_options = (
+       "--no-merges" => [ qw(rss atom log shortlog history) ],
+);
+
+# fill %input_params with the CGI parameters. All values except for 'opt'
+# should be single values, but opt can be an array. We should probably
+# build an array of parameters that can be multi-valued, but since for the time
+# being it's only this one, we just single it out
+while (my ($name, $symbol) = each %cgi_param_mapping) {
+       if ($symbol eq 'opt') {
+               $input_params{$name} = [ $cgi->param($symbol) ];
+       } else {
+               $input_params{$name} = $cgi->param($symbol);
+       }
+}
+
+# now read PATH_INFO and update the parameter list for missing parameters
+sub evaluate_path_info {
+       return if defined $input_params{'project'};
+       return if !$path_info;
+       $path_info =~ s,^/+,,;
+       return if !$path_info;
+
+       # find which part of PATH_INFO is project
+       my $project = $path_info;
+       $project =~ s,/+$,,;
+       while ($project && !check_head_link("$projectroot/$project")) {
+               $project =~ s,/*[^/]*$,,;
+       }
+       return unless $project;
+       $input_params{'project'} = $project;
+
+       # do not change any parameters if an action is given using the query string
+       return if $input_params{'action'};
+       $path_info =~ s,^\Q$project\E/*,,;
+
+       # next, check if we have an action
+       my $action = $path_info;
+       $action =~ s,/.*$,,;
+       if (exists $actions{$action}) {
+               $path_info =~ s,^$action/*,,;
+               $input_params{'action'} = $action;
+       }
+
+       # list of actions that want hash_base instead of hash, but can have no
+       # pathname (f) parameter
+       my @wants_base = (
+               'tree',
+               'history',
+       );
+
+       # we want to catch
+       # [$hash_parent_base[:$file_parent]..]$hash_parent[:$file_name]
+       my ($parentrefname, $parentpathname, $refname, $pathname) =
+               ($path_info =~ /^(?:(.+?)(?::(.+))?\.\.)?(.+?)(?::(.+))?$/);
+
+       # first, analyze the 'current' part
+       if (defined $pathname) {
+               # we got "branch:filename" or "branch:dir/"
+               # we could use git_get_type(branch:pathname), but:
+               # - it needs $git_dir
+               # - it does a git() call
+               # - the convention of terminating directories with a slash
+               #   makes it superfluous
+               # - embedding the action in the PATH_INFO would make it even
+               #   more superfluous
+               $pathname =~ s,^/+,,;
+               if (!$pathname || substr($pathname, -1) eq "/") {
+                       $input_params{'action'} ||= "tree";
+                       $pathname =~ s,/$,,;
+               } else {
+                       # the default action depends on whether we had parent info
+                       # or not
+                       if ($parentrefname) {
+                               $input_params{'action'} ||= "blobdiff_plain";
+                       } else {
+                               $input_params{'action'} ||= "blob_plain";
+                       }
+               }
+               $input_params{'hash_base'} ||= $refname;
+               $input_params{'file_name'} ||= $pathname;
+       } elsif (defined $refname) {
+               # we got "branch". In this case we have to choose if we have to
+               # set hash or hash_base.
+               #
+               # Most of the actions without a pathname only want hash to be
+               # set, except for the ones specified in @wants_base that want
+               # hash_base instead. It should also be noted that hand-crafted
+               # links having 'history' as an action and no pathname or hash
+               # set will fail, but that happens regardless of PATH_INFO.
+               $input_params{'action'} ||= "shortlog";
+               if (grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_base'} ||= $refname;
+               } else {
+                       $input_params{'hash'} ||= $refname;
+               }
+       }
+
+       # next, handle the 'parent' part, if present
+       if (defined $parentrefname) {
+               # a missing pathspec defaults to the 'current' filename, allowing e.g.
+               # someproject/blobdiff/oldrev..newrev:/filename
+               if ($parentpathname) {
+                       $parentpathname =~ s,^/+,,;
+                       $parentpathname =~ s,/$,,;
+                       $input_params{'file_parent'} ||= $parentpathname;
+               } else {
+                       $input_params{'file_parent'} ||= $input_params{'file_name'};
+               }
+               # we assume that hash_parent_base is wanted if a path was specified,
+               # or if the action wants hash_base instead of hash
+               if (defined $input_params{'file_parent'} ||
+                       grep { $_ eq $input_params{'action'} } @wants_base) {
+                       $input_params{'hash_parent_base'} ||= $parentrefname;
+               } else {
+                       $input_params{'hash_parent'} ||= $parentrefname;
+               }
+       }
+
+       # for the snapshot action, we allow URLs in the form
+       # $project/snapshot/$hash.ext
+       # where .ext determines the snapshot and gets removed from the
+       # passed $refname to provide the $hash.
+       #
+       # To be able to tell that $refname includes the format extension, we
+       # require the following two conditions to be satisfied:
+       # - the hash input parameter MUST have been set from the $refname part
+       #   of the URL (i.e. they must be equal)
+       # - the snapshot format MUST NOT have been defined already (e.g. from
+       #   CGI parameter sf)
+       # It's also useless to try any matching unless $refname has a dot,
+       # so we check for that too
+       if (defined $input_params{'action'} &&
+               $input_params{'action'} eq 'snapshot' &&
+               defined $refname && index($refname, '.') != -1 &&
+               $refname eq $input_params{'hash'} &&
+               !defined $input_params{'snapshot_format'}) {
+               # We loop over the known snapshot formats, checking for
+               # extensions. Allowed extensions are both the defined suffix
+               # (which includes the initial dot already) and the snapshot
+               # format key itself, with a prepended dot
+               while (my ($fmt, %opt) = each %known_snapshot_formats) {
+                       my $hash = $refname;
+                       my $sfx;
+                       $hash =~ s/(\Q$opt{'suffix'}\E|\Q.$fmt\E)$//;
+                       next unless $sfx = $1;
+                       # a valid suffix was found, so set the snapshot format
+                       # and reset the hash parameter
+                       $input_params{'snapshot_format'} = $fmt;
+                       $input_params{'hash'} = $hash;
+                       # we also set the format suffix to the one requested
+                       # in the URL: this way a request for e.g. .tgz returns
+                       # a .tgz instead of a .tar.gz
+                       $known_snapshot_formats{$fmt}{'suffix'} = $sfx;
+                       last;
+               }
+       }
+}
+evaluate_path_info();
+
+our $action = $input_params{'action'};
 if (defined $action) {
-       if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
+       if (!validate_action($action)) {
                die_error(400, "Invalid action parameter");
        }
 }
 
 # parameters which are pathnames
-our $project = $cgi->param('p');
+our $project = $input_params{'project'};
 if (defined $project) {
-       if (!validate_pathname($project) ||
-           !(-d "$projectroot/$project") ||
-           !check_head_link("$projectroot/$project") ||
-           ($export_ok && !(-e "$projectroot/$project/$export_ok")) ||
-           ($strict_export && !project_in_list($project))) {
+       if (!validate_project($project)) {
                undef $project;
                die_error(404, "No such project");
        }
 }
 
-our $file_name = $cgi->param('f');
+our $file_name = $input_params{'file_name'};
 if (defined $file_name) {
        if (!validate_pathname($file_name)) {
                die_error(400, "Invalid file parameter");
        }
 }
 
-our $file_parent = $cgi->param('fp');
+our $file_parent = $input_params{'file_parent'};
 if (defined $file_parent) {
        if (!validate_pathname($file_parent)) {
                die_error(400, "Invalid file parent parameter");
@@ -463,44 +695,41 @@ sub filter_snapshot_fmts {
 }
 
 # parameters which are refnames
-our $hash = $cgi->param('h');
+our $hash = $input_params{'hash'};
 if (defined $hash) {
        if (!validate_refname($hash)) {
                die_error(400, "Invalid hash parameter");
        }
 }
 
-our $hash_parent = $cgi->param('hp');
+our $hash_parent = $input_params{'hash_parent'};
 if (defined $hash_parent) {
        if (!validate_refname($hash_parent)) {
                die_error(400, "Invalid hash parent parameter");
        }
 }
 
-our $hash_base = $cgi->param('hb');
+our $hash_base = $input_params{'hash_base'};
 if (defined $hash_base) {
        if (!validate_refname($hash_base)) {
                die_error(400, "Invalid hash base parameter");
        }
 }
 
-my %allowed_options = (
-       "--no-merges" => [ qw(rss atom log shortlog history) ],
-);
-
-our @extra_options = $cgi->param('opt');
-if (defined @extra_options) {
-       foreach my $opt (@extra_options) {
-               if (not exists $allowed_options{$opt}) {
-                       die_error(400, "Invalid option parameter");
-               }
-               if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
-                       die_error(400, "Invalid option parameter for this action");
-               }
+our @extra_options = @{$input_params{'extra_options'}};
+# @extra_options is always defined, since it can only be (currently) set from
+# CGI, and $cgi->param() returns the empty array in array context if the param
+# is not set
+foreach my $opt (@extra_options) {
+       if (not exists $allowed_options{$opt}) {
+               die_error(400, "Invalid option parameter");
+       }
+       if (not grep(/^$action$/, @{$allowed_options{$opt}})) {
+               die_error(400, "Invalid option parameter for this action");
        }
 }
 
-our $hash_parent_base = $cgi->param('hpb');
+our $hash_parent_base = $input_params{'hash_parent_base'};
 if (defined $hash_parent_base) {
        if (!validate_refname($hash_parent_base)) {
                die_error(400, "Invalid hash parent base parameter");
@@ -508,23 +737,23 @@ sub filter_snapshot_fmts {
 }
 
 # other parameters
-our $page = $cgi->param('pg');
+our $page = $input_params{'page'};
 if (defined $page) {
        if ($page =~ m/[^0-9]/) {
                die_error(400, "Invalid page parameter");
        }
 }
 
-our $searchtype = $cgi->param('st');
+our $searchtype = $input_params{'searchtype'};
 if (defined $searchtype) {
        if ($searchtype =~ m/[^a-z]/) {
                die_error(400, "Invalid searchtype parameter");
        }
 }
 
-our $search_use_regexp = $cgi->param('sr');
+our $search_use_regexp = $input_params{'search_use_regexp'};
 
-our $searchtext = $cgi->param('s');
+our $searchtext = $input_params{'searchtext'};
 our $search_regexp;
 if (defined $searchtext) {
        if (length($searchtext) < 2) {
@@ -533,86 +762,15 @@ sub filter_snapshot_fmts {
        $search_regexp = $search_use_regexp ? $searchtext : quotemeta $searchtext;
 }
 
-# now read PATH_INFO and use it as alternative to parameters
-sub evaluate_path_info {
-       return if defined $project;
-       my $path_info = $ENV{"PATH_INFO"};
-       return if !$path_info;
-       $path_info =~ s,^/+,,;
-       return if !$path_info;
-       # find which part of PATH_INFO is project
-       $project = $path_info;
-       $project =~ s,/+$,,;
-       while ($project && !check_head_link("$projectroot/$project")) {
-               $project =~ s,/*[^/]*$,,;
-       }
-       # validate project
-       $project = validate_pathname($project);
-       if (!$project ||
-           ($export_ok && !-e "$projectroot/$project/$export_ok") ||
-           ($strict_export && !project_in_list($project))) {
-               undef $project;
-               return;
-       }
-       # do not change any parameters if an action is given using the query string
-       return if $action;
-       $path_info =~ s,^\Q$project\E/*,,;
-       my ($refname, $pathname) = split(/:/, $path_info, 2);
-       if (defined $pathname) {
-               # we got "project.git/branch:filename" or "project.git/branch:dir/"
-               # we could use git_get_type(branch:pathname), but it needs $git_dir
-               $pathname =~ s,^/+,,;
-               if (!$pathname || substr($pathname, -1) eq "/") {
-                       $action  ||= "tree";
-                       $pathname =~ s,/$,,;
-               } else {
-                       $action  ||= "blob_plain";
-               }
-               $hash_base ||= validate_refname($refname);
-               $file_name ||= validate_pathname($pathname);
-       } elsif (defined $refname) {
-               # we got "project.git/branch"
-               $action ||= "shortlog";
-               $hash   ||= validate_refname($refname);
-       }
-}
-evaluate_path_info();
-
 # path to the current git repository
 our $git_dir;
 $git_dir = "$projectroot/$project" if $project;
 
-# dispatch
-my %actions = (
-       "blame" => \&git_blame,
-       "blobdiff" => \&git_blobdiff,
-       "blobdiff_plain" => \&git_blobdiff_plain,
-       "blob" => \&git_blob,
-       "blob_plain" => \&git_blob_plain,
-       "commitdiff" => \&git_commitdiff,
-       "commitdiff_plain" => \&git_commitdiff_plain,
-       "commit" => \&git_commit,
-       "forks" => \&git_forks,
-       "heads" => \&git_heads,
-       "history" => \&git_history,
-       "log" => \&git_log,
-       "rss" => \&git_rss,
-       "atom" => \&git_atom,
-       "search" => \&git_search,
-       "search_help" => \&git_search_help,
-       "shortlog" => \&git_shortlog,
-       "summary" => \&git_summary,
-       "tag" => \&git_tag,
-       "tags" => \&git_tags,
-       "tree" => \&git_tree,
-       "snapshot" => \&git_snapshot,
-       "object" => \&git_object,
-       # those below don't need $project
-       "opml" => \&git_opml,
-       "project_list" => \&git_project_list,
-       "project_index" => \&git_project_index,
-);
+# list of supported snapshot formats
+our @snapshot_fmts = gitweb_check_feature('snapshot');
+@snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
 
+# dispatch
 if (!defined $action) {
        if (defined $hash) {
                $action = git_get_type($hash);
@@ -642,55 +800,96 @@ (%)
        # default is to use -absolute url() i.e. $my_uri
        my $href = $params{-full} ? $my_url : $my_uri;
 
-       # XXX: Warning: If you touch this, check the search form for updating,
-       # too.
-
-       my @mapping = (
-               project => "p",
-               action => "a",
-               file_name => "f",
-               file_parent => "fp",
-               hash => "h",
-               hash_parent => "hp",
-               hash_base => "hb",
-               hash_parent_base => "hpb",
-               page => "pg",
-               order => "o",
-               searchtext => "s",
-               searchtype => "st",
-               snapshot_format => "sf",
-               extra_options => "opt",
-               search_use_regexp => "sr",
-       );
-       my %mapping = @mapping;
-
        $params{'project'} = $project unless exists $params{'project'};
 
        if ($params{-replay}) {
-               while (my ($name, $symbol) = each %mapping) {
+               while (my ($name, $symbol) = each %cgi_param_mapping) {
                        if (!exists $params{$name}) {
-                               # to allow for multivalued params we use arrayref form
-                               $params{$name} = [ $cgi->param($symbol) ];
+                               $params{$name} = $input_params{$name};
                        }
                }
        }
 
        my ($use_pathinfo) = gitweb_check_feature('pathinfo');
        if ($use_pathinfo) {
-               # use PATH_INFO for project name
+               # try to put as many parameters as possible in PATH_INFO:
+               #   - project name
+               #   - action
+               #   - hash_parent or hash_parent_base:/file_parent
+               #   - hash or hash_base:/filename
+               #   - the snapshot_format as an appropriate suffix
+
+               # When the script is the root DirectoryIndex for the domain,
+               # $href here would be something like http://gitweb.example.com/
+               # Thus, we strip any trailing / from $href, to spare us double
+               # slashes in the final URL
+               $href =~ s,/$,,;
+
+               # Then add the project name, if present
                $href .= "/".esc_url($params{'project'}) if defined $params{'project'};
                delete $params{'project'};
 
-               # Summary just uses the project path URL
-               if (defined $params{'action'} && $params{'action'} eq 'summary') {
+               # since we destructively absorb parameters, we keep this
+               # boolean that remembers if we're handling a snapshot
+               my $is_snapshot = $params{'action'} eq 'snapshot';
+
+               # Summary just uses the project path URL, any other action is
+               # added to the URL
+               if (defined $params{'action'}) {
+                       $href .= "/".esc_url($params{'action'}) unless $params{'action'} eq 'summary';
                        delete $params{'action'};
                }
+
+               # Next, we put hash_parent_base:/file_parent..hash_base:/file_name,
+               # stripping nonexistent or useless pieces
+               $href .= "/" if ($params{'hash_base'} || $params{'hash_parent_base'}
+                       || $params{'hash_parent'} || $params{'hash'});
+               if (defined $params{'hash_base'}) {
+                       if (defined $params{'hash_parent_base'}) {
+                               $href .= esc_url($params{'hash_parent_base'});
+                               # skip the file_parent if it's the same as the file_name
+                               delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
+                               if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
+                                       $href .= ":/".esc_url($params{'file_parent'});
+                                       delete $params{'file_parent'};
+                               }
+                               $href .= "..";
+                               delete $params{'hash_parent'};
+                               delete $params{'hash_parent_base'};
+                       } elsif (defined $params{'hash_parent'}) {
+                               $href .= esc_url($params{'hash_parent'}). "..";
+                               delete $params{'hash_parent'};
+                       }
+
+                       $href .= esc_url($params{'hash_base'});
+                       if (defined $params{'file_name'} && $params{'file_name'} !~ /\.\./) {
+                               $href .= ":/".esc_url($params{'file_name'});
+                               delete $params{'file_name'};
+                       }
+                       delete $params{'hash'};
+                       delete $params{'hash_base'};
+               } elsif (defined $params{'hash'}) {
+                       $href .= esc_url($params{'hash'});
+                       delete $params{'hash'};
+               }
+
+               # If the action was a snapshot, we can absorb the
+               # snapshot_format parameter too
+               if ($is_snapshot) {
+                       my $fmt = $params{'snapshot_format'};
+                       # snapshot_format should always be defined when href()
+                       # is called, but just in case some code forgets, we
+                       # fall back to the default
+                       $fmt ||= $snapshot_fmts[0];
+                       $href .= $known_snapshot_formats{$fmt}{'suffix'};
+                       delete $params{'snapshot_format'};
+               }
        }
 
        # now encode the parameters explicitly
        my @result = ();
-       for (my $i = 0; $i < @mapping; $i += 2) {
-               my ($name, $symbol) = ($mapping[$i], $mapping[$i+1]);
+       for (my $i = 0; $i < @cgi_param_mapping; $i += 2) {
+               my ($name, $symbol) = ($cgi_param_mapping[$i], $cgi_param_mapping[$i+1]);
                if (defined $params{$name}) {
                        if (ref($params{$name}) eq "ARRAY") {
                                foreach my $par (@{$params{$name}}) {
@@ -710,6 +909,24 @@ (%)
 ## ======================================================================
 ## validation, quoting/unquoting and escaping
 
+sub validate_action {
+       my $input = shift || return undef;
+       return undef unless exists $actions{$input};
+       return $input;
+}
+
+sub validate_project {
+       my $input = shift || return undef;
+       if (!validate_pathname($input) ||
+               !(-d "$projectroot/$input") ||
+               !check_export_ok("$projectroot/$input") ||
+               ($strict_export && !project_in_list($input))) {
+               return undef;
+       } else {
+               return $input;
+       }
+}
+
 sub validate_pathname {
        my $input = shift || return undef;
 
@@ -1495,8 +1712,6 @@ sub format_diff_line {
 # linked.  Pass the hash of the tree/commit to snapshot.
 sub format_snapshot_links {
        my ($hash) = @_;
-       my @snapshot_fmts = gitweb_check_feature('snapshot');
-       @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
        my $num_fmts = @snapshot_fmts;
        if ($num_fmts > 1) {
                # A parenthesized list of links bearing format names.
@@ -1805,7 +2020,10 @@ sub git_get_project_ctags {
        my $ctags = {};
 
        $git_dir = "$projectroot/$path";
-       foreach (<$git_dir/ctags/*>) {
+       unless (opendir D, "$git_dir/ctags") {
+               return $ctags;
+       }
+       foreach (grep { -f $_ } map { "$git_dir/ctags/$_" } readdir(D)) {
                open CT, $_ or next;
                my $val = <CT>;
                chomp $val;
@@ -1813,6 +2031,7 @@ sub git_get_project_ctags {
                my $ctag = $_; $ctag =~ s#.*/##;
                $ctags->{$ctag} = $val;
        }
+       closedir D;
        $ctags;
 }
 
@@ -4126,7 +4345,7 @@ sub git_search_grep_body {
 ## actions
 
 sub git_project_list {
-       my $order = $cgi->param('o');
+       my $order = $input_params{'order'};
        if (defined $order && $order !~ m/none|project|descr|owner|age/) {
                die_error(400, "Unknown order parameter");
        }
@@ -4154,7 +4373,7 @@ sub git_project_list {
 }
 
 sub git_forks {
-       my $order = $cgi->param('o');
+       my $order = $input_params{'order'};
        if (defined $order && $order !~ m/none|project|descr|owner|age/) {
                die_error(400, "Unknown order parameter");
        }
@@ -4699,20 +4918,17 @@ sub git_tree {
 }
 
 sub git_snapshot {
-       my @supported_fmts = gitweb_check_feature('snapshot');
-       @supported_fmts = filter_snapshot_fmts(@supported_fmts);
-
-       my $format = $cgi->param('sf');
-       if (!@supported_fmts) {
+       my $format = $input_params{'snapshot_format'};
+       if (!@snapshot_fmts) {
                die_error(403, "Snapshots not allowed");
        }
        # default to first supported snapshot format
-       $format ||= $supported_fmts[0];
+       $format ||= $snapshot_fmts[0];
        if ($format !~ m/^[a-z0-9]+$/) {
                die_error(400, "Invalid snapshot format parameter");
        } elsif (!exists($known_snapshot_formats{$format})) {
                die_error(400, "Unknown snapshot format");
-       } elsif (!grep($_ eq $format, @supported_fmts)) {
+       } elsif (!grep($_ eq $format, @snapshot_fmts)) {
                die_error(403, "Unsupported snapshot format");
        }
 
diff --git a/grep.c b/grep.c
index e2c190a7f66f9e9311f91d9a41b7dc3f295f00fb..600f69f2fe2a0271f4bdf736f95f70c8f7381aa4 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -514,7 +514,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                                if (from <= last_shown)
                                        from = last_shown + 1;
                                if (last_shown && from != last_shown + 1)
-                                       printf(hunk_mark);
+                                       fputs(hunk_mark, stdout);
                                while (from < lno) {
                                        pcl = &prev[lno-from-1];
                                        show_line(opt, pcl->bol, pcl->eol,
@@ -524,7 +524,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                                last_shown = lno-1;
                        }
                        if (last_shown && lno != last_shown + 1)
-                               printf(hunk_mark);
+                               fputs(hunk_mark, stdout);
                        if (!opt->count)
                                show_line(opt, bol, eol, name, lno, ':');
                        last_shown = last_hit = lno;
@@ -535,7 +535,7 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         * we need to show this line.
                         */
                        if (last_shown && lno != last_shown + 1)
-                               printf(hunk_mark);
+                               fputs(hunk_mark, stdout);
                        show_line(opt, bol, eol, name, lno, '-');
                        last_shown = lno;
                }
index 20937ff94c1ce6ab7984d7b6b1903307e303eb60..846e91a23126b747fbea8d9a8511f708c3d70e43 100644 (file)
@@ -110,7 +110,7 @@ int main(int argc, const char **argv)
        }
 
        if (errstr) {
-               error (errstr);
+               error("%s", errstr);
                usage_with_options(hash_object_usage, hash_object_options);
        }
 
index 42f4d78e54edbb73d14952f5fb3992f2cebb40ae..5cecef434a7740a3f853462978c3e071b4da7e74 100644 (file)
@@ -1780,7 +1780,7 @@ static void one_remote_ref(char *refname)
        struct ref *ref;
        struct object *obj;
 
-       ref = alloc_ref_from_str(refname);
+       ref = alloc_ref(refname);
 
        if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
@@ -1887,7 +1887,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
        char *ref_info;
        struct ref *ref;
 
-       ref = alloc_ref_from_str(ls->dentry_name);
+       ref = alloc_ref(ls->dentry_name);
 
        if (http_fetch_ref(remote->url, ref) != 0) {
                fprintf(stderr,
index d3a4d31b4e92d69eb32d1a18ebb7be1f7470d1d1..60ed41a993bf9e213b7dfde5ff43528eff6b6252 100644 (file)
@@ -221,17 +221,23 @@ static void bad_object(unsigned long offset, const char *format, ...)
        die("pack has bad object at offset %lu: %s", offset, buf);
 }
 
+static void free_base_data(struct base_data *c)
+{
+       if (c->data) {
+               free(c->data);
+               c->data = NULL;
+               base_cache_used -= c->size;
+       }
+}
+
 static void prune_base_data(struct base_data *retain)
 {
        struct base_data *b = base_cache;
        for (b = base_cache;
             base_cache_used > delta_base_cache_limit && b;
             b = b->child) {
-               if (b->data && b != retain) {
-                       free(b->data);
-                       b->data = NULL;
-                       base_cache_used -= b->size;
-               }
+               if (b->data && b != retain)
+                       free_base_data(b);
        }
 }
 
@@ -244,7 +250,8 @@ static void link_base_data(struct base_data *base, struct base_data *c)
 
        c->base = base;
        c->child = NULL;
-       base_cache_used += c->size;
+       if (c->data)
+               base_cache_used += c->size;
        prune_base_data(c);
 }
 
@@ -255,10 +262,7 @@ static void unlink_base_data(struct base_data *c)
                base->child = NULL;
        else
                base_cache = NULL;
-       if (c->data) {
-               free(c->data);
-               base_cache_used -= c->size;
-       }
+       free_base_data(c);
 }
 
 static void *unpack_entry_data(unsigned long offset, unsigned long size)
@@ -334,7 +338,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
                        base_offset = (base_offset << 7) + (c & 127);
                }
                delta_base->offset = obj->idx.offset - base_offset;
-               if (delta_base->offset >= obj->idx.offset)
+               if (delta_base->offset <= 0 || delta_base->offset >= obj->idx.offset)
                        bad_object(obj->idx.offset, "delta base offset is out of bound");
                break;
        case OBJ_COMMIT:
@@ -408,22 +412,24 @@ static int find_delta(const union delta_base *base)
         return -first-1;
 }
 
-static int find_delta_children(const union delta_base *base,
-                              int *first_index, int *last_index)
+static void find_delta_children(const union delta_base *base,
+                               int *first_index, int *last_index)
 {
        int first = find_delta(base);
        int last = first;
        int end = nr_deltas - 1;
 
-       if (first < 0)
-               return -1;
+       if (first < 0) {
+               *first_index = 0;
+               *last_index = -1;
+               return;
+       }
        while (first > 0 && !memcmp(&deltas[first - 1].base, base, UNION_BASE_SZ))
                --first;
        while (last < end && !memcmp(&deltas[last + 1].base, base, UNION_BASE_SZ))
                ++last;
        *first_index = first;
        *last_index = last;
-       return 0;
 }
 
 static void sha1_object(const void *data, unsigned long size,
@@ -494,8 +500,10 @@ static void *get_base_data(struct base_data *c)
                        free(raw);
                        if (!c->data)
                                bad_object(obj->idx.offset, "failed to apply delta");
-               } else
+               } else {
                        c->data = get_data_from_pack(obj);
+                       c->size = obj->size;
+               }
 
                base_cache_used += c->size;
                prune_base_data(c);
@@ -504,49 +512,74 @@ static void *get_base_data(struct base_data *c)
 }
 
 static void resolve_delta(struct object_entry *delta_obj,
-                         struct base_data *base_obj, enum object_type type)
+                         struct base_data *base, struct base_data *result)
 {
-       void *delta_data;
-       unsigned long delta_size;
-       union delta_base delta_base;
-       int j, first, last;
-       struct base_data result;
+       void *base_data, *delta_data;
 
-       delta_obj->real_type = type;
+       delta_obj->real_type = base->obj->real_type;
        delta_data = get_data_from_pack(delta_obj);
-       delta_size = delta_obj->size;
-       result.data = patch_delta(get_base_data(base_obj), base_obj->size,
-                            delta_data, delta_size,
-                            &result.size);
+       base_data = get_base_data(base);
+       result->obj = delta_obj;
+       result->data = patch_delta(base_data, base->size,
+                                  delta_data, delta_obj->size, &result->size);
        free(delta_data);
-       if (!result.data)
+       if (!result->data)
                bad_object(delta_obj->idx.offset, "failed to apply delta");
-       sha1_object(result.data, result.size, type, delta_obj->idx.sha1);
+       sha1_object(result->data, result->size, delta_obj->real_type,
+                   delta_obj->idx.sha1);
        nr_resolved_deltas++;
+}
 
-       result.obj = delta_obj;
-       link_base_data(base_obj, &result);
+static void find_unresolved_deltas(struct base_data *base,
+                                  struct base_data *prev_base)
+{
+       int i, ref_first, ref_last, ofs_first, ofs_last;
+
+       /*
+        * This is a recursive function. Those brackets should help reducing
+        * stack usage by limiting the scope of the delta_base union.
+        */
+       {
+               union delta_base base_spec;
+
+               hashcpy(base_spec.sha1, base->obj->idx.sha1);
+               find_delta_children(&base_spec, &ref_first, &ref_last);
+
+               memset(&base_spec, 0, sizeof(base_spec));
+               base_spec.offset = base->obj->idx.offset;
+               find_delta_children(&base_spec, &ofs_first, &ofs_last);
+       }
+
+       if (ref_last == -1 && ofs_last == -1) {
+               free(base->data);
+               return;
+       }
+
+       link_base_data(prev_base, base);
 
-       hashcpy(delta_base.sha1, delta_obj->idx.sha1);
-       if (!find_delta_children(&delta_base, &first, &last)) {
-               for (j = first; j <= last; j++) {
-                       struct object_entry *child = objects + deltas[j].obj_no;
-                       if (child->real_type == OBJ_REF_DELTA)
-                               resolve_delta(child, &result, type);
+       for (i = ref_first; i <= ref_last; i++) {
+               struct object_entry *child = objects + deltas[i].obj_no;
+               if (child->real_type == OBJ_REF_DELTA) {
+                       struct base_data result;
+                       resolve_delta(child, base, &result);
+                       if (i == ref_last && ofs_last == -1)
+                               free_base_data(base);
+                       find_unresolved_deltas(&result, base);
                }
        }
 
-       memset(&delta_base, 0, sizeof(delta_base));
-       delta_base.offset = delta_obj->idx.offset;
-       if (!find_delta_children(&delta_base, &first, &last)) {
-               for (j = first; j <= last; j++) {
-                       struct object_entry *child = objects + deltas[j].obj_no;
-                       if (child->real_type == OBJ_OFS_DELTA)
-                               resolve_delta(child, &result, type);
+       for (i = ofs_first; i <= ofs_last; i++) {
+               struct object_entry *child = objects + deltas[i].obj_no;
+               if (child->real_type == OBJ_OFS_DELTA) {
+                       struct base_data result;
+                       resolve_delta(child, base, &result);
+                       if (i == ofs_last)
+                               free_base_data(base);
+                       find_unresolved_deltas(&result, base);
                }
        }
 
-       unlink_base_data(&result);
+       unlink_base_data(base);
 }
 
 static int compare_delta_entry(const void *a, const void *b)
@@ -622,37 +655,13 @@ static void parse_pack_objects(unsigned char *sha1)
                progress = start_progress("Resolving deltas", nr_deltas);
        for (i = 0; i < nr_objects; i++) {
                struct object_entry *obj = &objects[i];
-               union delta_base base;
-               int j, ref, ref_first, ref_last, ofs, ofs_first, ofs_last;
                struct base_data base_obj;
 
                if (obj->type == OBJ_REF_DELTA || obj->type == OBJ_OFS_DELTA)
                        continue;
-               hashcpy(base.sha1, obj->idx.sha1);
-               ref = !find_delta_children(&base, &ref_first, &ref_last);
-               memset(&base, 0, sizeof(base));
-               base.offset = obj->idx.offset;
-               ofs = !find_delta_children(&base, &ofs_first, &ofs_last);
-               if (!ref && !ofs)
-                       continue;
-               base_obj.data = get_data_from_pack(obj);
-               base_obj.size = obj->size;
                base_obj.obj = obj;
-               link_base_data(NULL, &base_obj);
-
-               if (ref)
-                       for (j = ref_first; j <= ref_last; j++) {
-                               struct object_entry *child = objects + deltas[j].obj_no;
-                               if (child->real_type == OBJ_REF_DELTA)
-                                       resolve_delta(child, &base_obj, obj->type);
-                       }
-               if (ofs)
-                       for (j = ofs_first; j <= ofs_last; j++) {
-                               struct object_entry *child = objects + deltas[j].obj_no;
-                               if (child->real_type == OBJ_OFS_DELTA)
-                                       resolve_delta(child, &base_obj, obj->type);
-                       }
-               unlink_base_data(&base_obj);
+               base_obj.data = NULL;
+               find_unresolved_deltas(&base_obj, NULL);
                display_progress(progress, nr_resolved_deltas);
        }
 }
@@ -745,7 +754,6 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
        for (i = 0; i < n; i++) {
                struct delta_entry *d = sorted_by_pos[i];
                enum object_type type;
-               int j, first, last;
                struct base_data base_obj;
 
                if (objects[d->obj_no].real_type != OBJ_REF_DELTA)
@@ -759,16 +767,7 @@ static void fix_unresolved_deltas(struct sha1file *f, int nr_unresolved)
                        die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
                base_obj.obj = append_obj_to_pack(f, d->base.sha1,
                                        base_obj.data, base_obj.size, type);
-               link_base_data(NULL, &base_obj);
-
-               find_delta_children(&d->base, &first, &last);
-               for (j = first; j <= last; j++) {
-                       struct object_entry *child = objects + deltas[j].obj_no;
-                       if (child->real_type == OBJ_REF_DELTA)
-                               resolve_delta(child, &base_obj, type);
-               }
-
-               unlink_base_data(&base_obj);
+               find_unresolved_deltas(&base_obj, NULL);
                display_progress(progress, nr_resolved_deltas);
        }
        free(sorted_by_pos);
@@ -790,7 +789,6 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                err = close(output_fd);
                if (err)
                        die("error while closing pack file: %s", strerror(errno));
-               chmod(curr_pack_name, 0444);
        }
 
        if (keep_msg) {
@@ -824,8 +822,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                if (move_temp_to_file(curr_pack_name, final_pack_name))
                        die("cannot store pack file");
        }
+       if (from_stdin)
+               chmod(final_pack_name, 0444);
 
-       chmod(curr_index_name, 0444);
        if (final_index_name != curr_index_name) {
                if (!final_index_name) {
                        snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
@@ -835,6 +834,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                if (move_temp_to_file(curr_index_name, final_index_name))
                        die("cannot store index file");
        }
+       chmod(final_index_name, 0444);
 
        if (!from_stdin) {
                printf("%s\n", sha1_to_hex(sha1));
@@ -879,10 +879,26 @@ int main(int argc, char **argv)
        char *index_name_buf = NULL, *keep_name_buf = NULL;
        struct pack_idx_entry **idx_objects;
        unsigned char pack_sha1[20];
-       int nongit = 0;
 
-       setup_git_directory_gently(&nongit);
-       git_config(git_index_pack_config, NULL);
+       /*
+        * We wish to read the repository's config file if any, and
+        * for that it is necessary to call setup_git_directory_gently().
+        * However if the cwd was inside .git/objects/pack/ then we need
+        * to go back there or all the pack name arguments will be wrong.
+        * And in that case we cannot rely on any prefix returned by
+        * setup_git_directory_gently() either.
+        */
+       {
+               char cwd[PATH_MAX+1];
+               int nongit;
+
+               if (!getcwd(cwd, sizeof(cwd)-1))
+                       die("Unable to get current working directory");
+               setup_git_directory_gently(&nongit);
+               git_config(git_index_pack_config, NULL);
+               if (chdir(cwd))
+                       die("Cannot come back to cwd");
+       }
 
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
index 4023797b00fe21ecbabe3407ef8a12fca0690607..6d756086939b631ab13bf4fcdb8deed2787eed6b 100644 (file)
@@ -121,15 +121,17 @@ static char *resolve_symlink(char *p, size_t s)
 }
 
 
-static int lock_file(struct lock_file *lk, const char *path)
+static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       if (strlen(path) >= sizeof(lk->filename)) return -1;
+       if (strlen(path) >= sizeof(lk->filename))
+               return -1;
        strcpy(lk->filename, path);
        /*
         * subtract 5 from size to make sure there's room for adding
         * ".lock" for the lock file name
         */
-       resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+       if (!(flags & LOCK_NODEREF))
+               resolve_symlink(lk->filename, sizeof(lk->filename)-5);
        strcat(lk->filename, ".lock");
        lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
        if (0 <= lk->fd) {
@@ -155,21 +157,21 @@ static int lock_file(struct lock_file *lk, const char *path)
        return lk->fd;
 }
 
-int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error)
+int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
 {
-       int fd = lock_file(lk, path);
-       if (fd < 0 && die_on_error)
+       int fd = lock_file(lk, path, flags);
+       if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
                die("unable to create '%s.lock': %s", path, strerror(errno));
        return fd;
 }
 
-int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on_error)
+int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
 {
        int fd, orig_fd;
 
-       fd = lock_file(lk, path);
+       fd = lock_file(lk, path, flags);
        if (fd < 0) {
-               if (die_on_error)
+               if (flags & LOCK_DIE_ON_ERROR)
                        die("unable to create '%s.lock': %s", path, strerror(errno));
                return fd;
        }
@@ -177,13 +179,13 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int die_on
        orig_fd = open(path, O_RDONLY);
        if (orig_fd < 0) {
                if (errno != ENOENT) {
-                       if (die_on_error)
+                       if (flags & LOCK_DIE_ON_ERROR)
                                die("cannot open '%s' for copying", path);
                        close(fd);
                        return error("cannot open '%s' for copying", path);
                }
        } else if (copy_fd(orig_fd, fd)) {
-               if (die_on_error)
+               if (flags & LOCK_DIE_ON_ERROR)
                        exit(128);
                close(fd);
                return -1;
@@ -215,7 +217,10 @@ int commit_lock_file(struct lock_file *lk)
 
 int hold_locked_index(struct lock_file *lk, int die_on_error)
 {
-       return hold_lock_file_for_update(lk, get_index_file(), die_on_error);
+       return hold_lock_file_for_update(lk, get_index_file(),
+                                        die_on_error
+                                        ? LOCK_DIE_ON_ERROR
+                                        : 0);
 }
 
 void set_alternate_index_output(const char *name)
index cec3c061360e9d33068fdb6c2555b69cc11f6771..5444f0860b2150d440dfbd5a4be4aa2daa1316fc 100644 (file)
@@ -52,11 +52,15 @@ static void show_parents(struct commit *commit, int abbrev)
        }
 }
 
-void show_decorations(struct commit *commit)
+void show_decorations(struct rev_info *opt, struct commit *commit)
 {
        const char *prefix;
        struct name_decoration *decoration;
 
+       if (opt->show_source && commit->util)
+               printf(" %s", (char *) commit->util);
+       if (!opt->show_decorations)
+               return;
        decoration = lookup_decoration(&name_decoration, &commit->object);
        if (!decoration)
                return;
@@ -279,7 +283,7 @@ void show_log(struct rev_info *opt)
                fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
-               show_decorations(commit);
+               show_decorations(opt, commit);
                if (opt->graph && !graph_is_commit_finished(opt->graph)) {
                        putchar('\n');
                        graph_show_remainder(opt->graph);
@@ -352,7 +356,7 @@ void show_log(struct rev_info *opt)
                        printf(" (from %s)",
                               diff_unique_abbrev(parent->object.sha1,
                                                  abbrev_commit));
-               show_decorations(commit);
+               show_decorations(opt, commit);
                printf("%s", diff_get_color_opt(&opt->diffopt, DIFF_RESET));
                if (opt->commit_format == CMIT_FMT_ONELINE) {
                        putchar(' ');
index 3c8127bb7cc8d27ff1e91ad6c159bc718fb7d9d0..f2a90084ae1874632318c7880e95426eca2682ea 100644 (file)
@@ -12,7 +12,7 @@ int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
 int log_tree_opt_parse(struct rev_info *, const char **, int);
 void show_log(struct rev_info *opt);
-void show_decorations(struct commit *commit);
+void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, const char *name,
                             const char **subject_p,
                             const char **extra_headers_p,
index 2a939c9dd835a7e7946eb1548e4cf637ae3ca329..3120a95f786eadd4cb5d167facc15714904e9665 100644 (file)
@@ -61,6 +61,7 @@ static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
        xdemitconf_t xecfg;
        xdemitcb_t ecb;
 
+       memset(&xpp, 0, sizeof(xpp));
        xpp.flags = XDF_NEED_MINIMAL;
        memset(&xecfg, 0, sizeof(xecfg));
        xecfg.ctxlen = 3;
index 245232a4083bbd8cdadf3b32a0a20db559941f8a..7472d3ecc9b8412a697a64c1307259a104e0abf9 100644 (file)
@@ -1332,7 +1332,7 @@ static int merge_recursive_config(const char *var, const char *value, void *cb)
                o->merge_rename_limit = git_config_int(var, value);
                return 0;
        }
-       return git_default_config(var, value, cb);
+       return git_xmerge_config(var, value, cb);
 }
 
 void init_merge_options(struct merge_options *o)
index 848d311c2b2c651dbb14893c260584f00c639357..2c76fb181f64e10c65517224b0c09cb648db81ac 100644 (file)
@@ -89,7 +89,8 @@ int pack_refs(unsigned int flags)
        memset(&cbdata, 0, sizeof(cbdata));
        cbdata.flags = flags;
 
-       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"), 1);
+       fd = hold_lock_file_for_update(&packed, git_path("packed-refs"),
+                                      LOCK_DIE_ON_ERROR);
        cbdata.refs_file = fdopen(fd, "w");
        if (!cbdata.refs_file)
                die("unable to create ref-pack file structure (%s)",
index 6096b6224ad551086afa346617eb714c15a51f78..1de53c8934c03b46604e94cbf4237ad5ffc57f83 100644 (file)
@@ -140,7 +140,8 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs)
                else
                        lo = mi + 1;
        } while (lo < hi);
-       die("internal error: pack revindex corrupt");
+       error("bad offset for revindex");
+       return NULL;
 }
 
 void discard_revindex(void)
diff --git a/path.c b/path.c
index 76e8872622e435b050f77198ef6eef6e6ff6869e..a074aea64921eb1fb90f079ede9087e6b8109f6a 100644 (file)
--- a/path.c
+++ b/path.c
@@ -32,6 +32,60 @@ static char *cleanup_path(char *path)
        return path;
 }
 
+char *mksnpath(char *buf, size_t n, const char *fmt, ...)
+{
+       va_list args;
+       unsigned len;
+
+       va_start(args, fmt);
+       len = vsnprintf(buf, n, fmt, args);
+       va_end(args);
+       if (len >= n) {
+               strlcpy(buf, bad_path, n);
+               return buf;
+       }
+       return cleanup_path(buf);
+}
+
+static char *git_vsnpath(char *buf, size_t n, const char *fmt, va_list args)
+{
+       const char *git_dir = get_git_dir();
+       size_t len;
+
+       len = strlen(git_dir);
+       if (n < len + 1)
+               goto bad;
+       memcpy(buf, git_dir, len);
+       if (len && !is_dir_sep(git_dir[len-1]))
+               buf[len++] = '/';
+       len += vsnprintf(buf + len, n - len, fmt, args);
+       if (len >= n)
+               goto bad;
+       return cleanup_path(buf);
+bad:
+       strlcpy(buf, bad_path, n);
+       return buf;
+}
+
+char *git_snpath(char *buf, size_t n, const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       (void)git_vsnpath(buf, n, fmt, args);
+       va_end(args);
+       return buf;
+}
+
+char *git_pathdup(const char *fmt, ...)
+{
+       char path[PATH_MAX];
+       va_list args;
+       va_start(args, fmt);
+       (void)git_vsnpath(path, sizeof(path), fmt, args);
+       va_end(args);
+       return xstrdup(path);
+}
+
 char *mkpath(const char *fmt, ...)
 {
        va_list args;
@@ -348,7 +402,7 @@ int normalize_absolute_path(char *buf, const char *path)
                        goto next;
                }
 
-               memcpy(dst, comp_start, comp_len);
+               memmove(dst, comp_start, comp_len);
                dst += comp_len;
        next:
                comp_start = comp_end;
index 6aab712e6ac6513b0b46958d93d536ef975276b4..ba94453781c98a97a95c65999873fc28b013340d 100644 (file)
@@ -1203,8 +1203,7 @@ =head1 COPYRIGHT
 # the method was called upon an instance and (undef, @args) if
 # it was called directly.
 sub _maybe_self {
-       # This breaks inheritance. Oh well.
-       ref $_[0] eq 'Git' ? @_ : (undef, @_);
+       UNIVERSAL::isa($_[0], 'Git') ? @_ : (undef, @_);
 }
 
 # Check if the command id is something reasonable.
index 1e799433395afa935b5ac1454c299ae873873893..f6ff31264b6908bac8bf71678e2eaf2e0cefc100 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -783,6 +783,20 @@ void pp_remainder(enum cmit_fmt fmt,
        }
 }
 
+char *reencode_commit_message(const struct commit *commit, const char **encoding_p)
+{
+       const char *encoding;
+
+       encoding = (git_log_output_encoding
+                   ? git_log_output_encoding
+                   : git_commit_encoding);
+       if (!encoding)
+               encoding = "utf-8";
+       if (encoding_p)
+               *encoding_p = encoding;
+       return logmsg_reencode(commit, encoding);
+}
+
 void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                         struct strbuf *sb, int abbrev,
                         const char *subject, const char *after_subject,
@@ -799,12 +813,7 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit,
                return;
        }
 
-       encoding = (git_log_output_encoding
-                   ? git_log_output_encoding
-                   : git_commit_encoding);
-       if (!encoding)
-               encoding = "utf-8";
-       reencoded = logmsg_reencode(commit, encoding);
+       reencoded = reencode_commit_message(commit, &encoding);
        if (reencoded) {
                msg = reencoded;
        }
index c229fd4d0da68b204e81e1e8127f8529abe06336..22a814311d2cfc032129b43f6da56706c7026b9c 100644 (file)
@@ -160,7 +160,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
-static int is_empty_blob_sha1(const unsigned char *sha1)
+int is_empty_blob_sha1(const unsigned char *sha1)
 {
        static const unsigned char empty_blob_sha1[20] = {
                0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
@@ -1269,6 +1269,11 @@ int read_index_from(struct index_state *istate, const char *path)
        die("index file corrupt");
 }
 
+int is_index_unborn(struct index_state *istate)
+{
+       return (!istate->cache_nr && !istate->alloc && !istate->timestamp);
+}
+
 int discard_index(struct index_state *istate)
 {
        istate->cache_nr = 0;
@@ -1489,25 +1494,30 @@ int write_index(const struct index_state *istate, int newfd)
 int read_index_unmerged(struct index_state *istate)
 {
        int i;
-       struct cache_entry **dst;
-       struct cache_entry *last = NULL;
+       int unmerged = 0;
 
        read_index(istate);
-       dst = istate->cache;
        for (i = 0; i < istate->cache_nr; i++) {
                struct cache_entry *ce = istate->cache[i];
-               if (ce_stage(ce)) {
-                       remove_name_hash(ce);
-                       if (last && !strcmp(ce->name, last->name))
-                               continue;
-                       cache_tree_invalidate_path(istate->cache_tree, ce->name);
-                       last = ce;
+               struct cache_entry *new_ce;
+               int size, len;
+
+               if (!ce_stage(ce))
                        continue;
-               }
-               *dst++ = ce;
+               unmerged = 1;
+               len = strlen(ce->name);
+               size = cache_entry_size(len);
+               new_ce = xcalloc(1, size);
+               hashcpy(new_ce->sha1, ce->sha1);
+               memcpy(new_ce->name, ce->name, len);
+               new_ce->ce_flags = create_ce_flags(len, 0);
+               new_ce->ce_mode = ce->ce_mode;
+               if (add_index_entry(istate, new_ce, 0))
+                       return error("%s: cannot drop to stage #0",
+                                    ce->name);
+               i = index_name_pos(istate, new_ce->name, len);
        }
-       istate->cache_nr = dst - istate->cache;
-       return !!last;
+       return unmerged;
 }
 
 struct update_callback_data
@@ -1565,3 +1575,30 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
        return !!data.add_errors;
 }
 
+/*
+ * Returns 1 if the path is an "other" path with respect to
+ * the index; that is, the path is not mentioned in the index at all,
+ * either as a file, a directory with some files in the index,
+ * or as an unmerged entry.
+ *
+ * We helpfully remove a trailing "/" from directories so that
+ * the output of read_directory can be used as-is.
+ */
+int index_name_is_other(const struct index_state *istate, const char *name,
+               int namelen)
+{
+       int pos;
+       if (namelen && name[namelen - 1] == '/')
+               namelen--;
+       pos = index_name_pos(istate, name, namelen);
+       if (0 <= pos)
+               return 0;       /* exact match */
+       pos = -pos - 1;
+       if (pos < istate->cache_nr) {
+               struct cache_entry *ce = istate->cache[pos];
+               if (ce_namelen(ce) == namelen &&
+                   !memcmp(ce->name, name, namelen))
+                       return 0; /* Yup, this one exists unmerged */
+       }
+       return 1;
+}
diff --git a/refs.c b/refs.c
index b6807505e243fd26cae50460bc003254406ae7e5..33ced65a7801f8653d608a9580e237ce8df39ae2 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -413,7 +413,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                *flag = 0;
 
        for (;;) {
-               const char *path = git_path("%s", ref);
+               char path[PATH_MAX];
                struct stat st;
                char *buf;
                int fd;
@@ -421,6 +421,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                if (--depth < 0)
                        return NULL;
 
+               git_snpath(path, sizeof(path), "%s", ref);
                /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
                        struct ref_list *list = get_packed_refs();
@@ -794,10 +795,10 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
        char *ref_file;
        const char *orig_ref = ref;
        struct ref_lock *lock;
-       struct stat st;
        int last_errno = 0;
-       int type;
+       int type, lflags;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
+       int missing = 0;
 
        lock = xcalloc(1, sizeof(struct ref_lock));
        lock->lock_fd = -1;
@@ -825,23 +826,27 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
                        orig_ref, strerror(errno));
                goto error_return;
        }
+       missing = is_null_sha1(lock->old_sha1);
        /* When the ref did not exist and we are creating it,
         * make sure there is no existing ref that is packed
         * whose name begins with our refname, nor a ref whose
         * name is a proper prefix of our refname.
         */
-       if (is_null_sha1(lock->old_sha1) &&
+       if (missing &&
             !is_refname_available(ref, NULL, get_packed_refs(), 0))
                goto error_return;
 
        lock->lk = xcalloc(1, sizeof(struct lock_file));
 
-       if (flags & REF_NODEREF)
+       lflags = LOCK_DIE_ON_ERROR;
+       if (flags & REF_NODEREF) {
                ref = orig_ref;
+               lflags |= LOCK_NODEREF;
+       }
        lock->ref_name = xstrdup(ref);
        lock->orig_ref_name = xstrdup(orig_ref);
        ref_file = git_path("%s", ref);
-       if (lstat(ref_file, &st) && errno == ENOENT)
+       if (missing)
                lock->force_write = 1;
        if ((flags & REF_NODEREF) && (type & REF_ISSYMREF))
                lock->force_write = 1;
@@ -851,8 +856,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
                error("unable to create directory for %s", ref_file);
                goto error_return;
        }
-       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);
 
+       lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
  error_return:
@@ -918,25 +923,33 @@ static int repack_without_ref(const char *refname)
        return commit_lock_file(&packlock);
 }
 
-int delete_ref(const char *refname, const unsigned char *sha1)
+int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
 {
        struct ref_lock *lock;
-       int err, i, ret = 0, flag = 0;
+       int err, i = 0, ret = 0, flag = 0;
 
        lock = lock_ref_sha1_basic(refname, sha1, 0, &flag);
        if (!lock)
                return 1;
-       if (!(flag & REF_ISPACKED)) {
+       if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
                /* loose */
-               i = strlen(lock->lk->filename) - 5; /* .lock */
-               lock->lk->filename[i] = 0;
-               err = unlink(lock->lk->filename);
+               const char *path;
+
+               if (!(delopt & REF_NODEREF)) {
+                       i = strlen(lock->lk->filename) - 5; /* .lock */
+                       lock->lk->filename[i] = 0;
+                       path = lock->lk->filename;
+               } else {
+                       path = git_path("%s", refname);
+               }
+               err = unlink(path);
                if (err && errno != ENOENT) {
                        ret = 1;
                        error("unlink(%s) failed: %s",
-                             lock->lk->filename, strerror(errno));
+                             path, strerror(errno));
                }
-               lock->lk->filename[i] = '.';
+               if (!(delopt & REF_NODEREF))
+                       lock->lk->filename[i] = '.';
        }
        /* removing the loose one could have resurrected an earlier
         * packed one.  Also, if it was not loose we need to repack
@@ -961,11 +974,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        struct ref_lock *lock;
        struct stat loginfo;
        int log = !lstat(git_path("logs/%s", oldref), &loginfo);
+       const char *symref = NULL;
 
-       if (S_ISLNK(loginfo.st_mode))
+       if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldref);
 
-       if (!resolve_ref(oldref, orig_sha1, 1, &flag))
+       symref = resolve_ref(oldref, orig_sha1, 1, &flag);
+       if (flag & REF_ISSYMREF)
+               return error("refname %s is a symbolic ref, renaming it is not supported",
+                       oldref);
+       if (!symref)
                return error("refname %s not found", oldref);
 
        if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
@@ -985,12 +1003,12 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
                        oldref, strerror(errno));
 
-       if (delete_ref(oldref, orig_sha1)) {
+       if (delete_ref(oldref, orig_sha1, REF_NODEREF)) {
                error("unable to delete old %s", oldref);
                goto rollback;
        }
 
-       if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) {
+       if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1, REF_NODEREF)) {
                if (errno==EISDIR) {
                        if (remove_empty_directories(git_path("%s", newref))) {
                                error("Directory not empty: %s", newref);
@@ -1033,7 +1051,6 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
                error("unable to lock %s for update", newref);
                goto rollback;
        }
-
        lock->force_write = 1;
        hashcpy(lock->old_sha1, orig_sha1);
        if (write_ref_sha1(lock, orig_sha1, logmsg)) {
@@ -1127,13 +1144,14 @@ static int log_ref_write(const char *ref_name, const unsigned char *old_sha1,
        int logfd, written, oflags = O_APPEND | O_WRONLY;
        unsigned maxlen, len;
        int msglen;
-       char *log_file, *logrec;
+       char log_file[PATH_MAX];
+       char *logrec;
        const char *committer;
 
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
 
-       log_file = git_path("logs/%s", ref_name);
+       git_snpath(log_file, sizeof(log_file), "logs/%s", ref_name);
 
        if (log_all_ref_updates &&
            (!prefixcmp(ref_name, "refs/heads/") ||
@@ -1262,7 +1280,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
        const char *lockpath;
        char ref[1000];
        int fd, len, written;
-       char *git_HEAD = xstrdup(git_path("%s", ref_target));
+       char *git_HEAD = git_pathdup("%s", ref_target);
        unsigned char old_sha1[20], new_sha1[20];
 
        if (logmsg && read_ref(ref_target, old_sha1))
index d5efadd93d63b2c39176763eb325ecddb6c1215a..570e11286ea295e825a23b1d5ed40c9fbd02be57 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -201,6 +201,7 @@ static void read_remotes_file(struct remote *remote)
 
        if (!f)
                return;
+       remote->origin = REMOTE_REMOTES;
        while (fgets(buffer, BUF_SIZE, f)) {
                int value_list;
                char *s, *p;
@@ -261,6 +262,7 @@ static void read_branches_file(struct remote *remote)
                s++;
        if (!*s)
                return;
+       remote->origin = REMOTE_BRANCHES;
        p = s + strlen(s);
        while (isspace(p[-1]))
                *--p = 0;
@@ -297,6 +299,17 @@ static void read_branches_file(struct remote *remote)
        }
        add_url_alias(remote, p);
        add_fetch_refspec(remote, strbuf_detach(&branch, 0));
+       /*
+        * Cogito compatible push: push current HEAD to remote #branch
+        * (master if missing)
+        */
+       strbuf_init(&branch, 0);
+       strbuf_addstr(&branch, "HEAD");
+       if (frag)
+               strbuf_addf(&branch, ":refs/heads/%s", frag);
+       else
+               strbuf_addstr(&branch, ":refs/heads/master");
+       add_push_refspec(remote, strbuf_detach(&branch, 0));
        remote->fetch_tags = 1; /* always auto-follow */
 }
 
@@ -341,14 +354,16 @@ static int handle_config(const char *key, const char *value, void *cb)
        if (prefixcmp(key,  "remote."))
                return 0;
        name = key + 7;
+       if (*name == '/') {
+               warning("Config remote shorthand cannot begin with '/': %s",
+                       name);
+               return 0;
+       }
        subkey = strrchr(name, '.');
        if (!subkey)
                return error("Config with no key for remote %s", name);
-       if (*subkey == '/') {
-               warning("Config remote shorthand cannot begin with '/': %s", name);
-               return 0;
-       }
        remote = make_remote(name, subkey - name);
+       remote->origin = REMOTE_CONFIG;
        if (!strcmp(subkey, ".mirror"))
                remote->mirror = git_config_bool(key, value);
        else if (!strcmp(subkey, ".skipdefaultupdate"))
@@ -748,17 +763,19 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec)
        return -1;
 }
 
-struct ref *alloc_ref(unsigned namelen)
+static struct ref *alloc_ref_with_prefix(const char *prefix, size_t prefixlen,
+               const char *name)
 {
-       struct ref *ret = xcalloc(1, sizeof(struct ref) + namelen);
-       return ret;
+       size_t len = strlen(name);
+       struct ref *ref = xcalloc(1, sizeof(struct ref) + prefixlen + len + 1);
+       memcpy(ref->name, prefix, prefixlen);
+       memcpy(ref->name + prefixlen, name, len);
+       return ref;
 }
 
-struct ref *alloc_ref_from_str(const char* str)
+struct ref *alloc_ref(const char *name)
 {
-       struct ref *ret = alloc_ref(strlen(str) + 1);
-       strcpy(ret->name, str);
-       return ret;
+       return alloc_ref_with_prefix("", 0, name);
 }
 
 static struct ref *copy_ref(const struct ref *ref)
@@ -869,21 +886,20 @@ static struct ref *try_explicit_object_name(const char *name)
        struct ref *ref;
 
        if (!*name) {
-               ref = alloc_ref(20);
-               strcpy(ref->name, "(delete)");
+               ref = alloc_ref("(delete)");
                hashclr(ref->new_sha1);
                return ref;
        }
        if (get_sha1(name, sha1))
                return NULL;
-       ref = alloc_ref_from_str(name);
+       ref = alloc_ref(name);
        hashcpy(ref->new_sha1, sha1);
        return ref;
 }
 
 static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 {
-       struct ref *ret = alloc_ref_from_str(name);
+       struct ref *ret = alloc_ref(name);
        tail_link_ref(ret, tail);
        return ret;
 }
@@ -1151,10 +1167,8 @@ static struct ref *get_expanded_map(const struct ref *remote_refs,
                        struct ref *cpy = copy_ref(ref);
                        match = ref->name + remote_prefix_len;
 
-                       cpy->peer_ref = alloc_ref(local_prefix_len +
-                                                 strlen(match) + 1);
-                       sprintf(cpy->peer_ref->name, "%s%s",
-                               refspec->dst, match);
+                       cpy->peer_ref = alloc_ref_with_prefix(refspec->dst,
+                                       local_prefix_len, match);
                        if (refspec->force)
                                cpy->peer_ref->force = 1;
                        *tail = cpy;
@@ -1187,25 +1201,18 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 
 static struct ref *get_local_ref(const char *name)
 {
-       struct ref *ret;
        if (!name)
                return NULL;
 
-       if (!prefixcmp(name, "refs/")) {
-               return alloc_ref_from_str(name);
-       }
+       if (!prefixcmp(name, "refs/"))
+               return alloc_ref(name);
 
        if (!prefixcmp(name, "heads/") ||
            !prefixcmp(name, "tags/") ||
-           !prefixcmp(name, "remotes/")) {
-               ret = alloc_ref(strlen(name) + 6);
-               sprintf(ret->name, "refs/%s", name);
-               return ret;
-       }
+           !prefixcmp(name, "remotes/"))
+               return alloc_ref_with_prefix("refs/", 5, name);
 
-       ret = alloc_ref(strlen(name) + 12);
-       sprintf(ret->name, "refs/heads/%s", name);
-       return ret;
+       return alloc_ref_with_prefix("refs/heads/", 11, name);
 }
 
 int get_fetch_map(const struct ref *remote_refs,
index c6163ff5b1f7e7c96bedaa9a6b561a9f4a8ed7c2..a46a5be131caf1d1d71f97cab3c3ba2cebb6386c 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -1,8 +1,15 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
+enum {
+       REMOTE_CONFIG,
+       REMOTE_REMOTES,
+       REMOTE_BRANCHES
+};
+
 struct remote {
        const char *name;
+       int origin;
 
        const char **url;
        int url_nr;
@@ -55,9 +62,7 @@ struct refspec {
 
 extern const struct refspec *tag_refspec;
 
-struct ref *alloc_ref(unsigned namelen);
-
-struct ref *alloc_ref_from_str(const char* str);
+struct ref *alloc_ref(const char *name);
 
 struct ref *copy_ref_list(const struct ref *ref);
 
index 5bb5316cdae16154fd4aaffe996710c0bcfc0b94..02931a151f1abe183763c9a1edb65ffd36d83bc3 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -351,8 +351,9 @@ int setup_rerere(struct string_list *merge_rr)
        if (!is_rerere_enabled())
                return -1;
 
-       merge_rr_path = xstrdup(git_path("MERGE_RR"));
-       fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
+       merge_rr_path = git_pathdup("MERGE_RR");
+       fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
+                                      LOCK_DIE_ON_ERROR);
        read_rr(merge_rr);
        return fd;
 }
index 2f646deab09c423143185b7f7928ae46ab9f4c97..9dc55d4003301c0ededbd6059e16cabf5684766b 100644 (file)
@@ -11,6 +11,7 @@
 #include "reflog-walk.h"
 #include "patch-ids.h"
 #include "decorate.h"
+#include "log-tree.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -199,6 +200,8 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
                        mark_parents_uninteresting(commit);
                        revs->limited = 1;
                }
+               if (revs->show_source && !commit->util)
+                       commit->util = (void *) name;
                return commit;
        }
 
@@ -292,10 +295,31 @@ static void file_change(struct diff_options *options,
        DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
-static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
+static int rev_compare_tree(struct rev_info *revs, struct commit *parent, struct commit *commit)
 {
+       struct tree *t1 = parent->tree;
+       struct tree *t2 = commit->tree;
+
        if (!t1)
                return REV_TREE_NEW;
+
+       if (revs->simplify_by_decoration) {
+               /*
+                * If we are simplifying by decoration, then the commit
+                * is worth showing if it has a tag pointing at it.
+                */
+               if (lookup_decoration(&name_decoration, &commit->object))
+                       return REV_TREE_DIFFERENT;
+               /*
+                * A commit that is not pointed by a tag is uninteresting
+                * if we are not limited by path.  This means that you will
+                * see the usual "commits that touch the paths" plus any
+                * tagged commit by specifying both --simplify-by-decoration
+                * and pathspec.
+                */
+               if (!revs->prune_data)
+                       return REV_TREE_SAME;
+       }
        if (!t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
@@ -306,12 +330,13 @@ static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree
        return tree_difference;
 }
 
-static int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
+static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 {
        int retval;
        void *tree;
        unsigned long size;
        struct tree_desc empty, real;
+       struct tree *t1 = commit->tree;
 
        if (!t1)
                return 0;
@@ -345,7 +370,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                return;
 
        if (!commit->parents) {
-               if (rev_same_tree_as_empty(revs, commit->tree))
+               if (rev_same_tree_as_empty(revs, commit))
                        commit->object.flags |= TREESAME;
                return;
        }
@@ -365,7 +390,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                        die("cannot simplify commit %s (because of %s)",
                            sha1_to_hex(commit->object.sha1),
                            sha1_to_hex(p->object.sha1));
-               switch (rev_compare_tree(revs, p->tree, commit->tree)) {
+               switch (rev_compare_tree(revs, p, commit)) {
                case REV_TREE_SAME:
                        tree_same = 1;
                        if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
@@ -385,7 +410,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
 
                case REV_TREE_NEW:
                        if (revs->remove_empty_trees &&
-                           rev_same_tree_as_empty(revs, p->tree)) {
+                           rev_same_tree_as_empty(revs, p)) {
                                /* We are adding all the specified
                                 * paths from this parent, so the
                                 * history beyond this parent is not
@@ -484,6 +509,8 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
 
                if (parse_commit(p) < 0)
                        return -1;
+               if (revs->show_source && !p->util)
+                       p->util = commit->util;
                p->object.flags |= left_flag;
                if (!(p->object.flags & SEEN)) {
                        p->object.flags |= SEEN;
@@ -1033,6 +1060,14 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->rewrite_parents = 1;
                revs->simplify_history = 0;
                revs->limited = 1;
+       } else if (!strcmp(arg, "--simplify-by-decoration")) {
+               revs->simplify_merges = 1;
+               revs->rewrite_parents = 1;
+               revs->simplify_history = 0;
+               revs->simplify_by_decoration = 1;
+               revs->limited = 1;
+               revs->prune = 1;
+               load_ref_decorations();
        } else if (!strcmp(arg, "--date-order")) {
                revs->lifo = 0;
                revs->topo_order = 1;
index 2fdb2dd0ff3425b68b47aa8fd11155aa881d4a4a..7cf848771b5be811f7741ce988b860760202f6f3 100644 (file)
@@ -43,6 +43,7 @@ struct rev_info {
                        lifo:1,
                        topo_order:1,
                        simplify_merges:1,
+                       simplify_by_decoration:1,
                        tag_objects:1,
                        tree_objects:1,
                        blob_objects:1,
@@ -53,6 +54,8 @@ struct rev_info {
                        left_right:1,
                        rewrite_parents:1,
                        print_parents:1,
+                       show_source:1,
+                       show_decorations:1,
                        reverse:1,
                        reverse_output_stage:1,
                        cherry_pick:1,
index c1c073b2f05a48772a45602cdc711eef6e211695..66b0d9d878a011393582b837301eb1fd5caf2e40 100644 (file)
@@ -25,7 +25,7 @@ static int add_info_ref(const char *path, const unsigned char *sha1, int flag, v
 
 static int update_info_refs(int force)
 {
-       char *path0 = xstrdup(git_path("info/refs"));
+       char *path0 = git_pathdup("info/refs");
        int len = strlen(path0);
        char *path1 = xmalloc(len + 2);
 
index 3fbb0820a0e01faa1d9786d5fbeb8b4d1b6e591d..0fa65baa59de4db058ca6a41bba29ac78eab2643 100644 (file)
@@ -388,7 +388,7 @@ static void read_info_alternates(const char * relative_base, int depth)
 void add_to_alternates_file(const char *reference)
 {
        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
-       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), 1);
+       int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
        char *alt = mkpath("%s/objects\n", reference);
        write_or_die(fd, alt, strlen(alt));
        if (commit_lock_file(lock))
@@ -423,23 +423,30 @@ void prepare_alt_odb(void)
        read_info_alternates(get_object_directory(), 0);
 }
 
-static int has_loose_object(const unsigned char *sha1)
+static int has_loose_object_local(const unsigned char *sha1)
 {
        char *name = sha1_file_name(sha1);
-       struct alternate_object_database *alt;
+       return !access(name, F_OK);
+}
 
-       if (!access(name, F_OK))
-               return 1;
+int has_loose_object_nonlocal(const unsigned char *sha1)
+{
+       struct alternate_object_database *alt;
        prepare_alt_odb();
        for (alt = alt_odb_list; alt; alt = alt->next) {
-               name = alt->name;
-               fill_sha1_path(name, sha1);
+               fill_sha1_path(alt->name, sha1);
                if (!access(alt->base, F_OK))
                        return 1;
        }
        return 0;
 }
 
+static int has_loose_object(const unsigned char *sha1)
+{
+       return has_loose_object_local(sha1) ||
+              has_loose_object_nonlocal(sha1);
+}
+
 static unsigned int pack_used_ctr;
 static unsigned int pack_mmap_calls;
 static unsigned int peak_pack_open_windows;
@@ -841,6 +848,11 @@ struct packed_git *add_packed_git(const char *path, int path_len, int local)
                return NULL;
        }
        memcpy(p->pack_name, path, path_len);
+
+       strcpy(p->pack_name + path_len, ".keep");
+       if (!access(p->pack_name, F_OK))
+               p->pack_keep = 1;
+
        strcpy(p->pack_name + path_len, ".pack");
        if (stat(p->pack_name, &st) || !S_ISREG(st.st_mode)) {
                free(p);
@@ -1110,7 +1122,8 @@ static int legacy_loose_object(unsigned char *map)
                return 0;
 }
 
-unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep)
+unsigned long unpack_object_header_buffer(const unsigned char *buf,
+               unsigned long len, enum object_type *type, unsigned long *sizep)
 {
        unsigned shift;
        unsigned char c;
@@ -1122,10 +1135,10 @@ unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned lon
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
-               if (len <= used)
-                       return 0;
-               if (sizeof(long) * 8 <= shift)
+               if (len <= used || sizeof(long) * 8 <= shift) {
+                       error("bad object header");
                        return 0;
+               }
                c = buf[used++];
                size += (c & 0x7f) << shift;
                shift += 7;
@@ -1164,7 +1177,7 @@ static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned lon
         * really worth it and we don't write it any longer.  But we
         * can still read it.
         */
-       used = unpack_object_header_gently(map, mapsize, &type, &size);
+       used = unpack_object_header_buffer(map, mapsize, &type, &size);
        if (!used || !valid_loose_object_type[type])
                return -1;
        map += used;
@@ -1313,8 +1326,10 @@ unsigned long get_size_from_delta(struct packed_git *p,
        } while ((st == Z_OK || st == Z_BUF_ERROR) &&
                 stream.total_out < sizeof(delta_head));
        inflateEnd(&stream);
-       if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head))
-               die("delta data unpack-initial failed");
+       if ((st != Z_STREAM_END) && stream.total_out != sizeof(delta_head)) {
+               error("delta data unpack-initial failed");
+               return 0;
+       }
 
        /* Examine the initial part of the delta to figure out
         * the result size.
@@ -1355,7 +1370,7 @@ static off_t get_delta_base(struct packed_git *p,
                        base_offset = (base_offset << 7) + (c & 127);
                }
                base_offset = delta_obj_offset - base_offset;
-               if (base_offset >= delta_obj_offset)
+               if (base_offset <= 0 || base_offset >= delta_obj_offset)
                        return 0;  /* out of bound */
                *curpos += used;
        } else if (type == OBJ_REF_DELTA) {
@@ -1381,15 +1396,32 @@ static int packed_delta_info(struct packed_git *p,
        off_t base_offset;
 
        base_offset = get_delta_base(p, w_curs, &curpos, type, obj_offset);
+       if (!base_offset)
+               return OBJ_BAD;
        type = packed_object_info(p, base_offset, NULL);
+       if (type <= OBJ_NONE) {
+               struct revindex_entry *revidx;
+               const unsigned char *base_sha1;
+               revidx = find_pack_revindex(p, base_offset);
+               if (!revidx)
+                       return OBJ_BAD;
+               base_sha1 = nth_packed_object_sha1(p, revidx->nr);
+               mark_bad_packed_object(p, base_sha1);
+               type = sha1_object_info(base_sha1, NULL);
+               if (type <= OBJ_NONE)
+                       return OBJ_BAD;
+       }
 
        /* We choose to only get the type of the base object and
         * ignore potentially corrupt pack file that expects the delta
         * based on a base with a wrong size.  This saves tons of
         * inflate() calls.
         */
-       if (sizep)
+       if (sizep) {
                *sizep = get_size_from_delta(p, w_curs, curpos);
+               if (*sizep == 0)
+                       type = OBJ_BAD;
+       }
 
        return type;
 }
@@ -1411,10 +1443,11 @@ static int unpack_object_header(struct packed_git *p,
         * insane, so we know won't exceed what we have been given.
         */
        base = use_pack(p, w_curs, *curpos, &left);
-       used = unpack_object_header_gently(base, left, &type, sizep);
-       if (!used)
-               die("object offset outside of pack file");
-       *curpos += used;
+       used = unpack_object_header_buffer(base, left, &type, sizep);
+       if (!used) {
+               type = OBJ_BAD;
+       } else
+               *curpos += used;
 
        return type;
 }
@@ -1498,8 +1531,9 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset,
                        *sizep = size;
                break;
        default:
-               die("pack %s contains unknown object type %d",
-                   p->pack_name, type);
+               error("unknown object type %i at offset %"PRIuMAX" in %s",
+                     type, (uintmax_t)obj_offset, p->pack_name);
+               type = OBJ_BAD;
        }
        unuse_pack(&w_curs);
        return type;
@@ -1663,9 +1697,12 @@ static void *unpack_delta_entry(struct packed_git *p,
                 * This is costly but should happen only in the presence
                 * of a corrupted pack, and is better than failing outright.
                 */
-               struct revindex_entry *revidx = find_pack_revindex(p, base_offset);
-               const unsigned char *base_sha1 =
-                                       nth_packed_object_sha1(p, revidx->nr);
+               struct revindex_entry *revidx;
+               const unsigned char *base_sha1;
+               revidx = find_pack_revindex(p, base_offset);
+               if (!revidx)
+                       return NULL;
+               base_sha1 = nth_packed_object_sha1(p, revidx->nr);
                error("failed to read delta base object %s"
                      " at offset %"PRIuMAX" from %s",
                      sha1_to_hex(base_sha1), (uintmax_t)base_offset,
@@ -1694,6 +1731,8 @@ static void *unpack_delta_entry(struct packed_git *p,
        return result;
 }
 
+int do_check_packed_object_crc;
+
 void *unpack_entry(struct packed_git *p, off_t obj_offset,
                   enum object_type *type, unsigned long *sizep)
 {
@@ -1701,6 +1740,19 @@ void *unpack_entry(struct packed_git *p, off_t obj_offset,
        off_t curpos = obj_offset;
        void *data;
 
+       if (do_check_packed_object_crc && p->index_version > 1) {
+               struct revindex_entry *revidx = find_pack_revindex(p, obj_offset);
+               unsigned long len = revidx[1].offset - obj_offset;
+               if (check_pack_crc(p, &w_curs, obj_offset, len, revidx->nr)) {
+                       const unsigned char *sha1 =
+                               nth_packed_object_sha1(p, revidx->nr);
+                       error("bad packed object CRC for %s",
+                             sha1_to_hex(sha1));
+                       mark_bad_packed_object(p, sha1);
+                       return NULL;
+               }
+       }
+
        *type = unpack_object_header(p, &w_curs, &curpos, sizep);
        switch (*type) {
        case OBJ_OFS_DELTA:
@@ -1954,7 +2006,14 @@ int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
                if (!find_pack_entry(sha1, &e, NULL))
                        return status;
        }
-       return packed_object_info(e.p, e.offset, sizep);
+
+       status = packed_object_info(e.p, e.offset, sizep);
+       if (status < 0) {
+               mark_bad_packed_object(e.p, sha1);
+               status = sha1_object_info(sha1, sizep);
+       }
+
+       return status;
 }
 
 static void *read_packed_sha1(const unsigned char *sha1,
@@ -2333,6 +2392,7 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)
        enum object_type type;
        char hdr[32];
        int hdrlen;
+       int ret;
 
        if (has_loose_object(sha1))
                return 0;
@@ -2340,7 +2400,10 @@ int force_object_loose(const unsigned char *sha1, time_t mtime)
        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);
+       ret = write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
+       free(buf);
+
+       return ret;
 }
 
 int has_pack_index(const unsigned char *sha1)
index 41b680915d7348bf622397da8b1465d3769a361a..159c2ab84fa2cdde0e540024a1ca22e0bbb43af8 100644 (file)
@@ -245,11 +245,13 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 
        *ref = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
+               char fullref[PATH_MAX];
                unsigned char sha1_from_ref[20];
                unsigned char *this_result;
 
                this_result = refs_found ? sha1_from_ref : sha1;
-               r = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+               mksnpath(fullref, sizeof(fullref), *p, len, str);
+               r = resolve_ref(fullref, this_result, 1, NULL);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
@@ -272,7 +274,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                char path[PATH_MAX];
                const char *ref, *it;
 
-               strcpy(path, mkpath(*p, len, str));
+               mksnpath(path, sizeof(path), *p, len, str);
                ref = resolve_ref(path, hash, 1, NULL);
                if (!ref)
                        continue;
index 3d8e06a20fade230136d50736a2f621d3c96002c..1c77192eb318d007689089eaf42f4f939c2f9ee4 100755 (executable)
@@ -47,6 +47,23 @@ test_expect_success 'attribute test' '
 
 '
 
+test_expect_success 'attribute test: read paths from stdin' '
+
+       cat <<EOF > expect
+f: test: f
+a/f: test: f
+a/c/f: test: f
+a/g: test: a/g
+a/b/g: test: a/b/g
+b/g: test: unspecified
+a/b/h: test: a/b/h
+a/b/d/g: test: a/b/d/*
+EOF
+
+       sed -e "s/:.*//" < expect | git check-attr --stdin test > actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'root subdir attribute test' '
 
        attr_check a/i a/i &&
index b0d31f5a9bb8b3474665147327d94ad5067fa206..849911683a799981a565416a88fe9092b3727853 100755 (executable)
@@ -27,4 +27,64 @@ test_expect_success 'reset should work' '
   test_cmp expect actual
 '
 
+test_expect_success 'reset should remove remnants from a failed merge' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
+test_expect_success 'Porcelain reset should remove remnants too' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git reset --hard &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
+test_expect_success 'Porcelain checkout -f should remove remnants too' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git checkout -f &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
+test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
+  git read-tree --reset -u HEAD &&
+  git ls-files -s >expect &&
+  sha1=$(git rev-parse :new) &&
+  (
+       echo "100644 $sha1 1    old"
+       echo "100644 $sha1 3    old"
+  ) | git update-index --index-info &&
+  >old &&
+  git ls-files -s &&
+  git checkout -f HEAD &&
+  git ls-files -s >actual &&
+  ! test -f old
+'
+
 test_done
index dc85e8b60a5c10e57047d1692e383f177e2c478d..653362ba221ee017512264c83a216b1ad1723bcd 100755 (executable)
@@ -7,6 +7,9 @@ test_description='Test shared repository initialization'
 
 . ./test-lib.sh
 
+# Remove a default ACL from the test dir if possible.
+setfacl -k . 2>/dev/null
+
 # User must have read permissions to the repo -> failure on --shared=0400
 test_expect_success 'shared = 0400 (faulty permission u-w)' '
        mkdir sub && (
@@ -17,6 +20,10 @@ test_expect_success 'shared = 0400 (faulty permission u-w)' '
        test $ret != "0"
 '
 
+modebits () {
+       ls -l "$1" | sed -e 's|^\(..........\).*|\1|'
+}
+
 for u in 002 022
 do
        test_expect_success "shared=1 does not clear bits preset by umask $u" '
@@ -82,8 +89,7 @@ do
 
                rm -f .git/info/refs &&
                git update-server-info &&
-               actual="$(ls -l .git/info/refs)" &&
-               actual=${actual%% *} &&
+               actual="$(modebits .git/info/refs)" &&
                test "x$actual" = "x-$y" || {
                        ls -lt .git/info
                        false
@@ -95,8 +101,7 @@ do
 
                rm -f .git/info/refs &&
                git update-server-info &&
-               actual="$(ls -l .git/info/refs)" &&
-               actual=${actual%% *} &&
+               actual="$(modebits .git/info/refs)" &&
                test "x$actual" = "x-$x" || {
                        ls -lt .git/info
                        false
index 04c2b164bcba0bdead45a50bbb14ceff19e8411f..bd589268fcf459f07ff6c53e43d41e66fe1e7903 100755 (executable)
@@ -75,6 +75,24 @@ test_expect_success "delete $m (by HEAD)" '
 '
 rm -f .git/$m
 
+cp -f .git/HEAD .git/HEAD.orig
+test_expect_success "delete symref without dereference" '
+       git update-ref --no-deref -d HEAD &&
+       ! test -f .git/HEAD
+'
+cp -f .git/HEAD.orig .git/HEAD
+
+test_expect_success "delete symref without dereference when the referred ref is packed" '
+       echo foo >foo.c &&
+       git add foo.c &&
+       git commit -m foo &&
+       git pack-refs --all &&
+       git update-ref --no-deref -d HEAD &&
+       ! test -f .git/HEAD
+'
+cp -f .git/HEAD.orig .git/HEAD
+git update-ref -d $m
+
 test_expect_success '(not) create HEAD with old sha1' "
        test_must_fail git update-ref HEAD $A $B
 "
index ed12c4d78298dec9c5f1bf83f4f877b07d2659c0..9fa561047430ae40a1860131a024e4fd7744f5cb 100755 (executable)
@@ -13,7 +13,7 @@ file if core.symlinks is false.'
 test_expect_success \
 'preparation' '
 git config core.symlinks false &&
-l=$(echo -n file | git hash-object -t blob -w --stdin) &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
 echo "120000 $l        symlink" | git update-index --index-info'
 
 test_expect_success \
diff --git a/t/t2011-checkout-invalid-head.sh b/t/t2011-checkout-invalid-head.sh
new file mode 100755 (executable)
index 0000000..764bb0a
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+test_description='checkout switching away from an invalid branch'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       echo hello >world &&
+       git add world &&
+       git commit -m initial
+'
+
+test_expect_success 'checkout master from invalid HEAD' '
+       echo 0000000000000000000000000000000000000000 >.git/HEAD &&
+       git checkout master --
+'
+
+test_done
index f195aefe3a207fa5bac447b59f16423da25abc21..1ed44ee503f9ecfb5222a9bce3f42ff2aa8127bc 100755 (executable)
@@ -13,12 +13,12 @@ even if a plain file is in the working tree if core.symlinks is false.'
 test_expect_success \
 'preparation' '
 git config core.symlinks false &&
-l=$(echo -n file | git hash-object -t blob -w --stdin) &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
 echo "120000 $l        symlink" | git update-index --index-info'
 
 test_expect_success \
 'modify the symbolic link' '
-echo -n new-file > symlink &&
+printf new-file > symlink &&
 git update-index symlink'
 
 test_expect_success \
index 2147eacc5057128facc08816a2980646bed28ec5..25e9971fd86af10c1031f4e8061c31a0c80725a7 100755 (executable)
@@ -112,6 +112,15 @@ test_expect_success 'config information was renamed, too' \
        "test $(git config branch.s.dummy) = Hello &&
         test_must_fail git config branch.s/s/dummy"
 
+test_expect_success 'renaming a symref is not allowed' \
+'
+       git symbolic-ref refs/heads/master2 refs/heads/master &&
+       test_must_fail git branch -m master2 master3 &&
+       git symbolic-ref refs/heads/master2 &&
+       test -f .git/refs/heads/master &&
+       ! test -f .git/refs/heads/master3
+'
+
 test_expect_success \
     'git branch -m u v should fail when the reflog for u is a symlink' '
      git branch -l u &&
index 087ef75061f4d56fcb6350284d91820485249087..413019acafc98646a61e77960527f042a8f96ac6 100755 (executable)
@@ -96,6 +96,13 @@ test_expect_success \
      git branch -d n/o/p &&
      git branch n'
 
+test_expect_success \
+       'see if up-to-date packed refs are preserved' \
+       'git branch q &&
+        git pack-refs --all --prune &&
+        git update-ref refs/heads/q refs/heads/q &&
+        ! test -f .git/refs/heads/q'
+
 test_expect_success 'pack, prune and repack' '
        git tag foo &&
        git pack-refs --all --prune &&
index bc93dda8fdecf1a5c939bca10f21ebcbc16c69ce..1f1b85067773e40262c2ace48a67b60179663931 100755 (executable)
@@ -123,4 +123,20 @@ test_expect_success 'pre-rebase hook stops rebase (2)' '
        test 0 = $(git rev-list HEAD...side | wc -l)
 '
 
+test_expect_success 'rebase --no-verify overrides pre-rebase (1)' '
+       git checkout test &&
+       git reset --hard side &&
+       git rebase --no-verify master &&
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+       test "z$(cat git)" = zworld
+'
+
+test_expect_success 'rebase --no-verify overrides pre-rebase (2)' '
+       git checkout test &&
+       git reset --hard side &&
+       EDITOR=true git rebase --no-verify -i master &&
+       test "z$(git symbolic-ref HEAD)" = zrefs/heads/test &&
+       test "z$(cat git)" = zworld
+'
+
 test_done
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
new file mode 100755 (executable)
index 0000000..8cde40f
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/sh
+#
+# Copyright(C) 2008 Stephen Habermann & Andreas Ericsson
+#
+test_description='git rebase -p should preserve merges
+
+Run "git rebase -p" and check that merges are properly carried along
+'
+. ./test-lib.sh
+
+GIT_AUTHOR_EMAIL=bogus_email_address
+export GIT_AUTHOR_EMAIL
+
+#echo 'Setting up:
+#
+#A1--A2  <-- origin/master
+# \   \
+#  B1--M  <-- topic
+#   \
+#    B2  <-- origin/topic
+#
+#'
+
+test_expect_success 'setup for merge-preserving rebase' \
+       'echo First > A &&
+       git add A &&
+       git-commit -m "Add A1" &&
+       git checkout -b topic &&
+       echo Second > B &&
+       git add B &&
+       git-commit -m "Add B1" &&
+       git checkout -f master &&
+       echo Third >> A &&
+       git-commit -a -m "Modify A2" &&
+
+       git clone ./. clone1 &&
+       cd clone1 &&
+       git checkout -b topic origin/topic &&
+       git merge origin/master &&
+       cd ..
+
+       git clone ./. clone2
+       cd clone2 &&
+       git checkout -b topic origin/topic &&
+       git merge origin/master &&
+       cd .. &&
+
+       git checkout topic &&
+       echo Fourth >> B &&
+       git commit -a -m "Modify B2"
+'
+
+test_expect_success 'rebase -p fakes interactive rebase' '
+       cd clone2 &&
+       git fetch &&
+       git rebase -p origin/topic &&
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
+       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge commit" | wc -l)
+'
+
+test_done
diff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh
new file mode 100755 (executable)
index 0000000..5816415
--- /dev/null
@@ -0,0 +1,139 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with preserve merges and ensures commits
+dropped by the --cherry-pick flag have their childrens parents
+rewritten.
+'
+. ./test-lib.sh
+
+# set up two branches like this:
+#
+# A - B - C - D - E
+#   \
+#     F - G - H
+#       \
+#         I
+#
+# where B, D and G touch the same file.
+
+test_expect_success 'setup' '
+       : > file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit -m A &&
+       git tag A &&
+       echo 1 > file1 &&
+       test_tick &&
+       git commit -m B file1 &&
+       : > file2 &&
+       git add file2 &&
+       test_tick &&
+       git commit -m C &&
+       echo 2 > file1 &&
+       test_tick &&
+       git commit -m D file1 &&
+       : > file3 &&
+       git add file3 &&
+       test_tick &&
+       git commit -m E &&
+       git tag E &&
+       git checkout -b branch1 A &&
+       : > file4 &&
+       git add file4 &&
+       test_tick &&
+       git commit -m F &&
+       git tag F &&
+       echo 3 > file1 &&
+       test_tick &&
+       git commit -m G file1 &&
+       git tag G &&
+       : > file5 &&
+       git add file5 &&
+       test_tick &&
+       git commit -m H &&
+       git tag H &&
+       git checkout -b branch2 F &&
+       : > file6 &&
+       git add file6 &&
+       test_tick &&
+       git commit -m I &&
+       git tag I
+'
+
+# A - B - C - D - E
+#   \             \ \
+#     F - G - H -- L \        -->   L
+#       \            |               \
+#         I -- G2 -- J -- K           I -- K
+# G2 = same changes as G
+test_expect_success 'skip same-resolution merges with -p' '
+       git checkout branch1 &&
+       ! git merge E &&
+       echo 23 > file1 &&
+       git add file1 &&
+       git commit -m L &&
+       git checkout branch2 &&
+       echo 3 > file1 &&
+       git commit -a -m G2 &&
+       ! git merge E &&
+       echo 23 > file1 &&
+       git add file1 &&
+       git commit -m J &&
+       echo file7 > file7 &&
+       git add file7 &&
+       git commit -m K &&
+       GIT_EDITOR=: git rebase -i -p branch1 &&
+       test $(git rev-parse branch2^^) = $(git rev-parse branch1) &&
+       test "23" = "$(cat file1)" &&
+       test "" = "$(cat file6)" &&
+       test "file7" = "$(cat file7)" &&
+
+       git checkout branch1 &&
+       git reset --hard H &&
+       git checkout branch2 &&
+       git reset --hard I
+'
+
+# A - B - C - D - E
+#   \             \ \
+#     F - G - H -- L \        -->   L
+#       \            |               \
+#         I -- G2 -- J -- K           I -- G2 -- K
+# G2 = different changes as G
+test_expect_success 'keep different-resolution merges with -p' '
+       git checkout branch1 &&
+       ! git merge E &&
+       echo 23 > file1 &&
+       git add file1 &&
+       git commit -m L &&
+       git checkout branch2 &&
+       echo 4 > file1 &&
+       git commit -a -m G2 &&
+       ! git merge E &&
+       echo 24 > file1 &&
+       git add file1 &&
+       git commit -m J &&
+       echo file7 > file7 &&
+       git add file7 &&
+       git commit -m K &&
+       ! GIT_EDITOR=: git rebase -i -p branch1 &&
+       echo 234 > file1 &&
+       git add file1 &&
+       GIT_EDITOR=: git rebase --continue &&
+       test $(git rev-parse branch2^^^) = $(git rev-parse branch1) &&
+       test "234" = "$(cat file1)" &&
+       test "" = "$(cat file6)" &&
+       test "file7" = "$(cat file7)" &&
+
+       git checkout branch1 &&
+       git reset --hard H &&
+       git checkout branch2 &&
+       git reset --hard I
+'
+
+test_done
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
new file mode 100644 (file)
index 0000000..aacfaae
--- /dev/null
@@ -0,0 +1,135 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Stephen Haberman
+#
+
+test_description='git rebase preserve merges
+
+This test runs git rebase with and tries to squash a commit from after a merge
+to before the merge.
+'
+. ./test-lib.sh
+
+# Copy/paste from t3404-rebase-interactive.sh
+echo "#!$SHELL_PATH" >fake-editor.sh
+cat >> fake-editor.sh <<\EOF
+case "$1" in
+*/COMMIT_EDITMSG)
+       test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+       test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
+       exit
+       ;;
+esac
+test -z "$EXPECT_COUNT" ||
+       test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+       exit
+test -z "$FAKE_LINES" && exit
+grep -v '^#' < "$1" > "$1".tmp
+rm -f "$1"
+cat "$1".tmp
+action=pick
+for line in $FAKE_LINES; do
+       case $line in
+       squash|edit)
+               action="$line";;
+       *)
+               echo sed -n "${line}s/^pick/$action/p"
+               sed -n "${line}p" < "$1".tmp
+               sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+               action=pick;;
+       esac
+done
+EOF
+
+test_set_editor "$(pwd)/fake-editor.sh"
+chmod a+x fake-editor.sh
+
+# set up two branches like this:
+#
+# A1 - B1 - D1 - E1 - F1
+#       \        /
+#        -- C1 --
+
+test_expect_success 'setup' '
+       touch a &&
+       touch b &&
+       git add a &&
+       git commit -m A1 &&
+       git tag A1
+       git add b &&
+       git commit -m B1 &&
+       git tag B1 &&
+       git checkout -b branch &&
+       touch c &&
+       git add c &&
+       git commit -m C1 &&
+       git checkout master &&
+       touch d &&
+       git add d &&
+       git commit -m D1 &&
+       git merge branch &&
+       touch f &&
+       git add f &&
+       git commit -m F1 &&
+       git tag F1
+'
+
+# Should result in:
+#
+# A1 - B1 - D2 - E2
+#       \        /
+#        -- C1 --
+#
+test_expect_success 'squash F1 into D1' '
+       FAKE_LINES="1 squash 3 2" git rebase -i -p B1 &&
+       test "$(git rev-parse HEAD^2)" = "$(git rev-parse branch)" &&
+       test "$(git rev-parse HEAD~2)" = "$(git rev-parse B1)" &&
+       git tag E2
+'
+
+# Start with:
+#
+# A1 - B1 - D2 - E2
+#  \
+#   G1 ---- L1 ---- M1
+#    \             /
+#     H1 -- J1 -- K1
+#      \         /
+#        -- I1 --
+#
+# And rebase G1..M1 onto E2
+
+test_expect_success 'rebase two levels of merge' '
+       git checkout -b branch2 A1 &&
+       touch g &&
+       git add g &&
+       git commit -m G1 &&
+       git checkout -b branch3 &&
+       touch h
+       git add h &&
+       git commit -m H1 &&
+       git checkout -b branch4 &&
+       touch i &&
+       git add i &&
+       git commit -m I1 &&
+       git tag I1 &&
+       git checkout branch3 &&
+       touch j &&
+       git add j &&
+       git commit -m J1 &&
+       git merge I1 --no-commit &&
+       git commit -m K1 &&
+       git tag K1 &&
+       git checkout branch2 &&
+       touch l &&
+       git add l &&
+       git commit -m L1 &&
+       git merge K1 --no-commit &&
+       git commit -m M1 &&
+       GIT_EDITOR=: git rebase -i -p E2 &&
+       test "$(git rev-parse HEAD~3)" = "$(git rev-parse E2)" &&
+       test "$(git rev-parse HEAD~2)" = "$(git rev-parse HEAD^2^2~2)" &&
+       test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse HEAD^2^2^1)"
+'
+
+test_done
index 66aca99fd32c6b98f5e6e34a3cf7b096b3e236cf..5b4d6f71387ce7f30cb7ee6b596357aa1942ab6e 100755 (executable)
@@ -187,6 +187,19 @@ test_expect_success 'but with -f it should work.' '
        test_must_fail git ls-files --error-unmatch baz
 '
 
+test_expect_failure 'refuse to remove cached empty file with modifications' '
+       touch empty &&
+       git add empty &&
+       echo content >empty &&
+       test_must_fail git rm --cached empty
+'
+
+test_expect_success 'remove intent-to-add file without --force' '
+       echo content >intent-to-add &&
+       git add -N intent-to-add
+       git rm --cached intent-to-add
+'
+
 test_expect_success 'Recursive test setup' '
        mkdir -p frotz &&
        echo qfwfq >frotz/nitfol &&
index 421f4bba3fbc666cf2ab7ec4289f10d29d61eebb..3cf5b5c4ea05d861d75aa146a52d7d273dcaa4cb 100755 (executable)
@@ -25,11 +25,11 @@ cat > expected <<\EOF
 EOF
 test_expect_success 'diff without --binary' \
        'git diff | git apply --stat --summary >current &&
-        cmp current expected'
+        test_cmp expected current'
 
 test_expect_success 'diff with --binary' \
        'git diff --binary | git apply --stat --summary >current &&
-        cmp current expected'
+        test_cmp expected current'
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
index fe6080da576c45cec800376e81915c51f11f7106..aeb5405cfeee83f13f3d543db2eba07f1fb5c241 100755 (executable)
@@ -236,12 +236,15 @@ show --patch-with-stat --summary side
 format-patch --stdout initial..side
 format-patch --stdout initial..master^
 format-patch --stdout initial..master
+format-patch --stdout --no-numbered initial..master
+format-patch --stdout --numbered initial..master
 format-patch --attach --stdout initial..side
 format-patch --attach --stdout initial..master^
 format-patch --attach --stdout initial..master
 format-patch --inline --stdout initial..side
 format-patch --inline --stdout initial..master^
 format-patch --inline --stdout initial..master
+format-patch --inline --stdout initial..master
 format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
 config format.subjectprefix DIFFERENT_PREFIX
 format-patch --inline --stdout initial..master^^
index 43346b9ba443fe22b56f0874a7cc885461d2aa81..e5ab74437ef5e42f7863e56546f0bdb1923c2949 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --attach --stdout initial..master
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/3] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -63,7 +63,7 @@ index 01e79c3..0000000
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/3] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -111,7 +111,7 @@ index 0000000..b1e6722
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [PATCH] Side
+Subject: [PATCH 3/3] Side
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
index d7490a9fd729890c80a4b8fc3da0783997f81a04..2c71d20d376094fe1be51baa316fb080ad2155b4 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --attach --stdout initial..master^
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/2] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -63,7 +63,7 @@ index 01e79c3..0000000
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/2] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
index fca5cce373767d96fd68a17a196889c8c9ea172f..58f8a7b7d6a86021e5b01888324dea97e8a50347 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --inline --stdout --subject-prefix=TESTCASE initial..master
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [TESTCASE] Second
+Subject: [TESTCASE 1/3] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -63,7 +63,7 @@ index 01e79c3..0000000
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [TESTCASE] Third
+Subject: [TESTCASE 2/3] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -111,7 +111,7 @@ index 0000000..b1e6722
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [TESTCASE] Side
+Subject: [TESTCASE 3/3] Side
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
index 6d6fac390849c964e75b56e48808a78dd3428ce1..9e7bbdffa226e1d687b1ac93aa5334540708a97e 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --inline --stdout initial..master
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/3] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -63,7 +63,7 @@ index 01e79c3..0000000
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/3] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -111,7 +111,7 @@ index 0000000..b1e6722
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [PATCH] Side
+Subject: [PATCH 3/3] Side
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
index 18a1110def4bbe25c0bd7020d35df589ef6f528f..f881f644ccdd9954ae38709a75060a5621666849 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --inline --stdout initial..master^
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/2] Second
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
@@ -63,7 +63,7 @@ index 01e79c3..0000000
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/2] Third
 MIME-Version: 1.0
 Content-Type: multipart/mixed; boundary="------------g-i-t--v-e-r-s-i-o-n"
 
diff --git a/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--no-numbered_initial..master
new file mode 100644 (file)
index 0000000..f7752eb
--- /dev/null
@@ -0,0 +1,127 @@
+$ git format-patch --stdout --no-numbered initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_--numbered_initial..master b/t/t4013/diff.format-patch_--stdout_--numbered_initial..master
new file mode 100644 (file)
index 0000000..8e67dbf
--- /dev/null
@@ -0,0 +1,127 @@
+$ git format-patch --stdout --numbered initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH 1/3] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH 2/3] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH 3/3] Side
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
index 8b88ca49276695aead0083ad49c53715874e11dc..7b89978e321bc20c55a7016f7111275bb553b1f0 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --stdout initial..master
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/3] Second
 
 This is the second commit.
 ---
@@ -48,7 +48,7 @@ g-i-t--v-e-r-s-i-o-n
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/3] Third
 
 ---
  dir/sub |    2 ++
@@ -82,7 +82,7 @@ g-i-t--v-e-r-s-i-o-n
 From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
-Subject: [PATCH] Side
+Subject: [PATCH 3/3] Side
 
 ---
  dir/sub |    2 ++
index 47a4b88637d4dc8650f0480278ae67b907eb5080..b7f9725dc4158d4cc6131203d3a6ee1b7fd8c456 100644 (file)
@@ -2,7 +2,7 @@ $ git format-patch --stdout initial..master^
 From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
-Subject: [PATCH] Second
+Subject: [PATCH 1/2] Second
 
 This is the second commit.
 ---
@@ -48,7 +48,7 @@ g-i-t--v-e-r-s-i-o-n
 From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
-Subject: [PATCH] Third
+Subject: [PATCH 2/2] Third
 
 ---
  dir/sub |    2 ++
index 520e095c597a61f0f263a274a53ae0aa2781da85..be541348c6f8d469428e2fcc7abaf6d25168c08f 100755 (executable)
@@ -32,7 +32,7 @@ EOF
 
 sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
 
-builtin_patterns="bibtex html java pascal php python ruby tex"
+builtin_patterns="bibtex html java objc pascal php python ruby tex"
 for p in $builtin_patterns
 do
        test_expect_success "builtin $p pattern compiles" '
@@ -65,7 +65,14 @@ test_expect_success 'custom pattern' '
 
 test_expect_success 'last regexp must not be negated' '
        git config diff.java.funcname "!static" &&
-       test_must_fail git diff --no-index Beer.java Beer-correct.java
+       git diff --no-index Beer.java Beer-correct.java 2>&1 |
+       grep "fatal: Last expression must not be negated:"
+'
+
+test_expect_success 'pattern which matches to end of line' '
+       git config diff.java.funcname "Beer$" &&
+       git diff --no-index Beer.java Beer-correct.java |
+       grep "^@@.*@@ Beer"
 '
 
 test_expect_success 'alternation in pattern' '
index 43d64bbd826049c5a0a8cf365ed5cb3f4489752c..390af2389f3b9559fcfebe1e21a24317562ab7af 100755 (executable)
@@ -45,17 +45,22 @@ test_numbered() {
        grep "^Subject: \[PATCH 2/2\]" $1
 }
 
-test_expect_success 'Default: no numbered' '
+test_expect_success 'single patch defaults to no numbers' '
+       git format-patch --stdout HEAD~1 >patch0.single &&
+       test_single_no_numbered patch0.single
+'
+
+test_expect_success 'multiple patch defaults to numbered' '
 
-       git format-patch --stdout HEAD~2 >patch0 &&
-       test_no_numbered patch0
+       git format-patch --stdout HEAD~2 >patch0.multiple &&
+       test_numbered patch0.multiple
 
 '
 
 test_expect_success 'Use --numbered' '
 
-       git format-patch --numbered --stdout HEAD~2 >patch1 &&
-       test_numbered patch1
+       git format-patch --numbered --stdout HEAD~1 >patch1 &&
+       test_single_numbered patch1
 
 '
 
diff --git a/t/t4030-diff-textconv.sh b/t/t4030-diff-textconv.sh
new file mode 100755 (executable)
index 0000000..03ba26a
--- /dev/null
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='diff.*.textconv tests'
+. ./test-lib.sh
+
+find_diff() {
+       sed '1,/^index /d' | sed '/^-- $/,$d'
+}
+
+cat >expect.binary <<'EOF'
+Binary files a/file and b/file differ
+EOF
+
+cat >expect.text <<'EOF'
+--- a/file
++++ b/file
+@@ -1 +1,2 @@
+ 0
++1
+EOF
+
+cat >hexdump <<'EOF'
+#!/bin/sh
+perl -e '$/ = undef; $_ = <>; s/./ord($&)/ge; print $_' "$1"
+EOF
+chmod +x hexdump
+
+test_expect_success 'setup binary file with history' '
+       printf "\\0\\n" >file &&
+       git add file &&
+       git commit -m one &&
+       printf "\\1\\n" >>file &&
+       git add file &&
+       git commit -m two
+'
+
+test_expect_success 'file is considered binary by porcelain' '
+       git diff HEAD^ HEAD >diff &&
+       find_diff <diff >actual &&
+       test_cmp expect.binary actual
+'
+
+test_expect_success 'file is considered binary by plumbing' '
+       git diff-tree -p HEAD^ HEAD >diff &&
+       find_diff <diff >actual &&
+       test_cmp expect.binary actual
+'
+
+test_expect_success 'setup textconv filters' '
+       echo file diff=foo >.gitattributes &&
+       git config diff.foo.textconv "$PWD"/hexdump &&
+       git config diff.fail.textconv false
+'
+
+test_expect_success 'diff produces text' '
+       git diff HEAD^ HEAD >diff &&
+       find_diff <diff >actual &&
+       test_cmp expect.text actual
+'
+
+test_expect_success 'diff-tree produces binary' '
+       git diff-tree -p HEAD^ HEAD >diff &&
+       find_diff <diff >actual &&
+       test_cmp expect.binary actual
+'
+
+test_expect_success 'log produces text' '
+       git log -1 -p >log &&
+       find_diff <log >actual &&
+       test_cmp expect.text actual
+'
+
+test_expect_success 'format-patch produces binary' '
+       git format-patch --no-binary --stdout HEAD^ >patch &&
+       find_diff <patch >actual &&
+       test_cmp expect.binary actual
+'
+
+test_expect_success 'status -v produces text' '
+       git reset --soft HEAD^ &&
+       git status -v >diff &&
+       find_diff <diff >actual &&
+       test_cmp expect.text actual &&
+       git reset --soft HEAD@{1}
+'
+
+cat >expect.stat <<'EOF'
+ file |  Bin 2 -> 4 bytes
+ 1 files changed, 0 insertions(+), 0 deletions(-)
+EOF
+test_expect_success 'diffstat does not run textconv' '
+       echo file diff=fail >.gitattributes &&
+       git diff --stat HEAD^ HEAD >actual &&
+       test_cmp expect.stat actual
+'
+# restore working setup
+echo file diff=foo >.gitattributes
+
+cat >expect.typechange <<'EOF'
+--- a/file
++++ /dev/null
+@@ -1,2 +0,0 @@
+-0
+-1
+diff --git a/file b/file
+new file mode 120000
+index ad8b3d2..67be421
+--- /dev/null
++++ b/file
+@@ -0,0 +1 @@
++frotz
+\ No newline at end of file
+EOF
+# make a symlink the hard way that works on symlink-challenged file systems
+test_expect_success 'textconv does not act on symlinks' '
+       printf frotz > file &&
+       git add file &&
+       git ls-files -s | sed -e s/100644/120000/ |
+               git update-index --index-info &&
+       git commit -m typechange &&
+       git show >diff &&
+       find_diff <diff >actual &&
+       test_cmp expect.typechange actual
+'
+
+test_done
index bc7a8a8689c8ca2b9902c577c9d9bdd1fa97815c..8f6aea48d84621ae3b7304636452c724a4bbe5b6 100755 (executable)
@@ -72,7 +72,7 @@ test_expect_success 'apply --directory (delete file)' '
        echo content >some/sub/dir/delfile &&
        git add some/sub/dir/delfile &&
        git apply --directory=some/sub/dir/ --index patch &&
-       ! git ls-files | grep delfile
+       ! (git ls-files | grep delfile)
 '
 
 cat > patch << 'EOF'
index 4448aba7e0727b70a4e525e57ad21cd7e226c1f8..2b912d77283fbd3fc02a7d9d6bca660e4193b8c3 100755 (executable)
@@ -22,7 +22,7 @@ test_expect_success setup '
                test_tick &&
                git commit -a -m $i || break
        done &&
-       git format-patch initial &&
+       git format-patch --no-numbered initial &&
        git checkout -b side initial &&
        echo local change >file-2-expect
 '
index e395ff4e341bacea21cc5cd909304b7bb4fcb044..c942c8be85339157e22f755d8fc94e64efaee4dd 100755 (executable)
@@ -57,6 +57,11 @@ test_expect_success \
      git update-ref HEAD $(TZ=GMT GIT_COMMITTER_DATE="2005-05-27 22:00:00" \
      git commit-tree $treeid </dev/null)'
 
+test_expect_success \
+    'create bare clone' \
+    'git clone --bare . bare.git &&
+     cp .gitattributes bare.git/info/attributes'
+
 test_expect_success \
     'remove ignored file' \
     'rm a/ignored'
@@ -73,11 +78,19 @@ test_expect_success \
     'git archive vs. git tar-tree' \
     'diff b.tar b2.tar'
 
+test_expect_success \
+    'git archive in a bare repo' \
+    '(cd bare.git && git archive HEAD) >b3.tar'
+
+test_expect_success \
+    'git archive vs. the same in a bare repo' \
+    'test_cmp b.tar b3.tar'
+
 test_expect_success \
     'validate file modification time' \
     'mkdir extract &&
      "$TAR" xf b.tar -C extract a/a &&
-     perl -e '\''print((stat("extract/a/a"))[9], "\n")'\'' >b.mtime &&
+     test-chmtime -v +0 extract/a/a |cut -f 1 >b.mtime &&
      echo "1117231200" >expected.mtime &&
      diff expected.mtime b.mtime'
 
@@ -151,6 +164,14 @@ test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
 
+test_expect_success \
+    'git archive --format=zip in a bare repo' \
+    '(cd bare.git && git archive --format=zip HEAD) >d1.zip'
+
+test_expect_success \
+    'git archive --format=zip vs. the same in a bare repo' \
+    'test_cmp d.zip d1.zip'
+
 $UNZIP -v >/dev/null 2>&1
 if [ $? -eq 127 ]; then
        echo "Skipping ZIP tests, because unzip was not found"
index b335c6b42de59b9632ff29693c2d7c75dae9794a..04522857abb716b8866e0f5153ec33b3ac780536 100755 (executable)
@@ -376,4 +376,10 @@ test_expect_success 'index-pack with --strict' '
        )
 '
 
+test_expect_success 'tolerate absurdly small packsizelimit' '
+       git config pack.packSizeLimit 2 &&
+       packname_9=$(git pack-objects test-9 <obj-list) &&
+       test $(wc -l <obj-list) = $(ls test-9-*.pack | wc -l)
+'
+
 test_done
index 6424db1f28e11c3ac6eb629ba4db7380812eab72..884e24253a0a9d262b39ae96ea5c03ecb7ba4072 100755 (executable)
@@ -11,13 +11,18 @@ test_expect_success \
     'rm -rf .git
      git init &&
      i=1 &&
-        while test $i -le 100
+     while test $i -le 100
      do
-                i=`printf '%03i' $i`
-         echo $i >file_$i &&
-         test-genrandom "$i" 8192 >>file_$i &&
-         git update-index --add file_$i &&
-                i=`expr $i + 1` || return 1
+         iii=`printf '%03i' $i`
+         test-genrandom "bar" 200 > wide_delta_$iii &&
+         test-genrandom "baz $iii" 50 >> wide_delta_$iii &&
+         test-genrandom "foo"$i 100 > deep_delta_$iii &&
+         test-genrandom "foo"`expr $i + 1` 100 >> deep_delta_$iii &&
+         test-genrandom "foo"`expr $i + 2` 100 >> deep_delta_$iii &&
+         echo $iii >file_$iii &&
+         test-genrandom "$iii" 8192 >>file_$iii &&
+         git update-index --add file_$iii deep_delta_$iii wide_delta_$iii &&
+         i=`expr $i + 1` || return 1
      done &&
      { echo 101 && test-genrandom 100 8192; } >file_101 &&
      git update-index --add file_101 &&
@@ -92,6 +97,31 @@ test_expect_success \
     '64-bit offsets: index-pack result should match pack-objects one' \
     'cmp "test-3-${pack3}.idx" "3.idx"'
 
+# returns the object number for given object in given pack index
+index_obj_nr()
+{
+    idx_file=$1
+    object_sha1=$2
+    nr=0
+    git show-index < $idx_file |
+    while read offs sha1 extra
+    do
+      nr=$(($nr + 1))
+      test "$sha1" = "$object_sha1" || continue
+      echo "$(($nr - 1))"
+      break
+    done
+}
+
+# returns the pack offset for given object as found in given pack index
+index_obj_offset()
+{
+    idx_file=$1
+    object_sha1=$2
+    git show-index < $idx_file | grep $object_sha1 |
+    ( read offs extra && echo "$offs" )
+}
+
 test_expect_success \
     '[index v1] 1) stream pack to repository' \
     'git index-pack --index-version=1 --stdin < "test-1-${pack1}.pack" &&
@@ -102,19 +132,22 @@ test_expect_success \
 
 test_expect_success \
     '[index v1] 2) create a stealth corruption in a delta base reference' \
-    '# this test assumes a delta smaller than 16 bytes at the end of the pack
-     git show-index <1.idx | sort -n | sed -ne \$p | (
-       read delta_offs delta_sha1 &&
-       git cat-file blob "$delta_sha1" > blob_1 &&
-       chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
-       dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
-         if=".git/objects/pack/pack-${pack1}.idx" skip=$((256 * 4 + 4)) \
-         bs=1 count=20 conv=notrunc &&
-       git cat-file blob "$delta_sha1" > blob_2 )'
+    '# This test assumes file_101 is a delta smaller than 16 bytes.
+     # It should be against file_100 but we substitute its base for file_099
+     sha1_101=`git hash-object file_101` &&
+     sha1_099=`git hash-object file_099` &&
+     offs_101=`index_obj_offset 1.idx $sha1_101` &&
+     nr_099=`index_obj_nr 1.idx $sha1_099` &&
+     chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+     dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
+        if=".git/objects/pack/pack-${pack1}.idx" \
+        skip=$((4 + 256 * 4 + $nr_099 * 24)) \
+        bs=1 count=20 conv=notrunc &&
+     git cat-file blob $sha1_101 > file_101_foo1'
 
 test_expect_success \
     '[index v1] 3) corrupted delta happily returned wrong data' \
-    '! cmp blob_1 blob_2'
+    'test -f file_101_foo1 && ! cmp file_101 file_101_foo1'
 
 test_expect_success \
     '[index v1] 4) confirm that the pack is actually corrupted' \
@@ -140,19 +173,22 @@ test_expect_success \
 
 test_expect_success \
     '[index v2] 2) create a stealth corruption in a delta base reference' \
-    '# this test assumes a delta smaller than 16 bytes at the end of the pack
-     git show-index <1.idx | sort -n | sed -ne \$p | (
-       read delta_offs delta_sha1 delta_crc &&
-       git cat-file blob "$delta_sha1" > blob_3 &&
-       chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
-       dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($delta_offs + 1)) \
-         if=".git/objects/pack/pack-${pack1}.idx" skip=$((8 + 256 * 4)) \
-         bs=1 count=20 conv=notrunc &&
-       git cat-file blob "$delta_sha1" > blob_4 )'
+    '# This test assumes file_101 is a delta smaller than 16 bytes.
+     # It should be against file_100 but we substitute its base for file_099
+     sha1_101=`git hash-object file_101` &&
+     sha1_099=`git hash-object file_099` &&
+     offs_101=`index_obj_offset 1.idx $sha1_101` &&
+     nr_099=`index_obj_nr 1.idx $sha1_099` &&
+     chmod +w ".git/objects/pack/pack-${pack1}.pack" &&
+     dd of=".git/objects/pack/pack-${pack1}.pack" seek=$(($offs_101 + 1)) \
+        if=".git/objects/pack/pack-${pack1}.idx" \
+        skip=$((8 + 256 * 4 + $nr_099 * 20)) \
+        bs=1 count=20 conv=notrunc &&
+     git cat-file blob $sha1_101 > file_101_foo2'
 
 test_expect_success \
     '[index v2] 3) corrupted delta happily returned wrong data' \
-    '! cmp blob_3 blob_4'
+    'test -f file_101_foo2 && ! cmp file_101 file_101_foo2'
 
 test_expect_success \
     '[index v2] 4) confirm that the pack is actually corrupted' \
@@ -160,16 +196,19 @@ test_expect_success \
 
 test_expect_success \
     '[index v2] 5) pack-objects refuses to reuse corrupted data' \
-    'test_must_fail git pack-objects test-5 <obj-list'
+    'test_must_fail git pack-objects test-5 <obj-list &&
+     test_must_fail git pack-objects --no-reuse-object test-6 <obj-list'
 
 test_expect_success \
     '[index v2] 6) verify-pack detects CRC mismatch' \
     'rm -f .git/objects/pack/* &&
      git index-pack --index-version=2 --stdin < "test-1-${pack1}.pack" &&
      git verify-pack ".git/objects/pack/pack-${pack1}.pack" &&
+     obj=`git hash-object file_001` &&
+     nr=`index_obj_nr ".git/objects/pack/pack-${pack1}.idx" $obj` &&
      chmod +w ".git/objects/pack/pack-${pack1}.idx" &&
      dd if=/dev/zero of=".git/objects/pack/pack-${pack1}.idx" conv=notrunc \
-        bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + 0)) &&
+        bs=1 count=4 seek=$((8 + 256 * 4 + `wc -l <obj-list` * 20 + $nr * 4)) &&
      ( while read obj
        do git cat-file -p $obj >/dev/null || exit 1
        done <obj-list ) &&
@@ -177,4 +216,14 @@ test_expect_success \
        ".git/objects/pack/pack-${pack1}.pack" 2>&1) &&
      echo "$err" | grep "CRC mismatch"'
 
+test_expect_success 'running index-pack in the object store' '
+    rm -f .git/objects/pack/* &&
+    cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
+    (
+       cd .git/objects/pack
+       git index-pack pack-${pack1}.pack
+    ) &&
+    test -f .git/objects/pack/pack-${pack1}.idx
+'
+
 test_done
index 31b20b21d23c2066fd124bf6a33dbdc689930170..d4e30fc43c68ed9b0fc175f2a88a07de6458f58e 100755 (executable)
@@ -41,11 +41,17 @@ create_new_pack() {
     git verify-pack -v ${pack}.pack
 }
 
+do_repack() {
+    pack=`printf "$blob_1\n$blob_2\n$blob_3\n" |
+          git pack-objects $@ .git/objects/pack/pack` &&
+    pack=".git/objects/pack/pack-${pack}"
+}
+
 do_corrupt_object() {
     ofs=`git show-index < ${pack}.idx | grep $1 | cut -f1 -d" "` &&
     ofs=$(($ofs + $2)) &&
     chmod +w ${pack}.pack &&
-    dd if=/dev/zero of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
+    dd of=${pack}.pack count=1 bs=1 conv=notrunc seek=$ofs &&
     test_must_fail git verify-pack ${pack}.pack
 }
 
@@ -60,7 +66,7 @@ test_expect_success \
 
 test_expect_success \
     'create corruption in header of first object' \
-    'do_corrupt_object $blob_1 0 &&
+    'do_corrupt_object $blob_1 0 < /dev/zero &&
      test_must_fail git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -119,7 +125,7 @@ test_expect_success \
     'create corruption in header of first delta' \
     'create_new_pack &&
      git prune-packed &&
-     do_corrupt_object $blob_2 0 &&
+     do_corrupt_object $blob_2 0 < /dev/zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -133,6 +139,15 @@ test_expect_success \
      git cat-file blob $blob_2 > /dev/null &&
      git cat-file blob $blob_3 > /dev/null'
 
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
 test_expect_success \
     'create corruption in data of first delta' \
     'create_new_pack &&
@@ -152,11 +167,20 @@ test_expect_success \
      git cat-file blob $blob_2 > /dev/null &&
      git cat-file blob $blob_3 > /dev/null'
 
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
 test_expect_success \
     'corruption in delta base reference of first delta (OBJ_REF_DELTA)' \
     'create_new_pack &&
      git prune-packed &&
-     do_corrupt_object $blob_2 2 &&
+     do_corrupt_object $blob_2 2 < /dev/zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
@@ -171,17 +195,75 @@ test_expect_success \
      git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
-    'corruption in delta base reference of first delta (OBJ_OFS_DELTA)' \
+    '... and then a repack "clears" the corruption' \
+    'do_repack &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    'corruption #0 in delta base reference of first delta (OBJ_OFS_DELTA)' \
     'create_new_pack --delta-base-offset &&
      git prune-packed &&
-     do_corrupt_object $blob_2 2 &&
+     do_corrupt_object $blob_2 2 < /dev/zero &&
      git cat-file blob $blob_1 > /dev/null &&
      test_must_fail git cat-file blob $blob_2 > /dev/null &&
      test_must_fail git cat-file blob $blob_3 > /dev/null'
 
 test_expect_success \
-    '... and a redundant pack allows for full recovery too' \
+    '... but having a loose copy allows for full recovery' \
     'mv ${pack}.idx tmp &&
+     git hash-object -t blob -w file_2 &&
+     mv tmp ${pack}.idx &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack --delta-base-offset &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    'corruption #1 in delta base reference of first delta (OBJ_OFS_DELTA)' \
+    'create_new_pack --delta-base-offset &&
+     git prune-packed &&
+     printf "\001" | do_corrupt_object $blob_2 2 &&
+     git cat-file blob $blob_1 > /dev/null &&
+     test_must_fail git cat-file blob $blob_2 > /dev/null &&
+     test_must_fail git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... but having a loose copy allows for full recovery' \
+    'mv ${pack}.idx tmp &&
+     git hash-object -t blob -w file_2 &&
+     mv tmp ${pack}.idx &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and then a repack "clears" the corruption' \
+    'do_repack --delta-base-offset &&
+     git prune-packed &&
+     git verify-pack ${pack}.pack &&
+     git cat-file blob $blob_1 > /dev/null &&
+     git cat-file blob $blob_2 > /dev/null &&
+     git cat-file blob $blob_3 > /dev/null'
+
+test_expect_success \
+    '... and a redundant pack allows for full recovery too' \
+    'do_corrupt_object $blob_2 2 < /dev/zero &&
+     git cat-file blob $blob_1 > /dev/null &&
+     test_must_fail git cat-file blob $blob_2 > /dev/null &&
+     test_must_fail git cat-file blob $blob_3 > /dev/null &&
+     mv ${pack}.idx tmp &&
      git hash-object -t blob -w file_1 &&
      git hash-object -t blob -w file_2 &&
      printf "$blob_1\n$blob_2\n" | git pack-objects .git/objects/pack/pack &&
index 544771d8fa98ec70d84e5083c2bfc0ebdf45f9a1..da69f087b41182be84be0c98a78c1a45b879cfe5 100755 (executable)
@@ -103,6 +103,17 @@ unset GIT_CONFIG GIT_CONFIG_LOCAL
 HOME=`pwd`/no-such-directory
 export HOME ;# this way we force the victim/.git/config to be used.
 
+test_expect_success \
+       'pushing a delete should be denied with denyDeletes' '
+       cd victim &&
+       git config receive.denyDeletes true &&
+       git branch extra master &&
+       cd .. &&
+       test -f victim/.git/refs/heads/extra &&
+       test_must_fail git send-pack ./victim/.git/ :extra master
+'
+rm -f victim/.git/refs/heads/extra
+
 test_expect_success \
         'pushing with --force should be denied with denyNonFastforwards' '
        cd victim &&
index 86abc6227105e27fdb9ad99e34193efb90a6242f..cb9aacc7bc62e2ecfdca0dc7f6071e5330fe09d0 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success setup '
        mkdir another && (
                cd another &&
                git init &&
-               git fetch .. master:master
+               git fetch --update-head-ok .. master:master
        ) &&
 
        >file2 && git add file2 && test_tick &&
index c4496635dfba85b0e9fc105c4516096d433881a4..1f59960d90c31f02768666c86654a97e1fad9305 100755 (executable)
@@ -28,7 +28,7 @@ tokens_match () {
 }
 
 check_remote_track () {
-       actual=$(git remote show "$1" | sed -n -e '$p') &&
+       actual=$(git remote show "$1" | sed -e '1,/Tracked/d') &&
        shift &&
        tokens_match "$*" "$actual"
 }
@@ -115,9 +115,11 @@ cat > test/expect << EOF
   New remote branch (next fetch will store in remotes/origin)
     master
   Tracked remote branches
-    side master
+    side
+    master
   Local branches pushed with 'git push'
-    master:upstream +refs/tags/lastbackup
+    master:upstream
+    +refs/tags/lastbackup
 EOF
 
 test_expect_success 'show' '
@@ -144,9 +146,11 @@ cat > test/expect << EOF
   Remote branch merged with 'git pull' while on branch master
     master
   Tracked remote branches
-    master side
+    master
+    side
   Local branches pushed with 'git push'
-    master:upstream +refs/tags/lastbackup
+    master:upstream
+    +refs/tags/lastbackup
 EOF
 
 test_expect_success 'show -n' '
@@ -188,7 +192,7 @@ test_expect_success 'prune --dry-run' '
 test_expect_success 'add --mirror && prune' '
        (mkdir mirror &&
         cd mirror &&
-        git init &&
+        git init --bare &&
         git remote add --mirror -f origin ../one) &&
        (cd one &&
         git branch -m side2 side) &&
@@ -324,4 +328,52 @@ test_expect_success 'reject adding remote with an invalid name' '
 
 '
 
+# The first three test if the tracking branches are properly renamed,
+# the last two ones check if the config is updated.
+
+test_expect_success 'rename a remote' '
+
+       git clone one four &&
+       (cd four &&
+        git remote rename origin upstream &&
+        rmdir .git/refs/remotes/origin &&
+        test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+        test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+        test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+        test "$(git config branch.master.remote)" = "upstream")
+
+'
+
+cat > remotes_origin << EOF
+URL: $(pwd)/one
+Push: refs/heads/master:refs/heads/upstream
+Pull: refs/heads/master:refs/heads/origin
+EOF
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
+       git clone one five &&
+       origin_url=$(pwd)/one &&
+       (cd five &&
+        git remote rm origin &&
+        mkdir -p .git/remotes &&
+        cat ../remotes_origin > .git/remotes/origin &&
+        git remote rename origin origin &&
+        ! test -f .git/remotes/origin &&
+        test "$(git config remote.origin.url)" = "$origin_url" &&
+        test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
+        test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+'
+
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
+       git clone one six &&
+       origin_url=$(pwd)/one &&
+       (cd six &&
+        git remote rm origin &&
+        echo "$origin_url" > .git/branches/origin &&
+        git remote rename origin origin &&
+        ! test -f .git/branches/origin &&
+        test "$(git config remote.origin.url)" = "$origin_url" &&
+        test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+'
+
 test_done
index 9aae4965dac0f2bf4bb8684c4b8d43dcb60529e9..9e679b402dc826185377c431d353decd8a8a2bed 100755 (executable)
@@ -323,4 +323,16 @@ test_expect_success 'auto tag following fetches minimum' '
        )
 '
 
+test_expect_success 'refuse to fetch into the current branch' '
+
+       test_must_fail git fetch . side:master
+
+'
+
+test_expect_success 'fetch into the current branch with --update-head-ok' '
+
+       git fetch --update-head-ok . side:master
+
+'
+
 test_done
index f0030ad00e4a6478fcb3ccfc503e576bd58003bd..4426df9226535c55eacff80217c4ab70f77639b6 100755 (executable)
@@ -39,6 +39,11 @@ mk_test () {
        )
 }
 
+mk_child() {
+       rm -rf "$1" &&
+       git clone testrepo "$1"
+}
+
 check_push_result () {
        (
                cd testrepo &&
@@ -425,29 +430,47 @@ test_expect_success 'push with dry-run' '
 
 test_expect_success 'push updates local refs' '
 
-       rm -rf parent child &&
-       mkdir parent &&
-       (cd parent && git init &&
-               echo one >foo && git add foo && git commit -m one) &&
-       git clone parent child &&
+       mk_test heads/master &&
+       mk_child child &&
        (cd child &&
-               echo two >foo && git commit -a -m two &&
+               git pull .. master &&
+               git push &&
+       test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
+
+'
+
+test_expect_success 'push updates up-to-date local refs' '
+
+       mk_test heads/master &&
+       mk_child child1 &&
+       mk_child child2 &&
+       (cd child1 && git pull .. master && git push) &&
+       (cd child2 &&
+               git pull ../child1 master &&
                git push &&
        test $(git rev-parse master) = $(git rev-parse remotes/origin/master))
 
 '
 
+test_expect_success 'push preserves up-to-date packed refs' '
+
+       mk_test heads/master &&
+       mk_child child &&
+       (cd child &&
+               git push &&
+       ! test -f .git/refs/remotes/origin/master)
+
+'
+
 test_expect_success 'push does not update local refs on failure' '
 
-       rm -rf parent child &&
-       mkdir parent &&
-       (cd parent && git init &&
-               echo one >foo && git add foo && git commit -m one &&
-               echo exit 1 >.git/hooks/pre-receive &&
-               chmod +x .git/hooks/pre-receive) &&
-       git clone parent child &&
+       mk_test heads/master &&
+       mk_child child &&
+       mkdir testrepo/.git/hooks &&
+       echo exit 1 >testrepo/.git/hooks/pre-receive &&
+       chmod +x testrepo/.git/hooks/pre-receive &&
        (cd child &&
-               echo two >foo && git commit -a -m two &&
+               git pull .. master
                test_must_fail git push &&
                test $(git rev-parse master) != \
                        $(git rev-parse remotes/origin/master))
@@ -456,11 +479,98 @@ test_expect_success 'push does not update local refs on failure' '
 
 test_expect_success 'allow deleting an invalid remote ref' '
 
-       pwd &&
+       mk_test heads/master &&
        rm -f testrepo/.git/objects/??/* &&
        git push testrepo :refs/heads/master &&
        (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
 
 '
 
+test_expect_success 'warn on push to HEAD of non-bare repository' '
+       mk_test heads/master
+       (cd testrepo &&
+               git checkout master &&
+               git config receive.denyCurrentBranch warn) &&
+       git push testrepo master 2>stderr &&
+       grep "warning.*this may cause confusion" stderr
+'
+
+test_expect_success 'deny push to HEAD of non-bare repository' '
+       mk_test heads/master
+       (cd testrepo &&
+               git checkout master &&
+               git config receive.denyCurrentBranch true) &&
+       test_must_fail git push testrepo master
+'
+
+test_expect_success 'allow push to HEAD of bare repository (bare)' '
+       mk_test heads/master
+       (cd testrepo &&
+               git checkout master &&
+               git config receive.denyCurrentBranch true &&
+               git config core.bare true) &&
+       git push testrepo master 2>stderr &&
+       ! grep "warning.*this may cause confusion" stderr
+'
+
+test_expect_success 'allow push to HEAD of non-bare repository (config)' '
+       mk_test heads/master
+       (cd testrepo &&
+               git checkout master &&
+               git config receive.denyCurrentBranch false
+       ) &&
+       git push testrepo master 2>stderr &&
+       ! grep "warning.*this may cause confusion" stderr
+'
+
+test_expect_success 'fetch with branches' '
+       mk_empty &&
+       git branch second $the_first_commit &&
+       git checkout second &&
+       echo ".." > testrepo/.git/branches/branch1 &&
+       (cd testrepo &&
+               git fetch branch1 &&
+               r=$(git show-ref -s --verify refs/heads/branch1) &&
+               test "z$r" = "z$the_commit" &&
+               test 1 = $(git for-each-ref refs/heads | wc -l)
+       ) &&
+       git checkout master
+'
+
+test_expect_success 'fetch with branches containing #' '
+       mk_empty &&
+       echo "..#second" > testrepo/.git/branches/branch2 &&
+       (cd testrepo &&
+               git fetch branch2 &&
+               r=$(git show-ref -s --verify refs/heads/branch2) &&
+               test "z$r" = "z$the_first_commit" &&
+               test 1 = $(git for-each-ref refs/heads | wc -l)
+       ) &&
+       git checkout master
+'
+
+test_expect_success 'push with branches' '
+       mk_empty &&
+       git checkout second &&
+       echo "testrepo" > .git/branches/branch1 &&
+       git push branch1 &&
+       (cd testrepo &&
+               r=$(git show-ref -s --verify refs/heads/master) &&
+               test "z$r" = "z$the_first_commit" &&
+               test 1 = $(git for-each-ref refs/heads | wc -l)
+       )
+'
+
+test_expect_success 'push with branches containing #' '
+       mk_empty &&
+       echo "testrepo#branch3" > .git/branches/branch2 &&
+       git push branch2 &&
+       (cd testrepo &&
+               r=$(git show-ref -s --verify refs/heads/branch3) &&
+               test "z$r" = "z$the_first_commit" &&
+               test 1 = $(git for-each-ref refs/heads | wc -l)
+       ) &&
+       git checkout master
+'
+
 test_done
index 997b2db827c4f37512c6b5d2f861e12105e2a32d..725771fac167ea5aac8cf65b916c96918b0f5e0d 100755 (executable)
@@ -29,6 +29,18 @@ test_expect_success 'checking the results' '
        diff file cloned/file
 '
 
+test_expect_success 'pulling into void using master:master' '
+       mkdir cloned-uho &&
+       (
+               cd cloned-uho &&
+               git init &&
+               git pull .. master:master
+       ) &&
+       test -f file &&
+       test -f cloned-uho/file &&
+       test_cmp file cloned-uho/file
+'
+
 test_expect_success 'test . as a remote' '
 
        git branch copy master &&
index 328e4d9a339101d04bb22e441a8955f6c2e1f260..27825f5f31ca2310e08fd2555589aa878728b292 100755 (executable)
@@ -19,4 +19,17 @@ test_expect_success 'clone -o' '
 
 '
 
+test_expect_success 'redirected clone' '
+
+       git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
+       test ! -s err
+
+'
+test_expect_success 'redirected clone -v' '
+
+       git clone -v "file://$(pwd)/parent" clone-redirected-v >out 2>err &&
+       test -s err
+
+'
+
 test_done
index 53892a555ce2e4a51db15066771f217a135e15e9..433c4de08f0cc8d220d5368ab2ab0dffde372482 100755 (executable)
@@ -18,11 +18,11 @@ git add file &&
 git commit -m initial &&
 git branch b-symlink &&
 git branch b-file &&
-l=$(echo -n file | git hash-object -t blob -w --stdin) &&
+l=$(printf file | git hash-object -t blob -w --stdin) &&
 echo "120000 $l        symlink" | git update-index --index-info &&
 git commit -m master &&
 git checkout b-symlink &&
-l=$(echo -n file-different | git hash-object -t blob -w --stdin) &&
+l=$(printf file-different | git hash-object -t blob -w --stdin) &&
 echo "120000 $l        symlink" | git update-index --index-info &&
 git commit -m b-symlink &&
 git checkout b-file &&
index 16cc63581383360d6dc92a69f646890eb00e580a..e6c9e59b617f4eaaa1148ed29b4ca92116bdf0c2 100755 (executable)
@@ -91,10 +91,10 @@ check_describe D-* HEAD^^
 check_describe A-* HEAD^^2
 check_describe B HEAD^^2^
 
-check_describe A-* --tags HEAD
-check_describe A-* --tags HEAD^
-check_describe D-* --tags HEAD^^
-check_describe A-* --tags HEAD^^2
+check_describe c-* --tags HEAD
+check_describe c-* --tags HEAD^
+check_describe e-* --tags HEAD^^
+check_describe c-* --tags HEAD^^2
 check_describe B --tags HEAD^^2^
 
 check_describe B-0-* --long HEAD^^2^
index f0edbf1a76739177943006461b853ec17e8bbb2b..f377fea4bb1d32c95096defb9fb08b291b67dbff 100755 (executable)
@@ -1090,4 +1090,15 @@ test_expect_success 'filename for the message is relative to cwd' '
        git cat-file tag tag-from-subdir-2 | grep "in sub directory"
 '
 
+# mixing modes and options:
+
+test_expect_success 'mixing incompatibles modes and options is forbidden' '
+       test_must_fail git tag -a
+       test_must_fail git tag -l -v
+       test_must_fail git tag -n 100
+       test_must_fail git tag -l -m msg
+       test_must_fail git tag -l -F some file
+       test_must_fail git tag -v -s
+'
+
 test_done
index 82769b89fc79d91982420ddfc83ce6c3dd672095..0e21632f19e75e1e7bbca1ceb2b9402a4d291584 100755 (executable)
@@ -330,12 +330,26 @@ test_expect_success \
     test "$(git config branch.track2.merge)"
     git config branch.autosetupmerge false'
 
-test_expect_success \
-    'checkout w/--track from non-branch HEAD fails' '
-    git checkout -b delete-me master &&
-    rm .git/refs/heads/delete-me &&
-    test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
-    test_must_fail git checkout --track -b track'
+test_expect_success 'checkout w/--track from non-branch HEAD fails' '
+    git checkout master^0 &&
+    test_must_fail git symbolic-ref HEAD &&
+    test_must_fail git checkout --track -b track &&
+    test_must_fail git rev-parse --verify track &&
+    test_must_fail git symbolic-ref HEAD &&
+    test "z$(git rev-parse master^0)" = "z$(git rev-parse HEAD)"
+'
+
+test_expect_success 'detach a symbolic link HEAD' '
+    git checkout master &&
+    git config --bool core.prefersymlinkrefs yes &&
+    git checkout side &&
+    git checkout master &&
+    it=$(git symbolic-ref HEAD) &&
+    test "z$it" = zrefs/heads/master &&
+    here=$(git rev-parse --verify refs/heads/master) &&
+    git checkout side^ &&
+    test "z$(git rev-parse --verify refs/heads/master)" = "z$here"
+'
 
 test_expect_success \
     'checkout with --track fakes a sensible -b <name>' '
index 3eb9faedcf75c7b8a535b369e5a19107c6e81026..ad42c78d7c21497a6ebb4e9cef4efd6334f947da 100755 (executable)
@@ -89,6 +89,14 @@ test_expect_success 'verbose' '
 
 '
 
+test_expect_success 'verbose respects diff config' '
+
+       git config color.diff always &&
+       git status -v >actual &&
+       grep "\[1mdiff --git" actual &&
+       git config --unset color.diff
+'
+
 test_expect_success 'cleanup commit messages (verbatim,-t)' '
 
        echo >>negative &&
index 1905fb34cd222aa9443d28fd673b0cc9e02f3335..93f875f50054225e0879843b6e29ac057ba6c020 100755 (executable)
@@ -292,6 +292,12 @@ test_expect_success 'status submodule summary is disabled by default' '
        test_cmp expect output
 '
 
+# we expect the same as the previous test
+test_expect_success 'status --untracked-files=all does not show submodule' '
+       git status --untracked-files=all >output &&
+       test_cmp expect output
+'
+
 head=$(cd sm && git rev-parse --short=7 --verify HEAD)
 
 cat >expect <<EOF
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
new file mode 100755 (executable)
index 0000000..519adba
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='verbose commit template'
+. ./test-lib.sh
+
+cat >check-for-diff <<EOF
+#!$SHELL_PATH
+exec grep '^diff --git' "\$1"
+EOF
+chmod +x check-for-diff
+test_set_editor "$PWD/check-for-diff"
+
+cat >message <<'EOF'
+subject
+
+body
+EOF
+
+test_expect_success 'setup' '
+       echo content >file &&
+       git add file &&
+       git commit -F message
+'
+
+test_expect_failure 'initial commit shows verbose diff' '
+       git commit --amend -v
+'
+
+test_expect_success 'second commit' '
+       echo content modified >file &&
+       git add file &&
+       git commit -F message
+'
+
+check_message() {
+       git log -1 --pretty=format:%s%n%n%b >actual &&
+       test_cmp "$1" actual
+}
+
+test_expect_success 'verbose diff is stripped out' '
+       git commit --amend -v &&
+       check_message message
+'
+
+test_expect_success 'verbose diff is stripped out (mnemonicprefix)' '
+       git config diff.mnemonicprefix true &&
+       git commit --amend -v &&
+       check_message message
+'
+
+cat >diff <<'EOF'
+This is an example commit message that contains a diff.
+
+diff --git c/file i/file
+new file mode 100644
+index 0000000..f95c11d
+--- /dev/null
++++ i/file
+@@ -0,0 +1 @@
++this is some content
+EOF
+
+test_expect_success 'diff in message is retained without -v' '
+       git commit --amend -F diff &&
+       check_message diff
+'
+
+test_expect_failure 'diff in message is retained with -v' '
+       git commit --amend -F diff -v &&
+       check_message diff
+'
+
+test_done
index 7313ac278cb7cb1d741e49f261e65d929ddeffb7..e5b210bc960c8433d6758f3932a86647208297ef 100755 (executable)
@@ -518,6 +518,46 @@ test_expect_success 'refresh the index before merging' '
        git merge c3
 '
 
+cat >expected <<EOF
+Merge branch 'c5' (early part)
+EOF
+
+test_expect_success 'merge early part of c2' '
+       git reset --hard c3 &&
+       echo c4 > c4.c &&
+       git add c4.c &&
+       git commit -m c4 &&
+       git tag c4 &&
+       echo c5 > c5.c &&
+       git add c5.c &&
+       git commit -m c5 &&
+       git tag c5 &&
+       git reset --hard c3 &&
+       echo c6 > c6.c &&
+       git add c6.c &&
+       git commit -m c6 &&
+       git tag c6 &&
+       git merge c5~1 &&
+       git show -s --pretty=format:%s HEAD > actual &&
+       test_cmp actual expected
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'merge --no-ff --no-commit && commit' '
+       git reset --hard c0 &&
+       git merge --no-ff --no-commit c1 &&
+       EDITOR=: git commit &&
+       verify_parents $c0 $c1
+'
+
+test_debug 'gitk --all'
+
+test_expect_success 'amending no-ff merge commit' '
+       EDITOR=: git commit --amend &&
+       verify_parents $c0 $c1
+'
+
 test_debug 'gitk --all'
 
 test_done
diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh
new file mode 100755 (executable)
index 0000000..3f602ea
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='git repack works correctly'
+
+. ./test-lib.sh
+
+test_expect_success 'objects in packs marked .keep are not repacked' '
+       echo content1 > file1 &&
+       echo content2 > file2 &&
+       git add . &&
+       git commit -m initial_commit &&
+       # Create two packs
+       # The first pack will contain all of the objects except one
+       git rev-list --objects --all | grep -v file2 |
+               git pack-objects pack > /dev/null &&
+       # The second pack will contain the excluded object
+       packsha1=$(git rev-list --objects --all | grep file2 |
+               git pack-objects pack) &&
+       touch -r pack-$packsha1.pack pack-$packsha1.keep &&
+       objsha1=$(git verify-pack -v pack-$packsha1.idx | head -n 1 |
+               sed -e "s/^\([0-9a-f]\{40\}\).*/\1/") &&
+       mv pack-* .git/objects/pack/ &&
+       git repack -A -d -l &&
+       git prune-packed &&
+       for p in .git/objects/pack/*.idx; do
+               idx=$(basename $p)
+               test "pack-$packsha1.idx" = "$idx" && continue
+               if git verify-pack -v $p | egrep "^$objsha1"; then
+                       found_duplicate_object=1
+                       echo "DUPLICATE OBJECT FOUND"
+                       break
+               fi
+       done &&
+       test -z "$found_duplicate_object"
+'
+
+test_expect_success 'loose objects in alternate ODB are not repacked' '
+       mkdir alt_objects &&
+       echo `pwd`/alt_objects > .git/objects/info/alternates &&
+       echo content3 > file3 &&
+       objsha1=$(GIT_OBJECT_DIRECTORY=alt_objects git hash-object -w file3) &&
+       git add file3 &&
+       git commit -m commit_file3 &&
+       git repack -a -d -l &&
+       git prune-packed &&
+       for p in .git/objects/pack/*.idx; do
+               if git verify-pack -v $p | egrep "^$objsha1"; then
+                       found_duplicate_object=1
+                       echo "DUPLICATE OBJECT FOUND"
+                       break
+               fi
+       done &&
+       test -z "$found_duplicate_object"
+'
+
+test_expect_success 'packed obs in alt ODB are repacked even when local repo is packless' '
+       mkdir alt_objects/pack
+       mv .git/objects/pack/* alt_objects/pack &&
+       git repack -a &&
+       myidx=$(ls -1 .git/objects/pack/*.idx) &&
+       test -f "$myidx" &&
+       for p in alt_objects/pack/*.idx; do
+               git verify-pack -v $p | sed -n -e "/^[0-9a-f]\{40\}/p"
+       done | while read sha1 rest; do
+               if ! ( git verify-pack -v $myidx | grep "^$sha1" ); then
+                       echo "Missing object in local pack: $sha1"
+                       return 1
+               fi
+       done
+'
+
+test_done
+
diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh
new file mode 100755 (executable)
index 0000000..4470a92
--- /dev/null
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='git blame encoding conversion'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/t8005/utf8.txt
+. "$TEST_DIRECTORY"/t8005/cp1251.txt
+. "$TEST_DIRECTORY"/t8005/sjis.txt
+
+test_expect_success 'setup the repository' '
+       # Create the file
+       echo "UTF-8 LINE" > file &&
+       git add file &&
+       git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" &&
+
+       echo "CP1251 LINE" >> file &&
+       git add file &&
+       git config i18n.commitencoding cp1251 &&
+       git commit --author "$CP1251_NAME <cp1251@localhost>" -m "$CP1251_MSG" &&
+
+       echo "SJIS LINE" >> file &&
+       git add file &&
+       git config i18n.commitencoding shift-jis &&
+       git commit --author "$SJIS_NAME <sjis@localhost>" -m "$SJIS_MSG"
+'
+
+cat >expected <<EOF
+author $SJIS_NAME
+summary $SJIS_MSG
+author $SJIS_NAME
+summary $SJIS_MSG
+author $SJIS_NAME
+summary $SJIS_MSG
+EOF
+
+test_expect_success \
+       'blame respects i18n.commitencoding' '
+       git blame --incremental file | \
+               grep "^\(author\|summary\) " > actual &&
+       test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $CP1251_NAME
+summary $CP1251_MSG
+author $CP1251_NAME
+summary $CP1251_MSG
+author $CP1251_NAME
+summary $CP1251_MSG
+EOF
+
+test_expect_success \
+       'blame respects i18n.logoutputencoding' '
+       git config i18n.logoutputencoding cp1251 &&
+       git blame --incremental file | \
+               grep "^\(author\|summary\) " > actual &&
+       test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $UTF8_NAME
+summary $UTF8_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+EOF
+
+test_expect_success \
+       'blame respects --encoding=utf-8' '
+       git blame --incremental --encoding=utf-8 file | \
+               grep "^\(author\|summary\) " > actual &&
+       test_cmp actual expected
+'
+
+cat >expected <<EOF
+author $SJIS_NAME
+summary $SJIS_MSG
+author $CP1251_NAME
+summary $CP1251_MSG
+author $UTF8_NAME
+summary $UTF8_MSG
+EOF
+
+test_expect_success \
+       'blame respects --encoding=none' '
+       git blame --incremental --encoding=none file | \
+               grep "^\(author\|summary\) " > actual &&
+       test_cmp actual expected
+'
+
+test_done
diff --git a/t/t8005/cp1251.txt b/t/t8005/cp1251.txt
new file mode 100644 (file)
index 0000000..ce41e98
--- /dev/null
@@ -0,0 +1,2 @@
+CP1251_NAME="Èâàí Ïåòðîâè÷ Ñèäîðîâ"
+CP1251_MSG="Òåñòîâîå ñîîáùåíèå"
diff --git a/t/t8005/sjis.txt b/t/t8005/sjis.txt
new file mode 100644 (file)
index 0000000..2ccfbad
--- /dev/null
@@ -0,0 +1,2 @@
+SJIS_NAME="\84I\84r\84p\84\84P\84u\84\84\84\82\84\80\84r\84y\84\89 \84R\84y\84t\84\80\84\82\84\80\84r"
+SJIS_MSG="\84S\84u\84\83\84\84\84\80\84r\84\80\84\84\83\84\80\84\80\84q\84\8b\84u\84~\84y\84u"
diff --git a/t/t8005/utf8.txt b/t/t8005/utf8.txt
new file mode 100644 (file)
index 0000000..f46cfc5
--- /dev/null
@@ -0,0 +1,2 @@
+UTF8_NAME="Иван Петрович Сидоров"
+UTF8_MSG="Тестовое сообщение"
index 328444a3068f5083e3d64e92e88660c724acffdc..91b5aced1bcea74a66c3365e4aceff355d577f6a 100755 (executable)
@@ -983,7 +983,7 @@ test_expect_success \
         git checkout subuse1 &&
         rm -rf sub && mkdir sub && cd sub &&
         git init &&
-        git fetch .. refs/heads/sub:refs/heads/master &&
+        git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
         git checkout master &&
         cd .. &&
         git submodule init &&
index c1850d29239f8846c42b4552493d2d4898fa221e..6a37f71d11800f92a117bfdcf38172bfc9000d77 100755 (executable)
@@ -424,7 +424,7 @@ cd "$WORKDIR"
 test_expect_success 'cvs update (-p)' '
     touch really-empty &&
     echo Line 1 > no-lf &&
-    echo -n Line 2 >> no-lf &&
+    printf "Line 2" >> no-lf &&
     git add really-empty no-lf &&
     git commit -q -m "Update -p test" &&
     git push gitcvs.git >/dev/null &&
index 90da448ebec3e5375b7725ba7f297c1c74199b87..d5358cbaac2022483b74366555fc9707a7d8ad97 100644 (file)
@@ -1,39 +1,83 @@
+/*
+ * This program can either change modification time of the given
+ * file(s) or just print it. The program does not change atime nor
+ * ctime (their values are explicitely preserved).
+ *
+ * The mtime can be changed to an absolute value:
+ *
+ *     test-chmtime =<seconds> file...
+ *
+ * Relative to the current time as returned by time(3):
+ *
+ *     test-chmtime =+<seconds> (or =-<seconds>) file...
+ *
+ * Or relative to the current mtime of the file:
+ *
+ *     test-chmtime <seconds> file...
+ *     test-chmtime +<seconds> (or -<seconds>) file...
+ *
+ * Examples:
+ *
+ * To just print the mtime use --verbose and set the file mtime offset to 0:
+ *
+ *     test-chmtime -v +0 file
+ *
+ * To set the mtime to current time:
+ *
+ *     test-chmtime =+0 file
+ *
+ */
 #include "git-compat-util.h"
 #include <utime.h>
 
-static const char usage_str[] = "(+|=|=+|=-|-)<seconds> <file>...";
+static const char usage_str[] = "-v|--verbose (+|=|=+|=-|-)<seconds> <file>...";
 
-int main(int argc, const char *argv[])
+static int timespec_arg(const char *arg, long int *set_time, int *set_eq)
 {
-       int i;
-       int set_eq;
-       long int set_time;
        char *test;
-       const char *timespec;
-
-       if (argc < 3)
-               goto usage;
-
-       timespec = argv[1];
-       set_eq = (*timespec == '=') ? 1 : 0;
-       if (set_eq) {
+       const char *timespec = arg;
+       *set_eq = (*timespec == '=') ? 1 : 0;
+       if (*set_eq) {
                timespec++;
                if (*timespec == '+') {
-                       set_eq = 2; /* relative "in the future" */
+                       *set_eq = 2; /* relative "in the future" */
                        timespec++;
                }
        }
-       set_time = strtol(timespec, &test, 10);
+       *set_time = strtol(timespec, &test, 10);
        if (*test) {
-               fprintf(stderr, "Not a base-10 integer: %s\n", argv[1] + 1);
-               goto usage;
+               fprintf(stderr, "Not a base-10 integer: %s\n", arg + 1);
+               return 0;
        }
-       if ((set_eq && set_time < 0) || set_eq == 2) {
+       if ((*set_eq && *set_time < 0) || *set_eq == 2) {
                time_t now = time(NULL);
-               set_time += now;
+               *set_time += now;
        }
+       return 1;
+}
+
+int main(int argc, const char *argv[])
+{
+       static int verbose;
 
-       for (i = 2; i < argc; i++) {
+       int i = 1;
+       /* no mtime change by default */
+       int set_eq = 0;
+       long int set_time = 0;
+
+       if (argc < 3)
+               goto usage;
+
+       if (strcmp(argv[i], "--verbose") == 0 || strcmp(argv[i], "-v") == 0) {
+               verbose = 1;
+               ++i;
+       }
+       if (timespec_arg(argv[i], &set_time, &set_eq))
+               ++i;
+       else
+               goto usage;
+
+       for (; i < argc; i++) {
                struct stat sb;
                struct utimbuf utb;
 
@@ -46,7 +90,12 @@ int main(int argc, const char *argv[])
                utb.actime = sb.st_atime;
                utb.modtime = set_eq ? set_time : sb.st_mtime + set_time;
 
-               if (utime(argv[i], &utb) < 0) {
+               if (verbose) {
+                       uintmax_t mtime = utb.modtime < 0 ? 0: utb.modtime;
+                       printf("%"PRIuMAX"\t%s\n", mtime, argv[i]);
+               }
+
+               if (utb.modtime != sb.st_mtime && utime(argv[i], &utb) < 0) {
                        fprintf(stderr, "Failed to modify time on %s: %s\n",
                                argv[i], strerror(errno));
                        return -1;
index 5110c56c4e80dc224ff3308e643fad6d5ed77ffb..56831c57c5cf6500714c46c63581ca98662d58c3 100644 (file)
@@ -75,7 +75,7 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
 
                        if (fd < 0)
                                continue;
-                       next = alloc_ref(path->len - name_offset + 1);
+                       next = alloc_ref(path->buf + name_offset);
                        if (read_in_full(fd, buffer, 40) != 40 ||
                                        get_sha1_hex(buffer, next->old_sha1)) {
                                close(fd);
@@ -83,7 +83,6 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
                                continue;
                        }
                        close(fd);
-                       strcpy(next->name, path->buf + name_offset);
                        (*tail)->next = next;
                        *tail = next;
                }
@@ -127,14 +126,13 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
                                      (*list)->next->name)) > 0)
                        list = &(*list)->next;
                if (!(*list)->next || cmp < 0) {
-                       struct ref *next = alloc_ref(len - 40);
+                       struct ref *next = alloc_ref(buffer + 41);
                        buffer[40] = '\0';
                        if (get_sha1_hex(buffer, next->old_sha1)) {
                                warning ("invalid SHA-1: %s", buffer);
                                free(next);
                                continue;
                        }
-                       strcpy(next->name, buffer + 41);
                        next->next = (*list)->next;
                        (*list)->next = next;
                        list = &(*list)->next;
@@ -501,7 +499,7 @@ static struct ref *get_refs_via_curl(struct transport *transport)
 
        strbuf_release(&buffer);
 
-       ref = alloc_ref_from_str("HEAD");
+       ref = alloc_ref("HEAD");
        if (!walker->fetch_ref(walker, ref) &&
            !resolve_remote_symref(ref, refs)) {
                ref->next = refs;
@@ -542,7 +540,7 @@ 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_from_str(e->name);
+               struct ref *ref = alloc_ref(e->name);
                hashcpy(ref->old_sha1, e->sha1);
                ref->next = result;
                result = ref;
@@ -644,7 +642,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        args.include_tag = data->followtags;
        args.verbose = (transport->verbose > 0);
        args.quiet = (transport->verbose < 0);
-       args.no_progress = args.quiet || !isatty(1);
+       args.no_progress = args.quiet || (!transport->progress && !isatty(1));
        args.depth = data->depth;
 
        for (i = 0; i < nr_heads; i++)
index d0b52053fff9bc463438674232bffb6024f3b1fc..6bbc1a82642ab9e5722cfe6ab34ec4246b3a9dd4 100644 (file)
@@ -25,6 +25,8 @@ struct transport {
        int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
        signed verbose : 2;
+       /* Force progress even if the output is not a tty */
+       unsigned progress : 1;
 };
 
 #define TRANSPORT_PUSH_ALL 1
index e59d144d28164f2451784513105f6269f0e9167c..54f301da67be879c80426bc21776427fdd38c02e 100644 (file)
@@ -352,7 +352,7 @@ static int unpack_failed(struct unpack_trees_options *o, const char *message)
        discard_index(&o->result);
        if (!o->gently) {
                if (message)
-                       return error(message);
+                       return error("%s", message);
                return -1;
        }
        return -1;
@@ -382,7 +382,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        o->merge_size = len;
 
        if (!dfc)
-               dfc = xcalloc(1, sizeof(struct cache_entry) + 1);
+               dfc = xcalloc(1, cache_entry_size(0));
        o->df_conflict_entry = dfc;
 
        if (len) {
diff --git a/userdiff.c b/userdiff.c
new file mode 100644 (file)
index 0000000..3681062
--- /dev/null
@@ -0,0 +1,167 @@
+#include "userdiff.h"
+#include "cache.h"
+#include "attr.h"
+
+static struct userdiff_driver *drivers;
+static int ndrivers;
+static int drivers_alloc;
+
+#define FUNCNAME(name, pattern) \
+       { name, NULL, -1, { pattern, REG_EXTENDED } }
+static struct userdiff_driver builtin_drivers[] = {
+FUNCNAME("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$"),
+FUNCNAME("java",
+        "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
+        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$"),
+FUNCNAME("objc",
+        /* Negate C statements that can look like functions */
+        "!^[ \t]*(do|for|if|else|return|switch|while)\n"
+        /* Objective-C methods */
+        "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
+        /* C functions */
+        "^[ \t]*(([ \t]*[A-Za-z_][A-Za-z_0-9]*){2,}[ \t]*\\([^;]*)$\n"
+        /* Objective-C class/protocol definitions */
+        "^(@(implementation|interface|protocol)[ \t].*)$"),
+FUNCNAME("pascal",
+        "^((procedure|function|constructor|destructor|interface|"
+               "implementation|initialization|finalization)[ \t]*.*)$"
+        "\n"
+        "^(.*=[ \t]*(class|record).*)$"),
+FUNCNAME("php", "^[\t ]*((function|class).*)"),
+FUNCNAME("python", "^[ \t]*((class|def)[ \t].*)$"),
+FUNCNAME("ruby", "^[ \t]*((class|module|def)[ \t].*)$"),
+FUNCNAME("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$"),
+FUNCNAME("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$"),
+{ "default", NULL, -1, { NULL, 0 } },
+};
+#undef FUNCNAME
+
+static struct userdiff_driver driver_true = {
+       "diff=true",
+       NULL,
+       0,
+       { NULL, 0 }
+};
+
+static struct userdiff_driver driver_false = {
+       "!diff",
+       NULL,
+       1,
+       { NULL, 0 }
+};
+
+static struct userdiff_driver *userdiff_find_by_namelen(const char *k, int len)
+{
+       int i;
+       for (i = 0; i < ndrivers; i++) {
+               struct userdiff_driver *drv = drivers + i;
+               if (!strncmp(drv->name, k, len) && !drv->name[len])
+                       return drv;
+       }
+       for (i = 0; i < ARRAY_SIZE(builtin_drivers); i++) {
+               struct userdiff_driver *drv = builtin_drivers + i;
+               if (!strncmp(drv->name, k, len) && !drv->name[len])
+                       return drv;
+       }
+       return NULL;
+}
+
+static struct userdiff_driver *parse_driver(const char *var,
+               const char *value, const char *type)
+{
+       struct userdiff_driver *drv;
+       const char *dot;
+       const char *name;
+       int namelen;
+
+       if (prefixcmp(var, "diff."))
+               return NULL;
+       dot = strrchr(var, '.');
+       if (dot == var + 4)
+               return NULL;
+       if (strcmp(type, dot+1))
+               return NULL;
+
+       name = var + 5;
+       namelen = dot - name;
+       drv = userdiff_find_by_namelen(name, namelen);
+       if (!drv) {
+               ALLOC_GROW(drivers, ndrivers+1, drivers_alloc);
+               drv = &drivers[ndrivers++];
+               memset(drv, 0, sizeof(*drv));
+               drv->name = xmemdupz(name, namelen);
+               drv->binary = -1;
+       }
+       return drv;
+}
+
+static int parse_funcname(struct userdiff_funcname *f, const char *k,
+               const char *v, int cflags)
+{
+       if (git_config_string(&f->pattern, k, v) < 0)
+               return -1;
+       f->cflags = cflags;
+       return 1;
+}
+
+static int parse_string(const char **d, const char *k, const char *v)
+{
+       if (git_config_string(d, k, v) < 0)
+               return -1;
+       return 1;
+}
+
+static int parse_tristate(int *b, const char *k, const char *v)
+{
+       if (v && !strcasecmp(v, "auto"))
+               *b = -1;
+       else
+               *b = git_config_bool(k, v);
+       return 1;
+}
+
+int userdiff_config(const char *k, const char *v)
+{
+       struct userdiff_driver *drv;
+
+       if ((drv = parse_driver(k, v, "funcname")))
+               return parse_funcname(&drv->funcname, k, v, 0);
+       if ((drv = parse_driver(k, v, "xfuncname")))
+               return parse_funcname(&drv->funcname, k, v, REG_EXTENDED);
+       if ((drv = parse_driver(k, v, "binary")))
+               return parse_tristate(&drv->binary, k, v);
+       if ((drv = parse_driver(k, v, "command")))
+               return parse_string(&drv->external, k, v);
+       if ((drv = parse_driver(k, v, "textconv")))
+               return parse_string(&drv->textconv, k, v);
+
+       return 0;
+}
+
+struct userdiff_driver *userdiff_find_by_name(const char *name) {
+       int len = strlen(name);
+       return userdiff_find_by_namelen(name, len);
+}
+
+struct userdiff_driver *userdiff_find_by_path(const char *path)
+{
+       static struct git_attr *attr;
+       struct git_attr_check check;
+
+       if (!attr)
+               attr = git_attr("diff", 4);
+       check.attr = attr;
+
+       if (!path)
+               return NULL;
+       if (git_checkattr(path, 1, &check))
+               return NULL;
+
+       if (ATTR_TRUE(check.value))
+               return &driver_true;
+       if (ATTR_FALSE(check.value))
+               return &driver_false;
+       if (ATTR_UNSET(check.value))
+               return NULL;
+       return userdiff_find_by_name(check.value);
+}
diff --git a/userdiff.h b/userdiff.h
new file mode 100644 (file)
index 0000000..ba29457
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef USERDIFF_H
+#define USERDIFF_H
+
+struct userdiff_funcname {
+       const char *pattern;
+       int cflags;
+};
+
+struct userdiff_driver {
+       const char *name;
+       const char *external;
+       int binary;
+       struct userdiff_funcname funcname;
+       const char *textconv;
+};
+
+int userdiff_config(const char *k, const char *v);
+struct userdiff_driver *userdiff_find_by_name(const char *name);
+struct userdiff_driver *userdiff_find_by_path(const char *path);
+
+#endif /* USERDIFF */
index 6b4cf70c6af1289501f72cbde78cd04da57293f3..679adab6a0fccef460acaf90b116b8c6cb9bd460 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -191,7 +191,7 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
        if (!get_sha1_hex(target, sha1))
                return 0;
        if (!check_ref_format(target)) {
-               struct ref *ref = alloc_ref_from_str(target);
+               struct ref *ref = alloc_ref(target);
                if (!walker->fetch_ref(walker, ref)) {
                        hashcpy(sha1, ref->old_sha1);
                        free(ref);
index d2eac36aeaafab2800b4f0802c7db9656c44cdeb..6a7645ed86fd5879e959460011a8add015d392d9 100644 (file)
@@ -277,20 +277,9 @@ static void wt_status_print_untracked(struct wt_status *s)
 
        read_directory(&dir, ".", "", 0, NULL);
        for(i = 0; i < dir.nr; i++) {
-               /* check for matching entry, which is unmerged; lifted from
-                * builtin-ls-files:show_other_files */
                struct dir_entry *ent = dir.entries[i];
-               int pos = cache_name_pos(ent->name, ent->len);
-               struct cache_entry *ce;
-               if (0 <= pos)
-                       die("bug in wt_status_print_untracked");
-               pos = -pos - 1;
-               if (pos < active_nr) {
-                       ce = active_cache[pos];
-                       if (ce_namelen(ce) == ent->len &&
-                           !memcmp(ce->name, ent->name, ent->len))
-                               continue;
-               }
+               if (!cache_name_is_other(ent->name, ent->len))
+                       continue;
                if (!shown_header) {
                        s->workdir_untracked = 1;
                        wt_status_print_untracked_header(s);
@@ -312,8 +301,17 @@ static void wt_status_print_verbose(struct wt_status *s)
        setup_revisions(0, NULL, &rev, s->reference);
        rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
        rev.diffopt.detect_rename = 1;
+       DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
        rev.diffopt.file = s->fp;
        rev.diffopt.close_file = 0;
+       /*
+        * If we're not going to stdout, then we definitely don't
+        * want color, since we are going to the commit message
+        * file (and even the "auto" setting won't work, since it
+        * will have checked isatty on stdout).
+        */
+       if (s->fp != stdout)
+               DIFF_OPT_CLR(&rev.diffopt, COLOR_DIFF);
        run_diff_index(&rev, 1);
 }
 
@@ -433,5 +431,5 @@ int git_status_config(const char *k, const char *v, void *cb)
                        return error("Invalid untracked files mode '%s'", v);
                return 0;
        }
-       return git_color_default_config(k, v, cb);
+       return git_diff_ui_config(k, v, cb);
 }
index f3f6db3297e4fd367d7af05a4755037aaf2aaf42..e8ef46d10dc53a1ee1781d07685984734bc3160f 100644 (file)
@@ -1,6 +1,9 @@
 #include "cache.h"
 #include "xdiff-interface.h"
-#include "strbuf.h"
+#include "xdiff/xtypes.h"
+#include "xdiff/xdiffi.h"
+#include "xdiff/xemit.h"
+#include "xdiff/xmacros.h"
 
 struct xdiff_emit_state {
        xdiff_emit_consume_fn consume;
@@ -153,6 +156,50 @@ int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
        return ret;
 }
 
+struct xdiff_emit_hunk_state {
+       xdiff_emit_hunk_consume_fn consume;
+       void *consume_callback_data;
+};
+
+static int process_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+                       xdemitconf_t const *xecfg)
+{
+       long s1, s2, same, p_next, t_next;
+       xdchange_t *xch, *xche;
+       struct xdiff_emit_hunk_state *state = ecb->priv;
+       xdiff_emit_hunk_consume_fn fn = state->consume;
+       void *consume_callback_data = state->consume_callback_data;
+
+       for (xch = xscr; xch; xch = xche->next) {
+               xche = xdl_get_hunk(xch, xecfg);
+
+               s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
+               s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
+               same = s2 + XDL_MAX(xch->i1 - s1, 0);
+               p_next = xche->i1 + xche->chg1;
+               t_next = xche->i2 + xche->chg2;
+
+               fn(consume_callback_data, same, p_next, t_next);
+       }
+       return 0;
+}
+
+int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
+                  xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
+                  xpparam_t const *xpp, xdemitconf_t *xecfg)
+{
+       struct xdiff_emit_hunk_state state;
+       xdemitcb_t ecb;
+
+       memset(&state, 0, sizeof(state));
+       memset(&ecb, 0, sizeof(ecb));
+       state.consume = fn;
+       state.consume_callback_data = consume_callback_data;
+       xecfg->emit_func = (void (*)())process_diff;
+       ecb.priv = &state;
+       return xdi_diff(mf1, mf2, xpp, xecfg, &ecb);
+}
+
 int read_mmfile(mmfile_t *ptr, const char *filename)
 {
        struct stat st;
@@ -207,6 +254,16 @@ static long ff_regexp(const char *line, long len,
 
        line_buffer = xstrndup(line, len); /* make NUL terminated */
 
+       /* Exclude terminating newline (and cr) from matching */
+       if (len > 0 && line[len-1] == '\n') {
+               if (len > 1 && line[len-2] == '\r')
+                       len -= 2;
+               else
+                       len--;
+       }
+
+       line_buffer = xstrndup(line, len); /* make NUL terminated */
+
        for (i = 0; i < regs->nr; i++) {
                struct ff_reg *reg = regs->array + i;
                if (!regexec(&reg->re, line_buffer, 2, pmatch, 0)) {
index eaf9cd34981695fc8d12fd35d01ceae93b22254d..7352b9a9c204c2b1d4ca9df5ce040fe22d6f521c 100644 (file)
@@ -4,12 +4,16 @@
 #include "xdiff/xdiff.h"
 
 typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
+typedef void (*xdiff_emit_hunk_consume_fn)(void *, long, long, long);
 
 int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
                  xdiff_emit_consume_fn fn, void *consume_callback_data,
                  xpparam_t const *xpp,
                  xdemitconf_t const *xecfg, xdemitcb_t *xecb);
+int xdi_diff_hunks(mmfile_t *mf1, mmfile_t *mf2,
+                  xdiff_emit_hunk_consume_fn fn, void *consume_callback_data,
+                  xpparam_t const *xpp, xdemitconf_t *xecfg);
 int parse_hunk_header(char *line, int len,
                      int *ob, int *on,
                      int *nb, int *nn);
index deebe02cd3489ef3ce94b407ff9c3de4c21a0eb1..84fff583e2a6e3411da4260b9af98043fade1e49 100644 (file)
@@ -87,6 +87,7 @@ typedef struct s_xdemitconf {
        unsigned long flags;
        find_func_t find_func;
        void *find_func_priv;
+       void (*emit_func)();
 } xdemitconf_t;
 
 typedef struct s_bdiffparam {
index 1bad8462fb32cffdc9ff20a278d513e7a444b257..9d0324a38c2a1974648e67161fa0ed1b0f811233 100644 (file)
@@ -538,6 +538,8 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
             xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
        xdchange_t *xscr;
        xdfenv_t xe;
+       emit_func_t ef = xecfg->emit_func ?
+               (emit_func_t)xecfg->emit_func : xdl_emit_diff;
 
        if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
 
@@ -551,7 +553,7 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
                return -1;
        }
        if (xscr) {
-               if (xdl_emit_diff(&xe, xscr, ecb, xecfg) < 0) {
+               if (ef(&xe, xscr, ecb, xecfg) < 0) {
 
                        xdl_free_script(xscr);
                        xdl_free_env(&xe);
index d3d9c845c6420e4881636d779c7029f900a0b067..4625c1b4215231dc343478b2f4f7b4bfccf2c766 100644 (file)
@@ -27,7 +27,6 @@
 
 static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
 static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
-static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
 
 
 
@@ -58,7 +57,7 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
  * Starting at the passed change atom, find the latest change atom to be included
  * inside the differential hunk according to the specified configuration.
  */
-static xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
        xdchange_t *xch, *xchp;
 
        for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
index 440a7390fa4abb0411c336cfba616e3229484e86..c2e2e830273782dc597606ddbb0401c04dce8f8f 100644 (file)
 #define XEMIT_H
 
 
+typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+                          xdemitconf_t const *xecfg);
 
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
                  xdemitconf_t const *xecfg);
 
index e87ab57c652b56b1a684e2a0a56885c1d1b27ef7..a43aa72cd088af07b13a6bc8de304ecc178047e5 100644 (file)
 #include "xinclude.h"
 
 
-
 #define XDL_KPDIS_RUN 4
 #define XDL_MAX_EQLIMIT 1024
-
+#define XDL_SIMSCAN_WINDOW 100
 
 
 typedef struct s_xdlclass {
@@ -312,6 +311,18 @@ void xdl_free_env(xdfenv_t *xe) {
 static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
        long r, rdis0, rpdis0, rdis1, rpdis1;
 
+       /*
+        * Limits the window the is examined during the similar-lines
+        * scan. The loops below stops when dis[i - r] == 1 (line that
+        * has no match), but there are corner cases where the loop
+        * proceed all the way to the extremities by causing huge
+        * performance penalties in case of big files.
+        */
+       if (i - s > XDL_SIMSCAN_WINDOW)
+               s = i - XDL_SIMSCAN_WINDOW;
+       if (e - i > XDL_SIMSCAN_WINDOW)
+               e = i + XDL_SIMSCAN_WINDOW;
+
        /*
         * Scans the lines before 'i' to find a run of lines that either
         * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).