]> asedeno.scripts.mit.edu Git - git.git/commitdiff
Merge branch 'jn/update-contrib-example-merge'
authorJunio C Hamano <gitster@pobox.com>
Fri, 3 Sep 2010 16:43:42 +0000 (09:43 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 3 Sep 2010 16:43:42 +0000 (09:43 -0700)
* jn/update-contrib-example-merge: (24 commits)
  merge script: learn --[no-]rerere-autoupdate
  merge script: notice @{-1} shorthand
  merge script: handle --no-ff --no-commit correctly
  merge script: --ff-only to disallow true merge
  merge script: handle many-way octopus
  merge script: handle -m --log correctly
  merge script: forbid merge -s index
  merge script: allow custom strategies
  merge script: merge -X<option>
  merge script: improve log message subject
  merge script: refuse to merge during merge
  merge script: tweak unmerged files message to match builtin
  merge script: --squash, --ff from unborn branch are errors
  fmt-merge-msg -m to override merge title
  merge-base --independent to print reduced parent list in a merge
  merge-base --octopus to mimic show-branch --merge-base
  Documentation: add a SEE ALSO section for merge-base
  t6200 (fmt-merge-msg): style nitpicks
  t6010 (merge-base): modernize style
  t7600 (merge): test merge from branch yet to be born
  ...

292 files changed:
.gitignore
Documentation/RelNotes-1.5.6.3.txt
Documentation/RelNotes-1.6.0.2.txt
Documentation/RelNotes-1.6.4.3.txt
Documentation/RelNotes-1.6.5.4.txt
Documentation/RelNotes-1.6.5.7.txt
Documentation/RelNotes-1.6.6.txt
Documentation/RelNotes-1.7.0.7.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.0.txt
Documentation/RelNotes-1.7.1.2.txt
Documentation/RelNotes-1.7.2.1.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.2.2.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.2.3.txt [new file with mode: 0644]
Documentation/RelNotes-1.7.3.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/fetch-options.txt
Documentation/git-apply.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-checkout.txt
Documentation/git-clean.txt
Documentation/git-commit.txt
Documentation/git-cvsimport.txt
Documentation/git-cvsserver.txt
Documentation/git-fast-export.txt
Documentation/git-fast-import.txt
Documentation/git-hash-object.txt
Documentation/git-instaweb.txt
Documentation/git-log.txt
Documentation/git-ls-files.txt
Documentation/git-notes.txt
Documentation/git-prune.txt
Documentation/git-pull.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-request-pull.txt
Documentation/git-reset.txt
Documentation/git-rev-parse.txt
Documentation/git-show-ref.txt
Documentation/git-status.txt
Documentation/git-svn.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/gitmodules.txt
Documentation/gitrepository-layout.txt
Documentation/howto/revert-a-faulty-merge.txt
Documentation/howto/revert-branch-rebase.txt
Documentation/install-webdoc.sh
Documentation/merge-config.txt
Documentation/merge-strategies.txt
Documentation/pretty-options.txt
Documentation/technical/api-merge.txt [new file with mode: 0644]
Documentation/technical/api-tree-walking.txt
Documentation/urls.txt
GIT-VERSION-GEN
INSTALL
Makefile
RelNotes
alias.c
archive.c
base85.c
bisect.c
branch.c
builtin/apply.c
builtin/blame.c
builtin/bundle.c
builtin/check-ref-format.c
builtin/checkout.c
builtin/clean.c
builtin/commit.c
builtin/config.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/for-each-ref.c
builtin/grep.c
builtin/help.c
builtin/index-pack.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/mailsplit.c
builtin/merge-file.c
builtin/merge-recursive.c
builtin/merge.c
builtin/mv.c
builtin/name-rev.c
builtin/notes.c
builtin/prune.c
builtin/push.c
builtin/receive-pack.c
builtin/remote.c
builtin/rerere.c
builtin/reset.c
builtin/revert.c
builtin/shortlog.c
builtin/show-ref.c
builtin/var.c
cache-tree.c
cache.h
commit.c
commit.h
compat/mingw.c
compat/nedmalloc/malloc.c.h
compat/regex/regex.c
compat/strtok_r.c [new file with mode: 0644]
config.mak.in
configure.ac
contrib/examples/git-commit.sh
contrib/examples/git-revert.sh
contrib/hooks/post-receive-email
contrib/svn-fe/.gitignore
contrib/svn-fe/Makefile
contrib/svn-fe/svn-fe.c
contrib/svn-fe/svn-fe.txt
contrib/workdir/git-new-workdir
convert.c
date.c
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore.h
environment.c
fast-import.c
git-compat-util.h
git-gui/git-gui.sh
git-gui/lib/blame.tcl
git-gui/lib/choose_repository.tcl
git-gui/lib/diff.tcl
git-gui/lib/option.tcl
git-gui/lib/shortcut.tcl
git-gui/lib/status_bar.tcl
git-gui/lib/win32.tcl
git-gui/windows/git-gui.sh
git-instaweb.sh
git-mergetool.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
gitweb/README
gitweb/gitweb.perl
graph.c
graph.h
http.c
http.h
imap-send.c
ll-merge.c
ll-merge.h
merge-recursive.c
merge-recursive.h
notes.c
object.h
pack-check.c
pack-refs.c
parse-options.h
path.c
perl/Makefile
reachable.c
read-cache.c
refs.c
refs.h
remote-curl.c
remote.c
rerere.c
resolve-undo.c
revision.c
revision.h
setup.c
sha1_file.c
sha1_name.c
shallow.c
string-list.h
submodule.c
submodule.h
t/.gitignore
t/Makefile
t/README
t/lib-git-svn.sh
t/lib-rebase.sh
t/t0003-attributes.sh
t/t0080-vcs-svn.sh [new file with mode: 0755]
t/t1001-read-tree-m-2way.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1300-repo-config.sh
t/t1402-check-ref-format.sh
t/t1501-worktree.sh
t/t2018-checkout-branch.sh [new file with mode: 0755]
t/t3030-merge-recursive.sh
t/t3210-pack-refs.sh
t/t3301-notes.sh
t/t3302-notes-index-expensive.sh
t/t3306-notes-prune.sh
t/t3400-rebase.sh
t/t3402-rebase-merge.sh
t/t3404-rebase-interactive.sh
t/t3407-rebase-abort.sh
t/t3410-rebase-preserve-dropped-merges.sh
t/t3415-rebase-autosquash.sh
t/t3418-rebase-continue.sh [new file with mode: 0755]
t/t3505-cherry-pick-empty.sh
t/t3507-cherry-pick-conflict.sh
t/t3508-cherry-pick-many-commits.sh
t/t3509-cherry-pick-merge-df.sh [new file with mode: 0755]
t/t3700-add.sh
t/t4013-diff-various.sh
t/t4013/diff.log_-S_F_master [new file with mode: 0644]
t/t4018-diff-funcname.sh
t/t4027-diff-submodule.sh
t/t4045-diff-relative.sh [new file with mode: 0755]
t/t4111-apply-subdir.sh [new file with mode: 0755]
t/t4150-am.sh
t/t4200-rerere.sh
t/t4202-log.sh
t/t5001-archive-attr.sh
t/t5520-pull.sh
t/t5525-fetch-tagopt.sh [new file with mode: 0755]
t/t5530-upload-pack-error.sh
t/t5601-clone.sh
t/t6018-rev-list-glob.sh
t/t6020-merge-df.sh
t/t6031-merge-recursive.sh
t/t6035-merge-dir-to-symlink.sh
t/t6037-merge-ours-theirs.sh
t/t6038-merge-text-auto.sh [new file with mode: 0755]
t/t6050-replace.sh
t/t7003-filter-branch.sh
t/t7006-pager.sh
t/t7300-clean.sh
t/t7403-submodule-sync.sh
t/t7405-submodule-merge.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7508-status.sh
t/t7509-commit.sh
t/t7607-merge-overwrite.sh
t/t7609-merge-co-error-msgs.sh [new file with mode: 0755]
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t9010-svn-fe.sh [new file with mode: 0755]
t/t9100-git-svn-basic.sh
t/t9130-git-svn-authors-file.sh
t/t9139-git-svn-non-utf8-commitencoding.sh
t/t9140-git-svn-reset.sh
t/t9155-git-svn-fetch-deleted-tag.sh [new file with mode: 0755]
t/t9156-git-svn-fetch-deleted-tag-2.sh [new file with mode: 0755]
t/t9300-fast-import.sh
t/t9350-fast-export.sh
t/test-lib.sh
test-line-buffer.c [new file with mode: 0644]
test-obj-pool.c [new file with mode: 0644]
test-string-pool.c [new file with mode: 0644]
test-svn-fe.c [new file with mode: 0644]
test-treap.c [new file with mode: 0644]
transport-helper.c
tree-diff.c
tree-walk.c
tree-walk.h
unpack-trees.c
unpack-trees.h
upload-pack.c
url.c
userdiff.c
vcs-svn/LICENSE [new file with mode: 0644]
vcs-svn/fast_export.c [new file with mode: 0644]
vcs-svn/fast_export.h [new file with mode: 0644]
vcs-svn/line_buffer.c [new file with mode: 0644]
vcs-svn/line_buffer.h [new file with mode: 0644]
vcs-svn/line_buffer.txt [new file with mode: 0644]
vcs-svn/obj_pool.h [new file with mode: 0644]
vcs-svn/repo_tree.c [new file with mode: 0644]
vcs-svn/repo_tree.h [new file with mode: 0644]
vcs-svn/string_pool.c [new file with mode: 0644]
vcs-svn/string_pool.h [new file with mode: 0644]
vcs-svn/string_pool.txt [new file with mode: 0644]
vcs-svn/svndump.c [new file with mode: 0644]
vcs-svn/svndump.h [new file with mode: 0644]
vcs-svn/trp.h [new file with mode: 0644]
vcs-svn/trp.txt [new file with mode: 0644]
wrapper.c
wt-status.c

index 14e2b6bde9bef55d678da8ba44dc180b039cd3ac..4cb14e0baeb325f871d0e48cb03446a5309f92de 100644 (file)
 /gitk-git/gitk-wish
 /gitweb/GITWEB-BUILD-OPTIONS
 /gitweb/gitweb.cgi
-/gitweb/gitweb.min.*
+/gitweb/static/gitweb.min.*
 /test-chmtime
 /test-ctype
 /test-date
 /test-dump-cache-tree
 /test-genrandom
 /test-index-version
+/test-line-buffer
 /test-match-trees
+/test-obj-pool
 /test-parse-options
 /test-path-utils
 /test-run-command
 /test-sha1
 /test-sigchain
+/test-string-pool
+/test-svn-fe
+/test-treap
 /common-cmds.h
 *.tar.gz
 *.dsc
index 942611299d59abd4bdd820e1258662067a304d62..f61dd3504afb9dc3fb7bb2d522e3adb573ef132f 100644 (file)
@@ -4,7 +4,7 @@ GIT v1.5.6.3 Release Notes
 Fixes since v1.5.6.2
 --------------------
 
-* Setting core.sharerepository to traditional "true" value was supposed to make
+* Setting core.sharedrepository to traditional "true" value was supposed to make
   the repository group writable but should not affect permission for others.
   However, since 1.5.6, it was broken to drop permission for others when umask is
   022, making the repository unreadable by others.
index 51b32f5d94c050f6cb5eae0b94cedda187e00312..e1e24b3295d4079c1bf784d11a8d6ec1691ec819 100644 (file)
@@ -17,7 +17,7 @@ Fixes since v1.6.0.1
 * Many commands did not use the correct working tree location when used
   with GIT_WORK_TREE environment settings.
 
-* Some systems needs to use compatibility fnmach and regex libraries
+* Some systems need to use compatibility fnmatch and regex libraries
   independent from each other; the compat/ area has been reorganized to
   allow this.
 
index 4f29babdeb03f9eddf73b7a6cb2a2e9b069baa4f..5643e6537de55ca961245cd1d7d01f46d9e9b855 100644 (file)
@@ -11,7 +11,7 @@ Fixes since v1.6.4.2
   been deprecated.
 
 * "git fetch" and "git clone" had an extra sanity check to verify the
-  presense of the corresponding *.pack file before downloading *.idx
+  presence of the corresponding *.pack file before downloading *.idx
   file by issuing a HEAD request.  Github server however sometimes
   gave 500 (Internal server error) response to HEAD even if a GET
   request for *.pack file to the same URL would have succeeded, and broke
index e42f8b239706465c7d1daf64ae052a31fa8ffcc8..d3a2a3e71243dc4612050d28a8cc779f34ab51ae 100644 (file)
@@ -26,7 +26,7 @@ Fixes since v1.6.5.3
    future versions, but not in this release,
 
  * "git merge -m <message> <branch>..." added the standard merge message
-   on its own after user-supplied message, which should have overrided the
+   on its own after user-supplied message, which should have overridden the
    standard one.
 
 Other minor documentation updates are included.
index 5b49ea53beb5592f97ccc68840d1b97616b18f23..dc5302c21cd2108a175235bc22efb0debac29670 100644 (file)
@@ -10,7 +10,7 @@ Fixes since v1.6.5.6
   an older version of git should just ignore them.  Instead we diagnosed
   it as an error.
 
-* With help.autocorrect set to non-zero value, the logic to guess typoes
+* With help.autocorrect set to non-zero value, the logic to guess typos
   in the subcommand name misfired and ran a random nonsense command.
 
 * If a command is run with an absolute path as a pathspec inside a bare
index 04e205c457cd11cba91327ad1e8b2c023743ac74..c50b59c4957a5def22fa6b2fc55cc85f3d46d0bc 100644 (file)
@@ -29,7 +29,7 @@ or adjust to the new behaviour, on the day their sysadmin decides to install
 the new version of git.  When we switched from "git-foo" to "git foo" in
 1.6.0, even though the change had been advertised and the transition
 guide had been provided for a very long time, the users procrastinated
-during the entire transtion period, and ended up panicking on the day
+during the entire transition period, and ended up panicking on the day
 their sysadmins updated their git installation.  We are trying to avoid
 repeating that unpleasantness in the 1.7.0 release.
 
@@ -94,7 +94,7 @@ users will fare this time.
  * "git diff" traditionally treated various "ignore whitespace" options
    only as a way to filter the patch output.  "git diff --exit-code -b"
    exited with non-zero status even if all changes were about changing the
-   ammount of whitespace and nothing else.  and "git diff -b" showed the
+   amount of whitespace and nothing else.  and "git diff -b" showed the
    "diff --git" header line for such a change without patch text.
 
    In 1.7.0, the "ignore whitespaces" will affect the semantics of the
diff --git a/Documentation/RelNotes-1.7.0.7.txt b/Documentation/RelNotes-1.7.0.7.txt
new file mode 100644 (file)
index 0000000..d0cb7ca
--- /dev/null
@@ -0,0 +1,16 @@
+Git v1.7.0.7 Release Notes
+==========================
+
+Fixes since v1.7.0.6
+--------------------
+
+ * "make NO_CURL=NoThanks install" was broken.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+And other minor fixes and documentation updates.
index 43e3f336150e1869bdae01823471d7ab216584a8..0bb8c0b2a2a771ac95b5e5c1fc9b44eac5f2fe5d 100644 (file)
@@ -202,7 +202,7 @@ release, unless otherwise noted.
    the branch is fully merged to its upstream branch if it is not merged
    to the current branch.  It now deletes it in such a case.
 
- * "fiter-branch" command incorrectly said --prune-empty and --filter-commit
+ * "filter-branch" command incorrectly said --prune-empty and --filter-commit
    were incompatible; the latter should be read as --commit-filter.
 
  * When using "git status" or asking "git diff" to compare the work tree
index 46b6a960c799a5324b48ea2e266eaab5187ded24..61ba14e262a450c47d1490f4eedadbf8031fd3ed 100644 (file)
@@ -17,3 +17,12 @@ Fixes since v1.7.1.1
 
  * "git rev-parse --parseopt --stop-at-non-option" did not stop at non option
    when --keep-dashdash was in effect.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.2.1.txt b/Documentation/RelNotes-1.7.2.1.txt
new file mode 100644 (file)
index 0000000..1103c47
--- /dev/null
@@ -0,0 +1,25 @@
+Git v1.7.2.1 Release Notes
+==========================
+
+Fixes since v1.7.2
+------------------
+
+ * "git instaweb" wasn't useful when your Apache was installed under a
+   name other than apache2 (e.g. "httpd").
+
+ * Similarly, "git web--browse" (invoked by "git help -w") learned that
+   chrome browser is sometimes called google-chrome.
+
+ * An overlong line after ".gitdir: " in a git file caused out of bounds
+   access to an array on the stack.
+
+ * "git config --path conf.var" to attempt to expand a variable conf.var
+   that uses "~/" short-hand segfaulted when $HOME environment variable
+   was not set.
+
+ * Documentation on Cygwin failed to build.
+
+ * The error message from "git pull blarg" when 'blarg' is an unknown
+   remote name has been improved.
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.2.2.txt b/Documentation/RelNotes-1.7.2.2.txt
new file mode 100644 (file)
index 0000000..71eb6a8
--- /dev/null
@@ -0,0 +1,22 @@
+Git v1.7.2.2 Release Notes
+==========================
+
+Fixes since v1.7.2.1
+--------------------
+
+ * Object transfer over smart http transport deadlocked the client when
+   the remote HTTP server returned a failure, instead of erroring it out.
+
+ * git-gui honors custom textconv filters when showing diff and blame;
+
+ * git diff --relative=subdir (without the necessary trailing /) did not
+   work well;
+
+ * "git diff-files -p --submodule" was recently broken;
+
+ * "git checkout -b n ':/token'" did not work;
+
+ * "git index-pack" (hence "git fetch/clone/pull/push") enabled the object
+   replacement machinery by mistake (it never should have);
+
+And other minor fixes and documentation updates.
diff --git a/Documentation/RelNotes-1.7.2.3.txt b/Documentation/RelNotes-1.7.2.3.txt
new file mode 100644 (file)
index 0000000..610960c
--- /dev/null
@@ -0,0 +1,39 @@
+Git v1.7.2.3 Release Notes
+==========================
+
+Fixes since v1.7.2.2
+--------------------
+
+ * When people try insane things such as delta-compressing 4GiB files, we
+   threw an assertion failure.
+
+ * "git archive" gave the full commit ID for "$Format:%h$".
+
+ * "git fetch --tags" did not fetch tags when remote.<nick>.tagopt was set
+   to --no-tags.  The command line option now overrides the configuration
+   setting.
+
+ * "git for-each-ref --format='%(objectname:short)'" has been completely
+   broken for a long time.
+
+ * "git gc" incorrectly pruned a rerere record that was created long
+   time ago but still is actively and repeatedly used.
+
+ * "git log --follow -M -p" was seriously broken in 1.7.2, reporting
+   assertion failure.
+
+ * Running "git log" with an incorrect option started pager nevertheless,
+   forcing the user to dismiss it.
+
+ * "git rebase" did not work well when the user has diff.renames
+   configuration variable set.
+
+ * An earlier (and rather old) fix to "git rebase" against a rebased
+   upstream broke a more normal, non rebased upstream case rather badly,
+   attempting to re-apply patches that are already accepted upstream.
+
+ * "git submodule sync" forgot to update the superproject's config file
+   when submodule URL changed.
+
+ * "git pack-refs --all --prune" did not remove a directory that has
+   become empty.
diff --git a/Documentation/RelNotes-1.7.3.txt b/Documentation/RelNotes-1.7.3.txt
new file mode 100644 (file)
index 0000000..3512bbb
--- /dev/null
@@ -0,0 +1,73 @@
+Git v1.7.3 Release Notes (draft)
+================================
+
+Updates since v1.7.2
+--------------------
+
+ * git-gui got various updates and a new maintainer, Pat Thoyts.
+
+ * Gitweb allows its configuration to change per each request; it used to
+   read the configuration once upon startup.
+
+ * When git finds a corrupt object, it now reports the file that contains
+   it.
+
+ * "git checkout -B <it>" is a shorter way to say "git branch -f <it>"
+   followed by "git checkout <it>".
+
+ * When "git checkout" or "git merge" refuse to proceed in order to
+   protect local modification to your working tree, they used to stop
+   after showing just one path that might be lost.  They now show all,
+   in a format that is easier to read.
+
+ * "git clean" learned "-e" ("--exclude") option.
+
+ * Hunk headers produced for C# files by "git diff" and friends show more
+   relevant context than before.
+
+ * diff.ignoresubmodules configuration variable can be used to squelch the
+   differences in submodules reported when running commands (e.g. "diff",
+   "status", etc.) at the superproject level.
+
+ * http.useragent configuration can be used to lie who you are to your
+   restrictive firewall.
+
+ * "git rebase --strategy <s>" learned "-X" option to pass extra options
+   that are understood by the chosen merge strategy.
+
+ * "git rebase -i" learned "exec" that you can insert into the insn sheet
+   to run a command between its steps.
+
+ * "git rebase" between branches that have many binary changes that do
+   not conflict should be faster.
+
+ * "git rebase -i" peeks into rebase.autosquash configuration and acts as
+   if you gave --autosquash from the command line.
+
+
+Also contains various documentation updates.
+
+
+Fixes since v1.7.2
+------------------
+
+All of the fixes in v1.7.2.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git merge -s recursive" (which is the default) did not handle cases
+   where a directory becomes a file (or vice versa) very well.
+
+ * "git fetch" and friends were accidentally broken for url with "+" in
+   its path, e.g. "git://git.gnome.org/gtk+".
+
+---
+exec >/var/tmp/1
+echo O=$(git describe master)
+O=v1.7.2.2-268-g7e42332
+O=v1.7.2
+git shortlog --no-merges $O..master ^maint
+exit 0
+
+What did we want to do with...
+
+1e3d411 (Enable custom schemes for column colors in the graph API, 2010-07-13)
index eb53e0636e3c3bab06e88ce3371945f5602c5756..ece3c77482b3ff006b973f1ed90b708e26556862 100644 (file)
@@ -7,17 +7,16 @@ Checklist (and a short version for the impatient):
          before committing
        - do not check in commented out code or unneeded files
        - the first line of the commit message should be a short
-         description and should skip the full stop
+         description (50 characters is the soft limit, see DISCUSSION
+         in git-commit(1)), and should skip the full stop
        - the body should provide a meaningful commit message, which:
                - uses the imperative, present tense: "change",
                  not "changed" or "changes".
                - includes motivation for the change, and contrasts
                  its implementation with previous behaviour
-       - if you want your work included in git.git, add a
-         "Signed-off-by: Your Name <you@example.com>" line to the
-         commit message (or just use the option "-s" when
-         committing) to confirm that you agree to the Developer's
-         Certificate of Origin
+       - add a "Signed-off-by: Your Name <you@example.com>" line to the
+         commit message (or just use the option "-s" when committing)
+         to confirm that you agree to the Developer's Certificate of Origin
        - make sure that you have tests for the bug you are fixing
        - make sure that the test suite passes after your commit
 
index e75434b3ef2d81fa8dafd552d4b689eb84eea441..61831f60ce61f8a601fed3ba0b40559929d0ee61 100644 (file)
@@ -128,7 +128,7 @@ advice.*::
                when writing commit messages. Default: true.
        commitBeforeMerge::
                Advice shown when linkgit:git-merge[1] refuses to
-               merge to avoid overwritting local changes.
+               merge to avoid overwriting local changes.
                Default: true.
        resolveConflict::
                Advices shown by various commands when conflicts
@@ -418,7 +418,7 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.
 
 core.deltaBaseCacheLimit::
        Maximum number of bytes to reserve for caching base objects
-       that multiple deltafied objects reference.  By storing the
+       that may be referenced by multiple deltified objects.  By storing the
        entire decompressed base objects in a cache Git is able
        to avoid unpacking and decompressing frequently used base
        objects multiple times.
@@ -563,7 +563,7 @@ not necessarily be the current directory.
 am.keepcr::
        If true, git-am will call git-mailsplit for patches in mbox format
        with parameter '--keep-cr'. In this case git-mailsplit will
-       not remove `\r` from lines ending with `\r\n`. Can be overrriden
+       not remove `\r` from lines ending with `\r\n`. Can be overridden
        by giving '--no-keep-cr' from the command line.
        See linkgit:git-am[1], linkgit:git-mailsplit[1].
 
@@ -826,6 +826,11 @@ diff.renames::
        will enable basic rename detection.  If set to "copies" or
        "copy", it will detect copies, as well.
 
+diff.ignoreSubmodules::
+       Sets the default value of --ignore-submodules. Note that this
+       affects only 'git diff' Porcelain, and not lower level 'diff'
+       commands such as 'git diff-files'.
+
 diff.suppressBlankEmpty::
        A boolean to inhibit the standard behavior of printing a space
        before each empty output line. Defaults to false.
@@ -1002,7 +1007,7 @@ gitcvs.usecrlfattr::
        If true, the server will look up the end-of-line conversion
        attributes for files to determine the '-k' modes to use. If
        the attributes force git to treat a file as text,
-       the '-k' mode will be left blank so cvs clients will
+       the '-k' mode will be left blank so CVS clients will
        treat it as text. If they suppress text conversion, the file
        will be set with '-kb' mode, which suppresses any newline munging
        the client might otherwise do. If the attributes do not allow
@@ -1243,6 +1248,15 @@ http.noEPSV::
        support EPSV mode. Can be overridden by the 'GIT_CURL_FTP_NO_EPSV'
        environment variable. Default is false (curl will use EPSV).
 
+http.useragent::
+       The HTTP USER_AGENT string presented to an HTTP server.  The default
+       value represents the version of the client git such as git/1.7.1.
+       This option allows you to override this value to a more common value
+       such as Mozilla/4.0.  This may be necessary, for instance, if
+       connecting through a firewall that restricts HTTP connections to a set
+       of common USER_AGENT strings (but not including those like git/1.7.1).
+       Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
+
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; git itself
        does not care per se, but this information is necessary e.g. when
@@ -1275,7 +1289,9 @@ instaweb.local::
        be bound to the local IP (127.0.0.1).
 
 instaweb.modulepath::
-       The module path for an apache httpd used by linkgit:git-instaweb[1].
+       The default module path for linkgit:git-instaweb[1] to use
+       instead of /usr/lib/apache2/modules.  Only used if httpd
+       is Apache.
 
 instaweb.port::
        The port number to bind the gitweb httpd to. See
@@ -1533,6 +1549,9 @@ rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
 
+rebase.autosquash::
+       If set to true enable '--autosquash' option by default.
+
 receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
        receiving data from git-push and updating refs.  You can stop
@@ -1558,6 +1577,10 @@ 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.denyDeleteCurrent::
+       If set to true, git-receive-pack will deny a ref update that
+       deletes the currently checked out branch of a non-bare repository.
+
 receive.denyCurrentBranch::
        If set to true or "refuse", git-receive-pack will deny a ref update
        to the currently checked out branch of a non-bare repository.
@@ -1623,7 +1646,9 @@ remote.<name>.tagopt::
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>. Setting it to \--tags will fetch every
        tag from remote <name>, even if they are not reachable from remote
-       branch heads.
+       branch heads. Passing these flags directly to linkgit:git-fetch[1] can
+       override this setting. See options \--tags and \--no-tags of
+       linkgit:git-fetch[1].
 
 remote.<name>.vcs::
        Setting this to a value <vcs> will cause git to interact with
@@ -1743,6 +1768,19 @@ submodule.<name>.update::
        URL and other values found in the `.gitmodules` file.  See
        linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
 
+submodule.<name>.ignore::
+       Defines under what circumstances "git status" and the diff family show
+       a submodule as modified. When set to "all", it will never be considered
+       modified, "dirty" will ignore all changes to the submodules work tree and
+       takes only differences between the HEAD of the submodule and the commit
+       recorded in the superproject into account. "untracked" will additionally
+       let submodules with modified tracked files in their work tree show up.
+       Using "none" (the default when this option is not set) also shows
+       submodules that have untracked files in their work tree as changed.
+       This setting overrides any setting made in .gitmodules for this submodule,
+       both settings can be overridden on the command line by using the
+       "--ignore-submodules" option.
+
 tar.umask::
        This variable can be used to restrict the permission bits of
        tar archive entries.  The default is 0002, which turns off the
index 2371262b10aad44391406e126a1a8027b543fd49..4656a97e6012796f182a725e14c13dc03f71bba2 100644 (file)
@@ -206,10 +206,29 @@ endif::git-format-patch[]
        the diff-patch output format.  Non default number of
        digits can be specified with `--abbrev=<n>`.
 
--B::
-       Break complete rewrite changes into pairs of delete and create.
-
--M::
+-B[<n>][/<m>]::
+       Break complete rewrite changes into pairs of delete and
+       create. This serves two purposes:
++
+It affects the way a change that amounts to a total rewrite of a file
+not as a series of deletion and insertion mixed together with a very
+few lines that happen to match textually as the context, but as a
+single deletion of everything old followed by a single insertion of
+everything new, and the number `m` controls this aspect of the -B
+option (defaults to 60%). `-B/70%` specifies that less than 30% of the
+original should remain in the result for git to consider it a total
+rewrite (i.e. otherwise the resulting patch will be a series of
+deletion and insertion mixed together with context lines).
++
+When used with -M, a totally-rewritten file is also considered as the
+source of a rename (usually -M only considers a file that disappeared
+as the source of a rename), and the number `n` controls this aspect of
+the -B option (defaults to 50%). `-B20%` specifies that a change with
+addition and deletion compared to 20% or more of the file's size are
+eligible for being picked up as a possible source of a rename to
+another file.
+
+-M[<n>]::
 ifndef::git-log[]
        Detect renames.
 endif::git-log[]
@@ -218,9 +237,15 @@ ifdef::git-log[]
        For following files across renames while traversing history, see
        `--follow`.
 endif::git-log[]
+       If `n` is specified, it is a is a threshold on the similarity
+       index (i.e. amount of addition/deletions compared to the
+       file's size). For example, `-M90%` means git should consider a
+       delete/add pair to be a rename if more than 90% of the file
+       hasn't changed.
 
--C::
+-C[<n>]::
        Detect copies as well as renames.  See also `--find-copies-harder`.
+       If `n` is specified, it has the same meaning as for `-M<n>`.
 
 ifndef::git-format-patch[]
 --diff-filter=[ACDMRTUXB*]::
@@ -330,7 +355,11 @@ endif::git-format-patch[]
 
 --ignore-submodules[=<when>]::
        Ignore changes to submodules in the diff generation. <when> can be
-       either "untracked", "dirty" or "all", which is the default. When
+       either "none", "untracked", "dirty" or "all", which is the default
+       Using "none" will consider the submodule modified when it either contains
+       untracked or modified files or its HEAD differs from the commit recorded
+       in the superproject and can be used to override any settings of the
+       'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
        "untracked" is used submodules are not considered dirty when they only
        contain untracked content (but they are still scanned for modified
        content). Using "dirty" ignores all changes to the work tree of submodules,
index 9333c42c558464ce2430df39e9864f932e786b36..470ac31396606a18298f69329b050a86943c580c 100644 (file)
@@ -49,7 +49,9 @@ ifndef::git-pull[]
 endif::git-pull[]
        By default, tags that point at objects that are downloaded
        from the remote repository are fetched and stored locally.
-       This option disables this automatic tag following.
+       This option disables this automatic tag following. The default
+       behavior for a remote may be specified with the remote.<name>.tagopt
+       setting. See linkgit:git-config[1].
 
 -t::
 --tags::
@@ -58,7 +60,9 @@ endif::git-pull[]
        objects reachable from the branch heads that are being
        tracked will not be fetched by this mechanism.  This
        flag lets all tags and their associated objects be
-       downloaded.
+       downloaded. The default behavior for a remote may be
+       specified with the remote.<name>.tagopt setting. See
+       linkgit:git-config[1].
 
 -u::
 --update-head-ok::
index 8463439ac5047d5f1921db4d6f2d765e7728c73b..4a74b23d403d970643fcb8e0438ab6649f54e54c 100644 (file)
@@ -26,6 +26,10 @@ with the `--cache` option the patch is only applied to the index.
 Without these options, the command applies the patch only to files,
 and does not require them to be in a git repository.
 
+This command applies the patch but does not create a commit.  Use
+linkgit:git-am[1] to create commits from patches generated by
+linkgit:git-format-patch[1] and/or received by email.
+
 OPTIONS
 -------
 <patch>...::
@@ -242,6 +246,12 @@ If `--index` is not specified, then the submodule commits in the patch
 are ignored and only the absence or presence of the corresponding
 subdirectory is checked and (if possible) updated.
 
+
+SEE ALSO
+--------
+linkgit:git-am[1].
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index 86b3015c134938c03a396b9fbcfcd5cd47e96718..efbe3790bb4a0521c954650ad43075f583500c7b 100644 (file)
@@ -971,7 +971,7 @@ logical change in each commit.
 The smaller the changes in your commit, the most effective "git
 bisect" will be. And you will probably need "git bisect" less in the
 first place, as small changes are easier to review even if they are
-only reviewed by the commiter.
+only reviewed by the committer.
 
 Another good idea is to have good commit messages. They can be very
 helpful to understand why some changes were made.
index 1bacd2e1044f5b3b7d5a60e1687387cc277fc52a..66e570113a077d2c52c4fc3ac09d24a272df5301 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
 'git checkout' --patch [<tree-ish>] [--] [<paths>...]
 
@@ -21,7 +21,7 @@ also update `HEAD` to set the specified branch as the current
 branch.
 
 'git checkout' [<branch>]::
-'git checkout' -b <new branch> [<start point>]::
+'git checkout' -b|-B <new_branch> [<start point>]::
 
        This form switches branches by updating the index, working
        tree, and HEAD to reflect the specified branch.
@@ -31,6 +31,17 @@ were called and then checked out; in this case you can
 use the `--track` or `--no-track` options, which will be passed to
 'git branch'.  As a convenience, `--track` without `-b` implies branch
 creation; see the description of `--track` below.
++
+If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+is reset. This is the transactional equivalent of
++
+------------
+$ git branch -f <branch> [<start point>]
+$ git checkout <branch>
+------------
++
+that is to say, the branch is not reset/created unless "git checkout" is
+successful.
 
 'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
 
@@ -75,6 +86,12 @@ entries; instead, unmerged entries are ignored.
        Create a new branch named <new_branch> and start it at
        <start_point>; see linkgit:git-branch[1] for details.
 
+-B::
+       Creates the branch <new_branch> and start it at <start_point>;
+       if it already exists, then reset it to <start_point>. This is
+       equivalent to running "git branch" with "-f"; see
+       linkgit:git-branch[1] for details.
+
 -t::
 --track::
        When creating a new branch, set up "upstream" configuration. See
index a81cb6c28050e83778f11f2957d3489f50fa6004..60e38e6e275f5cf90a1ccba36dd3930cafa1b630 100644 (file)
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
 SYNOPSIS
 --------
 [verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
 
 DESCRIPTION
 -----------
@@ -45,6 +45,12 @@ OPTIONS
        Be quiet, only report errors, but not the files that are
        successfully removed.
 
+-e <pattern>::
+--exclude=<pattern>::
+       Specify special exceptions to not be cleaned.  Each <pattern> is
+       the same form as in $GIT_DIR/info/excludes and this option can be
+       given multiple times.
+
 -x::
        Don't use the ignore rules.  This allows removing all untracked
        files, including build products.  This can be used (possibly in
index c28603ecf58bd065cc8f7c3c4436f1c0f82a7d2e..42fb1f57b21d7c355d3df6be07329c478786a9a7 100644 (file)
@@ -130,11 +130,11 @@ OPTIONS
        Usually recording a commit that has the exact same tree as its
        sole parent commit is a mistake, and the command prevents you
        from making such a commit.  This option bypasses the safety, and
-       is primarily for use by foreign scm interface scripts.
+       is primarily for use by foreign SCM interface scripts.
 
 --allow-empty-message::
        Like --allow-empty this command is primarily for use by foreign
-       scm interface scripts. It allows you to create a commit with an
+       SCM interface scripts. It allows you to create a commit with an
        empty commit message without using plumbing commands like
        linkgit:git-commit-tree[1].
 
index 8bcd875a677ab6ff91fc4600d541eac4750efce7..608cd63fc359d591e2aafbb8bd2fbae9b868b68b 100644 (file)
@@ -188,7 +188,7 @@ ISSUES
 ------
 Problems related to timestamps:
 
- * If timestamps of commits in the cvs repository are not stable enough
+ * If timestamps of commits in the CVS repository are not stable enough
    to be used for ordering commits changes may show up in the wrong
    order.
  * If any files were ever "cvs import"ed more than once (e.g., import of
@@ -201,7 +201,7 @@ Problems related to branches:
 
  * Branches on which no commits have been made are not imported.
  * All files from the branching point are added to a branch even if
-   never added in cvs.
+   never added in CVS.
  * This applies to files added to the source branch *after* a daughter
    branch was created: if previously no commit was made on the daughter
    branch they will erroneously be added to the daughter branch in git.
index 7004dd2decfb2a9cf82b248aae1f848beaf885cb..f4472c61dbb297d61053ef2e8503ad7c56d06148 100644 (file)
@@ -366,8 +366,8 @@ CRLF Line Ending Conversions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 By default the server leaves the '-k' mode blank for all files,
-which causes the cvs client to treat them as a text files, subject
-to crlf conversion on some platforms.
+which causes the CVS client to treat them as a text files, subject
+to end-of-line conversion on some platforms.
 
 You can make the server use the end-of-line conversion attributes to
 set the '-k' modes for files by setting the `gitcvs.usecrlfattr`
index 98ec6b5871b8dfb3cee2e1302099536d8b0dcd7f..8a6a3cb255258dddc38f69b20525f8d123683681 100644 (file)
@@ -90,6 +90,12 @@ marks the same across runs.
        resulting stream can only be used by a repository which
        already contains the necessary objects.
 
+--full-tree::
+       This option will cause fast-export to issue a "deleteall"
+       directive for each commit followed by a full list of all files
+       in the commit (as opposed to just listing the files which are
+       different from the commit's first parent).
+
 [git-rev-list-args...]::
        A list of arguments, acceptable to 'git rev-parse' and
        'git rev-list', that specifies the specific objects and references
index 77a0a2481a34f987aab4688002a6e6a0ae2e497f..966ba4f21337e72f7be0a5b852e76a8b12192f7f 100644 (file)
@@ -482,9 +482,11 @@ External data format::
        'M' SP <mode> SP <dataref> SP <path> LF
 ....
 +
-Here `<dataref>` can be either a mark reference (`:<idnum>`)
+Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
 set by a prior `blob` command, or a full 40-byte SHA-1 of an
-existing Git blob object.
+existing Git blob object.  If `<mode>` is `040000`` then
+`<dataref>` must be the full 40-byte SHA-1 of an existing
+Git tree object or a mark reference set with `--import-marks`.
 
 Inline data format::
        The data content for the file has not been supplied yet.
@@ -509,6 +511,8 @@ in octal.  Git only supports the following modes:
 * `160000`: A gitlink, SHA-1 of the object refers to a commit in
   another repository. Git links can only be specified by SHA or through
   a commit mark. They are used to implement submodules.
+* `040000`: A subdirectory.  Subdirectories can only be specified by
+  SHA or through a tree mark set with `--import-marks`.
 
 In both formats `<path>` is the complete path of the file to be added
 (if not already existing) or modified (if already existing).
index 6904739a48443ba255b2506ad92c10f91fa61d21..51edeecbe5b546c6fbbbe117f39b51f352cb6bc8 100644 (file)
@@ -49,7 +49,7 @@ OPTIONS
 
 --no-filters::
        Hash the contents as is, ignoring any input filter that would
-       have been chosen by the attributes mechanism, including crlf
+       have been chosen by the attributes mechanism, including the end-of-line
        conversion. If the file is read from standard input then this
        is always implied, unless the --path option is given.
 
index 2c3c4d299472a265bfc4486816933ca3dc69b44f..e70cea932063b04f3ab3c78b882f4981514d663e 100644 (file)
@@ -49,15 +49,18 @@ OPTIONS
        linkgit:git-web--browse[1] for more information about this. If
        the script fails, the URL will be printed to stdout.
 
+start::
 --start::
        Start the httpd instance and exit.  This does not generate
        any of the configuration files for spawning a new instance.
 
+stop::
 --stop::
        Stop the httpd instance and exit.  This does not generate
        any of the configuration files for spawning a new instance,
        nor does it close the browser.
 
+restart::
 --restart::
        Restart the httpd instance and exit.  This does not generate
        any of the configuration files for spawning a new instance.
index e970664fe1a47c3f35f0f7c60b6c8f9781caed31..c213bdbdc500b1117805bc84cb2a0f31eeff3bcd 100644 (file)
@@ -55,6 +55,9 @@ OPTIONS
        paths.  With this, the full diff is shown for commits that touch
        the specified paths; this means that "<path>..." limits only
        commits, and doesn't limit diff for those commits.
++
+Note that this affects all diff-based output types, e.g. those
+produced by --stat etc.
 
 --log-size::
        Before the log message print out its size in bytes. Intended
index 3521637b582687978c088dc463e8784817a92217..a7c8174d01810bfb28e331aafc27414dd6cfad50 100644 (file)
@@ -106,8 +106,16 @@ OPTIONS
        with `-s` or `-u` options does not make any sense.
 
 -t::
-       Identify the file status with the following tags (followed by
-       a space) at the start of each line:
+       This feature is semi-deprecated. For scripting purpose,
+       linkgit:git-status[1] `--porcelain` and
+       linkgit:git-diff-files[1] `--name-status` are almost always
+       superior alternatives, and users should look at
+       linkgit:git-status[1] `--short` or linkgit:git-diff[1]
+       `--name-status` for more user-friendly alternatives.
++
+This option identifies the file status with the following tags (followed by
+a space) at the start of each line:
+
        H::     cached
        S::     skip-worktree
        M::     unmerged
@@ -132,6 +140,12 @@ OPTIONS
        lines, show only a partial prefix.
        Non default number of digits can be specified with --abbrev=<n>.
 
+--debug::
+       After each line that describes a file, add more data about its
+       cache entry.  This is intended to show as much information as
+       possible for manual inspection; the exact format may change at
+       any time.
+
 \--::
        Do not interpret any more arguments as options.
 
index 5540af5d1667aad16ac11c53593921733c4af56f..2981d8c5efd4e7e0f65d051cfe3b28d3430e3213 100644 (file)
@@ -129,10 +129,12 @@ OPTIONS
        is taken to be in `refs/notes/` if it is not qualified.
 
 -n::
+--dry-run::
        Do not remove anything; just report the object names whose notes
        would be removed.
 
 -v::
+--verbose::
        Report all object names whose notes are removed.
 
 
index 15cfb7a8dc8e5e6eb25ee58b8fe15d89ad62dc8a..4d673a56864ca88de0e98f54a6de469e99ea0f44 100644 (file)
@@ -31,10 +31,12 @@ OPTIONS
 -------
 
 -n::
+--dry-run::
        Do not remove anything; just report what it would
        remove.
 
 -v::
+--verbose::
        Report all removed objects.
 
 \--::
index ab4de103586e8382801dad7de2f43c57f4758e7e..c50f7dcb890139d829bae51c30b513d54fd7761e 100644 (file)
@@ -8,29 +8,72 @@ git-pull - Fetch from and merge with another repository or a local branch
 
 SYNOPSIS
 --------
-'git pull' <options> <repository> <refspec>...
+'git pull' [options] [<repository> [<refspec>...]]
 
 
 DESCRIPTION
 -----------
-Runs 'git fetch' with the given parameters, and calls 'git merge'
-to merge the retrieved head(s) into the current branch.
-With `--rebase`, calls 'git rebase' instead of 'git merge'.
 
-Note that you can use `.` (current directory) as the
-<repository> to pull from the local repository -- this is useful
-when merging local branches into the current branch.
+Incorporates changes from a remote repository into the current
+branch.  In its default mode, `git pull` is shorthand for
+`git fetch` followed by `git merge FETCH_HEAD`.
 
-Also note that options meant for 'git pull' itself and underlying
-'git merge' must be given before the options meant for 'git fetch'.
+More precisely, 'git pull' runs 'git fetch' with the given
+parameters and calls 'git merge' to merge the retrieved branch
+heads into the current branch.
+With `--rebase`, it runs 'git rebase' instead of 'git merge'.
 
-*Warning*: Running 'git pull' (actually, the underlying 'git merge')
+<repository> should be the name of a remote repository as
+passed to linkgit:git-fetch[1].  <refspec> can name an
+arbitrary remote ref (for example, the name of a tag) or even
+a collection of refs with corresponding remote tracking branches
+(e.g., refs/heads/*:refs/remotes/origin/*), but usually it is
+the name of a branch in the remote repository.
+
+Default values for <repository> and <branch> are read from the
+"remote" and "merge" configuration for the current branch
+as set by linkgit:git-branch[1] `--track`.
+
+Assume the following history exists and the current branch is
+"`master`":
+
+------------
+         A---B---C master on origin
+        /
+    D---E---F---G master
+------------
+
+Then "`git pull`" will fetch and replay the changes from the remote
+`master` branch since it diverged from the local `master` (i.e., `E`)
+until its current commit (`C`) on top of `master` and record the
+result in a new commit along with the names of the two parent commits
+and a log message from the user describing the changes.
+
+------------
+         A---B---C remotes/origin/master
+        /         \
+    D---E---F---G---H master
+------------
+
+See linkgit:git-merge[1] for details, including how conflicts
+are presented and handled.
+
+In git 1.7.0 or later, to cancel a conflicting merge, use
+`git reset --merge`.  *Warning*: In older versions of git, running 'git pull'
 with uncommitted changes is discouraged: while possible, it leaves you
-in a state that is hard to back out of in the case of a conflict.
+in a state that may be hard to back out of in the case of a conflict.
+
+If any of the remote changes overlap with local uncommitted changes,
+the merge will be automatically cancelled and the work tree untouched.
+It is generally best to get any local changes in working order before
+pulling or stash them away with linkgit:git-stash[1].
 
 OPTIONS
 -------
 
+Options meant for 'git pull' itself and the underlying 'git merge'
+must be given before the options meant for 'git fetch'.
+
 -q::
 --quiet::
        This is passed to both underlying git-fetch to squelch reporting of
index b68abff28a0fe27fbcb2bbffa2296cffb1297eaf..658ff2ff67f23e9a9d2942e38528dba415139ac3 100644 (file)
@@ -200,16 +200,29 @@ summary::
        For a successfully pushed ref, the summary shows the old and new
        values of the ref in a form suitable for using as an argument to
        `git log` (this is `<old>..<new>` in most cases, and
-       `<old>...<new>` for forced non-fast-forward updates). For a
-       failed update, more details are given for the failure.
-       The string `rejected` indicates that git did not try to send the
-       ref at all (typically because it is not a fast-forward). The
-       string `remote rejected` indicates that the remote end refused
-       the update; this rejection is typically caused by a hook on the
-       remote side. The string `remote failure` indicates that the
-       remote end did not report the successful update of the ref
-       (perhaps because of a temporary error on the remote side, a
-       break in the network connection, or other transient error).
+       `<old>...<new>` for forced non-fast-forward updates).
++
+For a failed update, more details are given:
++
+--
+rejected::
+       Git did not try to send the ref at all, typically because it
+       is not a fast-forward and you did not force the update.
+
+remote rejected::
+       The remote end refused the update.  Usually caused by a hook
+       on the remote side, or because the remote repository has one
+       of the following safety options in effect:
+       `receive.denyCurrentBranch` (for pushes to the checked out
+       branch), `receive.denyNonFastForwards` (for forced
+       non-fast-forward updates), `receive.denyDeletes` or
+       `receive.denyDeleteCurrent`.  See linkgit:git-config[1].
+
+remote failure::
+       The remote end did not report the successful update of the ref,
+       perhaps because of a temporary error on the remote side, a
+       break in the network connection, or other transient error.
+--
 
 from::
        The name of the local ref being pushed, minus its
index f6037c4f6a7c6988a9a497455e555979d44e2600..2e78da448f30ad260cb7683a5610f424d0c5ffea 100644 (file)
@@ -412,6 +412,13 @@ turn `core.sparseCheckout` on in order to have sparse checkout
 support.
 
 
+BUGS
+----
+In order to match a directory with $GIT_DIR/info/sparse-checkout,
+trailing slash must be used. The form without trailing slash, while
+works with .gitignore, does not work with sparse checkout.
+
+
 SEE ALSO
 --------
 linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
index be23ad2359b486e4da2ef85962d73cdfed7097fe..45c52d2e57d2f4c2783c32b4cb3ff8f4dcd6414e 100644 (file)
@@ -199,6 +199,9 @@ rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
 
+rebase.autosquash::
+       If set to true enable '--autosquash' option by default.
+
 OPTIONS
 -------
 <newbase>::
@@ -250,6 +253,13 @@ on top of the <upstream> branch using the given strategy, using
 the 'ours' strategy simply discards all patches from the <branch>,
 which makes little sense.
 
+-X <strategy-option>::
+--strategy-option=<strategy-option>::
+       Pass the <strategy-option> through to the merge strategy.
+       This implies `\--merge` and, if no strategy has been
+       specified, `-s recursive`.  Note the reversal of 'ours' and
+       'theirs' as noted in above for the `-m` option.
+
 -q::
 --quiet::
        Be quiet. Implies --no-stat.
@@ -326,6 +336,7 @@ idea unless you know what you are doing (see BUGS below).
        instead.
 
 --autosquash::
+--no-autosquash::
        When the commit log message begins with "squash! ..." (or
        "fixup! ..."), and there is a commit whose title begins with
        the same ..., automatically modify the todo list of rebase -i
@@ -334,6 +345,10 @@ idea unless you know what you are doing (see BUGS below).
        commit from `pick` to `squash` (or `fixup`).
 +
 This option is only valid when the '--interactive' option is used.
++
+If the '--autosquash' option is enabled by default using the
+configuration variable `rebase.autosquash`, this option can be
+used to override and disable this setting.
 
 --no-ff::
        With --interactive, cherry-pick all rebased commits instead of
@@ -459,6 +474,30 @@ sure that the current HEAD is "B", and call
 $ git rebase -i -p --onto Q O
 -----------------------------
 
+Reordering and editing commits usually creates untested intermediate
+steps.  You may want to check that your history editing did not break
+anything by running a test, or at least recompiling at intermediate
+points in history by using the "exec" command (shortcut "x").  You may
+do so by creating a todo list like this one:
+
+-------------------------------------------
+pick deadbee Implement feature XXX
+fixup f1a5c00 Fix to feature XXX
+exec make
+pick c0ffeee The oneline of the next commit
+edit deadbab The oneline of the commit after
+exec cd subdir; make test
+...
+-------------------------------------------
+
+The interactive rebase will stop when a command fails (i.e. exits with
+non-0 status) to give you an opportunity to fix the problem. You can
+continue with `git rebase --continue`.
+
+The "exec" command launches the command in a shell (the one specified
+in `$SHELL`, or the default shell if `$SHELL` is not set), so you can
+use shell features (like "cd", ">", ";" ...). The command is run from
+the root of the working tree.
 
 SPLITTING COMMITS
 -----------------
index 19335fddae2b706cd785258a8c02a5595c525667..400f61f6e24947525e51b7e5d808378819945205 100644 (file)
@@ -7,7 +7,7 @@ git-request-pull - Generates a summary of pending changes
 
 SYNOPSIS
 --------
-'git request-pull' <start> <url> [<end>]
+'git request-pull' [-p] <start> <url> [<end>]
 
 DESCRIPTION
 -----------
@@ -17,6 +17,9 @@ the given URL in the generated summary.
 
 OPTIONS
 -------
+-p::
+       Show patch text
+
 <start>::
        Commit to start at.
 
index 645f0c17485d35e6696be950fc61f5fdcf3dd14e..9cf31485feec4b03850edb1f2f3a4b765245f4cc 100644 (file)
@@ -8,40 +8,50 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]
 'git reset' [-q] [<commit>] [--] <paths>...
 'git reset' --patch [<commit>] [--] [<paths>...]
+'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
-Sets the current head to the specified commit and optionally resets the
-index and working tree to match.
-
-This command is useful if you notice some small error in a recent
-commit (or set of commits) and want to redo that part without showing
-the undo in the history.
-
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
-
-The second and third forms with 'paths' and/or --patch are used to
-revert selected paths in the index from a given commit, without moving
-HEAD.
-
+In the first and second form, copy entries from <commit> to the index.
+In the third form, set the current branch to <commit>, optionally
+modifying index and worktree to match.  The <commit> defaults to HEAD
+in all forms.
+
+'git reset' [-q] [<commit>] [--] <paths>...::
+       This form resets the index entries for all <paths> to their
+       state at the <commit>.  (It does not affect the worktree, nor
+       the current branch.)
++
+This means that `git reset <paths>` is the opposite of `git add
+<paths>`.
 
-OPTIONS
--------
---mixed::
-       Resets the index but not the working tree (i.e., the changed files
-       are preserved but not marked for commit) and reports what has not
-       been updated. This is the default action.
+'git reset' --patch|-p [<commit>] [--] [<paths>...]::
+       Interactively select hunks in the difference between the index
+       and <commit> (defaults to HEAD).  The chosen hunks are applied
+       in reverse to the index.
++
+This means that `git reset -p` is the opposite of `git add -p` (see
+linkgit:git-add[1]).
 
+'git reset' [--<mode>] [<commit>]::
+       This form points the current branch to <commit> and then
+       updates index and working tree according to <mode>, which must
+       be one of the following:
++
+--
 --soft::
        Does not touch the index file nor the working tree at all, but
        requires them to be in a good order. This leaves all your changed
        files "Changes to be committed", as 'git status' would
        put it.
 
+--mixed::
+       Resets the index but not the working tree (i.e., the changed files
+       are preserved but not marked for commit) and reports what has not
+       been updated. This is the default action.
+
 --hard::
        Matches the working tree and index to that of the tree being
        switched to. Any changes to tracked files in the working tree
@@ -59,132 +69,46 @@ OPTIONS
        the given commit.  If a file that is different between the
        current commit and the given commit has local changes, reset
        is aborted.
+--
 
--p::
---patch::
-       Interactively select hunks in the difference between the index
-       and <commit> (defaults to HEAD).  The chosen hunks are applied
-       in reverse to the index.
-+
-This means that `git reset -p` is the opposite of `git add -p` (see
-linkgit:git-add[1]).
+If you want to undo a commit other than the latest on a branch,
+linkgit:git-revert[1] is your friend.
+
+
+OPTIONS
+-------
 
 -q::
 --quiet::
        Be quiet, only report errors.
 
-<commit>::
-       Commit to make the current HEAD. If not given defaults to HEAD.
-
-DISCUSSION
-----------
 
-The tables below show what happens when running:
-
-----------
-git reset --option target
-----------
-
-to reset the HEAD to another commit (`target`) with the different
-reset options depending on the state of the files.
-
-In these tables, A, B, C and D are some different states of a
-file. For example, the first line of the first table means that if a
-file is in state A in the working tree, in state B in the index, in
-state C in HEAD and in state D in the target, then "git reset --soft
-target" will put the file in state A in the working tree, in state B
-in the index and in state D in HEAD.
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       A       B     C    D     --soft   A       B     D
-                               --mixed  A       D     D
-                               --hard   D       D     D
-                               --merge (disallowed)
-                               --keep  (disallowed)
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       A       B     C    C     --soft   A       B     C
-                               --mixed  A       C     C
-                               --hard   C       C     C
-                               --merge (disallowed)
-                               --keep   A       C     C
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       B       B     C    D     --soft   B       B     D
-                               --mixed  B       D     D
-                               --hard   D       D     D
-                               --merge  D       D     D
-                               --keep  (disallowed)
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       B       B     C    C     --soft   B       B     C
-                               --mixed  B       C     C
-                               --hard   C       C     C
-                               --merge  C       C     C
-                               --keep   B       C     C
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       B       C     C    D     --soft   B       C     D
-                               --mixed  B       D     D
-                               --hard   D       D     D
-                               --merge (disallowed)
-                               --keep  (disallowed)
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       B       C     C    C     --soft   B       C     C
-                               --mixed  B       C     C
-                               --hard   C       C     C
-                               --merge  B       C     C
-                               --keep   B       C     C
-
-"reset --merge" is meant to be used when resetting out of a conflicted
-merge. Any mergy operation guarantees that the work tree file that is
-involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the work tree. So if
-we see some difference between the index and the target and also
-between the index and the work tree, then it means that we are not
-resetting out from a state that a mergy operation left after failing
-with a conflict. That is why we disallow --merge option in this case.
-
-"reset --keep" is meant to be used when removing some of the last
-commits in the current branch while keeping changes in the working
-tree. If there could be conflicts between the changes in the commit we
-want to remove and the changes in the working tree we want to keep,
-the reset is disallowed. That's why it is disallowed if there are both
-changes between the working tree and HEAD, and between HEAD and the
-target. To be safe, it is also disallowed when there are unmerged
-entries.
-
-The following tables show what happens when there are unmerged
-entries:
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       X       U     A    B     --soft  (disallowed)
-                               --mixed  X       B     B
-                               --hard   B       B     B
-                               --merge  B       B     B
-                               --keep  (disallowed)
-
-      working index HEAD target         working index HEAD
-      ----------------------------------------------------
-       X       U     A    A     --soft  (disallowed)
-                               --mixed  X       A     A
-                               --hard   A       A     A
-                               --merge  A       A     A
-                               --keep  (disallowed)
-
-X means any state and U means an unmerged index.
-
-Examples
+EXAMPLES
 --------
 
+Undo add::
++
+------------
+$ edit                                     <1>
+$ git add frotz.c filfre.c
+$ mailx                                    <2>
+$ git reset                                <3>
+$ git pull git://info.example.com/ nitfol  <4>
+------------
++
+<1> You are happily working on something, and find the changes
+in these files are in good order.  You do not want to see them
+when you run "git diff", because you plan to work on other files
+and changes with these files are distracting.
+<2> Somebody asks you to pull, and the changes sounds worthy of merging.
+<3> However, you already dirtied the index (i.e. your index does
+not match the HEAD commit).  But you know the pull you are going
+to make does not affect frotz.c nor filfre.c, so you revert the
+index changes for these two files.  Your changes in working tree
+remain there.
+<4> Then you can pull and merge, leaving frotz.c and filfre.c
+changes still in the working tree.
+
 Undo a commit and redo::
 +
 ------------
@@ -204,19 +128,6 @@ edit the message further, you can give -C option instead.
 +
 See also the --amend option to linkgit:git-commit[1].
 
-Undo commits permanently::
-+
-------------
-$ git commit ...
-$ 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.  (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::
 +
 ------------
@@ -232,28 +143,18 @@ current HEAD.
 <2> Rewind the master branch to get rid of those three commits.
 <3> Switch to "topic/wip" branch and keep working.
 
-Undo add::
+Undo commits permanently::
 +
 ------------
-$ edit                                     <1>
-$ git add frotz.c filfre.c
-$ mailx                                    <2>
-$ git reset                                <3>
-$ git pull git://info.example.com/ nitfol  <4>
+$ git commit ...
+$ git reset --hard HEAD~3   <1>
 ------------
 +
-<1> You are happily working on something, and find the changes
-in these files are in good order.  You do not want to see them
-when you run "git diff", because you plan to work on other files
-and changes with these files are distracting.
-<2> Somebody asks you to pull, and the changes sounds worthy of merging.
-<3> However, you already dirtied the index (i.e. your index does
-not match the HEAD commit).  But you know the pull you are going
-to make does not affect frotz.c nor filfre.c, so you revert the
-index changes for these two files.  Your changes in working tree
-remain there.
-<4> Then you can pull and merge, leaving frotz.c and filfre.c
-changes still in the working tree.
+<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.  (See the
+"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
+the implications of doing so.)
 
 Undo a merge or pull::
 +
@@ -355,7 +256,7 @@ Keep changes in working tree while discarding some previous commits::
 Suppose you are working on something and you commit it, and then you
 continue working a bit more, but now you think that what you have in
 your working tree should be in another branch that has nothing to do
-with what you commited previously. You can start a new branch and
+with what you committed previously. You can start a new branch and
 reset it while keeping the changes in your work tree.
 +
 ------------
@@ -376,6 +277,114 @@ $ git reset --keep start                    <3>
 <3> But you can use "reset --keep" to remove the unwanted commit after
     you switched to "branch2".
 
+
+DISCUSSION
+----------
+
+The tables below show what happens when running:
+
+----------
+git reset --option target
+----------
+
+to reset the HEAD to another commit (`target`) with the different
+reset options depending on the state of the files.
+
+In these tables, A, B, C and D are some different states of a
+file. For example, the first line of the first table means that if a
+file is in state A in the working tree, in state B in the index, in
+state C in HEAD and in state D in the target, then "git reset --soft
+target" will put the file in state A in the working tree, in state B
+in the index and in state D in HEAD.
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       A       B     C    D     --soft   A       B     D
+                               --mixed  A       D     D
+                               --hard   D       D     D
+                               --merge (disallowed)
+                               --keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       A       B     C    C     --soft   A       B     C
+                               --mixed  A       C     C
+                               --hard   C       C     C
+                               --merge (disallowed)
+                               --keep   A       C     C
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       B     C    D     --soft   B       B     D
+                               --mixed  B       D     D
+                               --hard   D       D     D
+                               --merge  D       D     D
+                               --keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       B     C    C     --soft   B       B     C
+                               --mixed  B       C     C
+                               --hard   C       C     C
+                               --merge  C       C     C
+                               --keep   B       C     C
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       C     C    D     --soft   B       C     D
+                               --mixed  B       D     D
+                               --hard   D       D     D
+                               --merge (disallowed)
+                               --keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       C     C    C     --soft   B       C     C
+                               --mixed  B       C     C
+                               --hard   C       C     C
+                               --merge  B       C     C
+                               --keep   B       C     C
+
+"reset --merge" is meant to be used when resetting out of a conflicted
+merge. Any mergy operation guarantees that the work tree file that is
+involved in the merge does not have local change wrt the index before
+it starts, and that it writes the result out to the work tree. So if
+we see some difference between the index and the target and also
+between the index and the work tree, then it means that we are not
+resetting out from a state that a mergy operation left after failing
+with a conflict. That is why we disallow --merge option in this case.
+
+"reset --keep" is meant to be used when removing some of the last
+commits in the current branch while keeping changes in the working
+tree. If there could be conflicts between the changes in the commit we
+want to remove and the changes in the working tree we want to keep,
+the reset is disallowed. That's why it is disallowed if there are both
+changes between the working tree and HEAD, and between HEAD and the
+target. To be safe, it is also disallowed when there are unmerged
+entries.
+
+The following tables show what happens when there are unmerged
+entries:
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       X       U     A    B     --soft  (disallowed)
+                               --mixed  X       B     B
+                               --hard   B       B     B
+                               --merge  B       B     B
+                               --keep  (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       X       U     A    A     --soft  (disallowed)
+                               --mixed  X       A     A
+                               --hard   A       A     A
+                               --merge  A       A     A
+                               --keep  (disallowed)
+
+X means any state and U means an unmerged index.
+
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds <torvalds@osdl.org>
index 0727f431c6dc01296b04cc93204ba37d16b5a32b..be4c0533603443cd9b4df5bf732bbcdd31802a11 100644 (file)
@@ -184,10 +184,13 @@ scripts the same facilities C builtins have. It works as an option normalizer
 (e.g. splits single switches aggregate values), a bit like `getopt(1)` does.
 
 It takes on the standard input the specification of the options to parse and
-understand, and echoes on the standard output a line suitable for `sh(1)` `eval`
+understand, and echoes on the standard output a string suitable for `sh(1)` `eval`
 to replace the arguments with normalized ones.  In case of error, it outputs
 usage on the standard error stream, and exits with code 129.
 
+Note: Make sure you quote the result when passing it to `eval`.  See
+below for an example.
+
 Input Format
 ~~~~~~~~~~~~
 
@@ -244,7 +247,7 @@ bar=      some cool option --bar with an argument
   An option group Header
 C?        option C with an optional argument"
 
-eval `echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?`
+eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
 ------------
 
 SQ-QUOTE
index 3f9d9c6db39e030c82e1159043c45c834a47012c..75780d7d63894e220bf938da0b4f5dca40d6301d 100644 (file)
@@ -163,9 +163,15 @@ flag, so you can do
 
 to get a listing of all tags together with what they dereference.
 
+FILES
+-----
+`.git/refs/*`, `.git/packed-refs`
+
 SEE ALSO
 --------
-linkgit:git-ls-remote[1]
+linkgit:git-ls-remote[1],
+linkgit:git-update-ref[1],
+linkgit:gitrepository-layout[5]
 
 AUTHORS
 -------
index 2fd054c1040ce43949382e4744a0d7a814a8d9bd..dae190a5f21d3835024ce263bf4bd9b08048cfa6 100644 (file)
@@ -55,7 +55,11 @@ specified.
 
 --ignore-submodules[=<when>]::
        Ignore changes to submodules when looking for changes. <when> can be
-       either "untracked", "dirty" or "all", which is the default. When
+       either "none", "untracked", "dirty" or "all", which is the default.
+       Using "none" will consider the submodule modified when it either contains
+       untracked or modified files or its HEAD differs from the commit recorded
+       in the superproject and can be used to override any settings of the
+       'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
        "untracked" is used submodules are not considered dirty when they only
        contain untracked content (but they are still scanned for modified
        content). Using "dirty" ignores all changes to the work tree of submodules,
index b09bd9761faa42f2bc87306ee5c1890647dce769..4b84d08fc87c13f6db371ecc52710bd09865d784 100644 (file)
@@ -646,6 +646,12 @@ svn.brokenSymlinkWorkaround::
        revision fetched.  If unset, 'git svn' assumes this option to
        be "true".
 
+svn.pathnameencoding::
+       This instructs git svn to recode pathnames to a given encoding.
+       It can be used by windows users and by those who work in non-utf8
+       locales to avoid corrupted file names with non-ASCII characters.
+       Valid encodings are the ones supported by Perl's Encode module.
+
 Since the noMetadata, rewriteRoot, rewriteUUID, useSvnsyncProps and useSvmProps
 options all affect the metadata generated and used by 'git svn'; they
 *must* be set in the configuration file before any history is imported
index 27ece5885720c0db79e30a6de9663e3bdfb37f07..93e3b07c6cdf6c580cd7dff0d973478b91178481 100644 (file)
@@ -44,20 +44,25 @@ unreleased) version of git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.7.2/git.html[documentation for release 1.7.2]
+* link:v1.7.2.3/git.html[documentation for release 1.7.2.3]
 
 * release notes for
+  link:RelNotes-1.7.2.3.txt[1.7.2.3],
+  link:RelNotes-1.7.2.2.txt[1.7.2.2],
+  link:RelNotes-1.7.2.1.txt[1.7.2.1],
   link:RelNotes-1.7.2.txt[1.7.2].
 
-* link:v1.7.1.1/git.html[documentation for release 1.7.1.1]
+* link:v1.7.1.2/git.html[documentation for release 1.7.1.2]
 
 * release notes for
+  link:RelNotes-1.7.1.2.txt[1.7.1.2],
   link:RelNotes-1.7.1.1.txt[1.7.1.1],
   link:RelNotes-1.7.1.txt[1.7.1].
 
-* link:v1.7.0.6/git.html[documentation for release 1.7.0.6]
+* link:v1.7.0.7/git.html[documentation for release 1.7.0.7]
 
 * release notes for
+  link:RelNotes-1.7.0.7.txt[1.7.0.7],
   link:RelNotes-1.7.0.6.txt[1.7.0.6],
   link:RelNotes-1.7.0.5.txt[1.7.0.5],
   link:RelNotes-1.7.0.4.txt[1.7.0.4],
@@ -724,6 +729,13 @@ The documentation for git suite was started by David Greaves
 <david@dgreaves.com>, and later enhanced greatly by the
 contributors on the git-list <git@vger.kernel.org>.
 
+Reporting Bugs
+--------------
+
+Report bugs to the Git mailing list <git@vger.kernel.org> where the
+development and maintenance is primarily done.  You do not have to be
+subscribed to the list to send a message there.
+
 SEE ALSO
 --------
 linkgit:gittutorial[7], linkgit:gittutorial-2[7],
index 564586b943f439cb5ae04c4e76bc19d093c625da..e5a27d875eb63dbfeccbf5357983f5c94668ffeb 100644 (file)
@@ -317,6 +317,17 @@ command is "cat").
        smudge = cat
 ------------------------
 
+For best results, `clean` should not alter its output further if it is
+run twice ("clean->clean" should be equivalent to "clean"), and
+multiple `smudge` commands should not alter `clean`'s output
+("smudge->smudge->clean" should be equivalent to "clean").  See the
+section on merging below.
+
+The "indent" filter is well-behaved in this regard: it will not modify
+input that is already correctly indented.  In this case, the lack of a
+smudge filter means that the clean filter _must_ accept its own output
+without modifying it.
+
 
 Interaction between checkin/checkout attributes
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -331,6 +342,29 @@ In the check-out codepath, the blob content is first converted
 with `text`, and then `ident` and fed to `filter`.
 
 
+Merging branches with differing checkin/checkout attributes
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you have added attributes to a file that cause the canonical
+repository format for that file to change, such as adding a
+clean/smudge filter or text/eol/ident attributes, merging anything
+where the attribute is not in place would normally cause merge
+conflicts.
+
+To prevent these unnecessary merge conflicts, git can be told to run a
+virtual check-out and check-in of all three stages of a file when
+resolving a three-way merge by setting the `merge.renormalize`
+configuration variable.  This prevents changes caused by check-in
+conversion from causing spurious merge conflicts when a converted file
+is merged with an unconverted file.
+
+As long as a "smudge->clean" results in the same output as a "clean"
+even on files that are already smudged, this strategy will
+automatically resolve all filter-related conflicts.  Filters that do
+not act in this way may cause additional merge conflicts that must be
+resolved manually.
+
+
 Generating diff text
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -441,6 +475,8 @@ patterns are available:
 
 - `cpp` suitable for source code in the C and C++ languages.
 
+- `csharp` suitable for source code in the C# language.
+
 - `html` suitable for HTML/XHTML documents.
 
 - `java` suitable for source code in the Java language.
index 72a13d18e019ef00de0e8a79bbe5fe4651ab8dba..bcffd95ada574a8fb3c6e62e539e736a17170414 100644 (file)
@@ -44,6 +44,21 @@ submodule.<name>.update::
        This config option is overridden if 'git submodule update' is given
        the '--merge' or '--rebase' options.
 
+submodule.<name>.ignore::
+       Defines under what circumstances "git status" and the diff family show
+       a submodule as modified. When set to "all", it will never be considered
+       modified, "dirty" will ignore all changes to the submodules work tree and
+       takes only differences between the HEAD of the submodule and the commit
+       recorded in the superproject into account. "untracked" will additionally
+       let submodules with modified tracked files in their work tree show up.
+       Using "none" (the default when this option is not set) also shows
+       submodules that have untracked files in their work tree as changed.
+       If this option is also present in the submodules entry in .git/config of
+       the superproject, the setting there will override the one found in
+       .gitmodules.
+       Both settings can be overridden on the command line by using the
+       "--ignore-submodule" option.
+
 
 EXAMPLES
 --------
index 3cd32d6803874909d671a2d3f48c1d5701ce89cf..eb3d040783e8202f502d05e8b4dc4e3b39736779 100644 (file)
@@ -16,7 +16,7 @@ You may find these things in your git repository (`.git`
 directory for a repository associated with your working tree, or
 `<project>.git` directory for a public 'bare' repository. It is
 also possible to have a working tree where `.git` is a plain
-ascii file containing `gitdir: <path>`, i.e. the path to the
+ASCII file containing `gitdir: <path>`, i.e. the path to the
 real git repository).
 
 objects::
index ff5c0bc27a416de16f106c15b9076082c6509e42..6fd711996a775d1da8ab2051db98896a601c8971 100644 (file)
@@ -229,7 +229,7 @@ reverting W.  Mainline's history would look like this:
    A---B---C
 
 But if you don't actually need to change commit A, then you need some way to
-recreate it as a new commit with the same changes in it.  The rebase commmand's
+recreate it as a new commit with the same changes in it.  The rebase command's
 --no-ff option provides a way to do this:
 
     $ git rebase [-i] --no-ff P
index 8c32da6deb05b5da700a5bd0a4281bf862b23f2c..093c656048a81e6cdc46d1aecf80fbffd97c94ea 100644 (file)
@@ -112,25 +112,19 @@ $ git tag pu-anchor pu
 $ git rebase master
 * Applying: Redo "revert" using three-way merge machinery.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: Remove git-apply-patch-script.
 First trying simple merge strategy to cherry-pick.
 Simple cherry-pick fails; trying Automatic cherry-pick.
 Removing Documentation/git-apply-patch-script.txt
 Removing git-apply-patch-script
-Finished one cherry-pick.
 * Applying: Document "git cherry-pick" and "git revert"
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: mailinfo and applymbox updates
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: Show commits in topo order and name all commits.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 * Applying: More documentation updates.
 First trying simple merge strategy to cherry-pick.
-Finished one cherry-pick.
 ------------------------------------------------
 
 The temporary tag 'pu-anchor' is me just being careful, in case 'git
index 34d02a24188921ebac1b70f7cf0c6517a45e8fb1..37e67d1a14aa6f8ef5c8914054c03cc6f5cc762b 100755 (executable)
@@ -12,7 +12,7 @@ do
        then
                : did not match
        elif test -f "$T/$h" &&
-          $DIFF -u -I'Last updated [0-9][0-9]-[A-Z][a-z][a-z]-' "$T/$h" "$h"
+               $DIFF -u -I'^Last updated ' "$T/$h" "$h"
        then
                :; # up to date
        else
index a403155052299dd0aaafd6bdfe0fec92d0d0ac7c..b72f5339704e89087992b7d6060e88f43b9d661b 100644 (file)
@@ -15,6 +15,16 @@ merge.renameLimit::
        during a merge; if not specified, defaults to the value of
        diff.renameLimit.
 
+merge.renormalize::
+       Tell git that canonical representation of files in the
+       repository has changed over time (e.g. earlier commits record
+       text files with CRLF line endings, but recent ones use LF line
+       endings).  In such a repository, git can convert the data
+       recorded in commits to a canonical form before performing a
+       merge to reduce unnecessary conflicts.  For more information,
+       see section "Merging branches with differing checkin/checkout
+       attributes" in linkgit:gitattributes[5].
+
 merge.stat::
        Whether to print the diffstat between ORIG_HEAD and the merge result
        at the end of the merge.  True by default.
index a5bc1dbb95b466d0c6e37f683c886663908375e7..049313d6015758fb3e95c21fecc40e021e8aa4bd 100644 (file)
@@ -40,6 +40,18 @@ the other tree did, declaring 'our' history contains all that happened in it.
 theirs;;
        This is opposite of 'ours'.
 
+renormalize;;
+       This runs a virtual check-out and check-in of all three stages
+       of a file when resolving a three-way merge.  This option is
+       meant to be used when merging branches with different clean
+       filters or end-of-line normalization rules.  See "Merging
+       branches with differing checkin/checkout attributes" in
+       linkgit:gitattributes[5] for details.
+
+no-renormalize;;
+       Disables the `renormalize` option.  This overrides the
+       `merge.renormalize` configuration variable.
+
 subtree[=path];;
        This option is a more advanced form of 'subtree' strategy, where
        the strategy makes a guess on how two trees must be shifted to
index d78e121c76ef12b2d72fbdf1558fa967dfdba85b..9b6f3899ec28fb431c1f3969bf5bfb717c12f346 100644 (file)
@@ -1,5 +1,5 @@
 --pretty[='<format>']::
---format[='<format>']::
+--format='<format>'::
 
        Pretty-print the contents of the commit logs in a given format,
        where '<format>' can be one of 'oneline', 'short', 'medium',
diff --git a/Documentation/technical/api-merge.txt b/Documentation/technical/api-merge.txt
new file mode 100644 (file)
index 0000000..a7e050b
--- /dev/null
@@ -0,0 +1,73 @@
+merge API
+=========
+
+The merge API helps a program to reconcile two competing sets of
+improvements to some files (e.g., unregistered changes from the work
+tree versus changes involved in switching to a new branch), reporting
+conflicts if found.  The library called through this API is
+responsible for a few things.
+
+ * determining which trees to merge (recursive ancestor consolidation);
+
+ * lining up corresponding files in the trees to be merged (rename
+   detection, subtree shifting), reporting edge cases like add/add
+   and rename/rename conflicts to the user;
+
+ * performing a three-way merge of corresponding files, taking
+   path-specific merge drivers (specified in `.gitattributes`)
+   into account.
+
+Low-level (single file) merge
+-----------------------------
+
+`ll_merge`::
+
+       Perform a three-way single-file merge in core.  This is
+       a thin wrapper around `xdl_merge` that takes the path and
+       any merge backend specified in `.gitattributes` or
+       `.git/info/attributes` into account.  Returns 0 for a
+       clean merge.
+
+The caller:
+
+1. allocates an mmbuffer_t variable for the result;
+2. allocates and fills variables with the file's original content
+   and two modified versions (using `read_mmfile`, for example);
+3. calls ll_merge();
+4. reads the output from result_buf.ptr and result_buf.size;
+5. releases buffers when finished (free(ancestor.ptr); free(ours.ptr);
+   free(theirs.ptr); free(result_buf.ptr);).
+
+If the modifications do not merge cleanly, `ll_merge` will return a
+nonzero value and `result_buf` will generally include a description of
+the conflict bracketed by markers such as the traditional `<<<<<<<`
+and `>>>>>>>`.
+
+The `ancestor_label`, `our_label`, and `their_label` parameters are
+used to label the different sides of a conflict if the merge driver
+supports this.
+
+The `flag` parameter is a bitfield:
+
+ - The `LL_OPT_VIRTUAL_ANCESTOR` bit indicates whether this is an
+   internal merge to consolidate ancestors for a recursive merge.
+
+ - The `LL_OPT_FAVOR_MASK` bits allow local conflicts to be automatically
+   resolved in favor of one side or the other (as in 'git merge-file'
+   `--ours`/`--theirs`/`--union`).
+   They can be populated by `create_ll_flag`, whose argument can be
+   `XDL_MERGE_FAVOR_OURS`, `XDL_MERGE_FAVOR_THEIRS`, or
+   `XDL_MERGE_FAVOR_UNION`.
+
+Everything else
+---------------
+
+Talk about <merge-recursive.h> and merge_file():
+
+ - merge_trees() to merge with rename detection
+ - merge_recursive() for ancestor consolidation
+ - try_merge_command() for other strategies
+ - conflict format
+ - merge options
+
+(Daniel, Miklos, Stephan, JC)
index 55b728632cb7fa18de0d1dc070f5007acef053e0..14af37c3f14854e87d9d28f60b5ff72eb189c37f 100644 (file)
@@ -42,6 +42,8 @@ information.
 
 * `data` can be anything the `fn` callback would want to use.
 
+* `show_all_errors` tells whether to stop at the first error or not.
+
 Initializing
 ------------
 
index 1dcd1e7f1ede3382ae992a266f5437fe06f8dc75..289019478d16719e5d2c86b7a17017005bd9a80d 100644 (file)
@@ -25,7 +25,7 @@ The ssh and git protocols additionally support ~username expansion:
 - git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
 - {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
 
-For local respositories, also supported by git natively, the following
+For local repositories, also supported by git natively, the following
 syntaxes may be used:
 
 - /path/to/repo.git/
index e88f50cafb5dcb4cd2f4d183a53382ccc84b91a7..f6d301a10f7b57edb623de8d5a78c90f4fbc71a7 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.2
+DEF_VER=v1.7.2.GIT
 
 LF='
 '
diff --git a/INSTALL b/INSTALL
index 61086ab1204a4304cb1d84eeea9d1649878ac9e1..59200b730ec00a63f981691f7fd37f2eacb11653 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -157,3 +157,36 @@ Issues of note:
    It has been reported that docbook-xsl version 1.72 and 1.73 are
    buggy; 1.72 misformats manual pages for callouts, and 1.73 needs
    the patch in contrib/patches/docbook-xsl-manpages-charmap.patch
+
+   Users attempting to build the documentation on Cygwin may need to ensure
+   that the /etc/xml/catalog file looks something like this:
+
+   <?xml version="1.0"?>
+   <!DOCTYPE catalog PUBLIC
+      "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN"
+      "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"
+   >
+   <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
+     <rewriteURI
+       uriStartString = "http://docbook.sourceforge.net/release/xsl/current"
+       rewritePrefix = "/usr/share/sgml/docbook/xsl-stylesheets"
+     />
+     <rewriteURI
+       uriStartString="http://www.oasis-open.org/docbook/xml/4.5"
+       rewritePrefix="/usr/share/sgml/docbook/xml-dtd-4.5"
+     />
+  </catalog>
+
+  This can be achieved with the following two xmlcatalog commands:
+
+  xmlcatalog --noout \
+     --add rewriteURI \
+        http://docbook.sourceforge.net/release/xsl/current \
+        /usr/share/sgml/docbook/xsl-stylesheets \
+     /etc/xml/catalog
+
+  xmlcatalog --noout \
+     --add rewriteURI \
+         http://www.oasis-open.org/docbook/xml/4.5/xsl/current \
+         /usr/share/sgml/docbook/xml-dtd-4.5 \
+     /etc/xml/catalog
index bc3c57058faba66f6a7a947e1e9642f47053b5bb..32e3eb385d82126396c452f43b1fa70d5a7b4321 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -68,6 +68,8 @@ all::
 #
 # Define NO_MKSTEMPS if you don't have mkstemps in the C library.
 #
+# Define NO_STRTOK_R if you don't have strtok_r in the C library.
+#
 # Define NO_LIBGEN_H if you don't have libgen.h.
 #
 # Define NEEDS_LIBGEN if your libgen needs -lgen when linking
@@ -408,12 +410,17 @@ TEST_PROGRAMS_NEED_X += test-date
 TEST_PROGRAMS_NEED_X += test-delta
 TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 TEST_PROGRAMS_NEED_X += test-genrandom
+TEST_PROGRAMS_NEED_X += test-line-buffer
 TEST_PROGRAMS_NEED_X += test-match-trees
+TEST_PROGRAMS_NEED_X += test-obj-pool
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
 TEST_PROGRAMS_NEED_X += test-run-command
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-string-pool
+TEST_PROGRAMS_NEED_X += test-svn-fe
+TEST_PROGRAMS_NEED_X += test-treap
 TEST_PROGRAMS_NEED_X += test-index-version
 
 TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
@@ -468,6 +475,7 @@ export PYTHON_PATH
 
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
+VCSSVN_LIB=vcs-svn/lib.a
 
 LIB_H += advice.h
 LIB_H += archive.h
@@ -1035,6 +1043,7 @@ ifeq ($(uname_S),Windows)
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
+       NO_STRTOK_R = YesPlease
        NO_MEMMEM = YesPlease
        # NEEDS_LIBICONV = YesPlease
        NO_ICONV = YesPlease
@@ -1089,6 +1098,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        NO_STRLCPY = YesPlease
+       NO_STRTOK_R = YesPlease
        NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        OLD_ICONV = YesPlease
@@ -1319,6 +1329,10 @@ endif
 ifdef NO_STRTOULL
        COMPAT_CFLAGS += -DNO_STRTOULL
 endif
+ifdef NO_STRTOK_R
+       COMPAT_CFLAGS += -DNO_STRTOK_R
+       COMPAT_OBJS += compat/strtok_r.o
+endif
 ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
@@ -1739,7 +1753,9 @@ ifndef NO_CURL
 endif
 XDIFF_OBJS = xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
        xdiff/xmerge.o xdiff/xpatience.o
-OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS)
+VCSSVN_OBJS = vcs-svn/string_pool.o vcs-svn/line_buffer.o \
+       vcs-svn/repo_tree.o vcs-svn/fast_export.o vcs-svn/svndump.o
+OBJECTS := $(GIT_OBJS) $(XDIFF_OBJS) $(VCSSVN_OBJS)
 
 dep_files := $(foreach f,$(OBJECTS),$(dir $f).depend/$(notdir $f).d)
 dep_dirs := $(addsuffix .depend,$(sort $(dir $(OBJECTS))))
@@ -1854,12 +1870,18 @@ builtin/prune.o builtin/reflog.o reachable.o: reachable.h
 builtin/commit.o builtin/revert.o wt-status.o: wt-status.h
 builtin/tar-tree.o archive-tar.o: tar.h
 builtin/pack-objects.o: thread-utils.h
+connect.o transport.o http-backend.o: url.h
 http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h
-http.o http-walker.o http-push.o remote-curl.o: http.h
+http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h
 
 xdiff-interface.o $(XDIFF_OBJS): \
        xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
        xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
+
+$(VCSSVN_OBJS): \
+       vcs-svn/obj_pool.h vcs-svn/trp.h vcs-svn/string_pool.h \
+       vcs-svn/line_buffer.h vcs-svn/repo_tree.h vcs-svn/fast_export.h \
+       vcs-svn/svndump.h
 endif
 
 exec_cmd.s exec_cmd.o: EXTRA_CPPFLAGS = \
@@ -1872,7 +1894,7 @@ builtin/init-db.s builtin/init-db.o: EXTRA_CPPFLAGS = \
 
 config.s config.o: EXTRA_CPPFLAGS = -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
-http.s http.o: EXTRA_CPPFLAGS = -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
+http.s http.o: EXTRA_CPPFLAGS = -DGIT_HTTP_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
 http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
@@ -1908,6 +1930,8 @@ $(LIB_FILE): $(LIB_OBJS)
 $(XDIFF_LIB): $(XDIFF_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
 
+$(VCSSVN_LIB): $(VCSSVN_OBJS)
+       $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
 
 doc:
        $(MAKE) -C Documentation all
@@ -2006,12 +2030,18 @@ test-date$X: date.o ctype.o
 
 test-delta$X: diff-delta.o patch-delta.o
 
+test-line-buffer$X: vcs-svn/lib.a
+
 test-parse-options$X: parse-options.o
 
+test-string-pool$X: vcs-svn/lib.a
+
+test-svn-fe$X: vcs-svn/lib.a
+
 .PRECIOUS: $(TEST_OBJS)
 
 test-%$X: test-%.o $(GITLIBS)
-       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(filter %.a,$^) $(LIBS)
 
 check-sha1:: test-sha1$X
        ./test-sha1.sh
@@ -2075,10 +2105,19 @@ endif
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
        { test "$$bindir/" = "$$execdir/" || \
-               { $(RM) "$$execdir/git$X" && \
+         for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
+               $(RM) "$$execdir/$$p" && \
                test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
-               ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
-               cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
+               ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
+               cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
+         done; \
+       } && \
+       for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
+               $(RM) "$$bindir/$$p" && \
+               ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
+               ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
+               cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
+       done && \
        for p in $(BUILT_INS); do \
                $(RM) "$$execdir/$$p" && \
                ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
@@ -2177,8 +2216,8 @@ distclean: clean
        $(RM) configure
 
 clean:
-       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
-               builtin/*.o $(LIB_FILE) $(XDIFF_LIB)
+       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
+               builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
        $(RM) -r bin-wrappers
@@ -2254,6 +2293,7 @@ check-docs::
                documented,gitglossary | \
                documented,githooks | \
                documented,gitrepository-layout | \
+               documented,gitrevisions | \
                documented,gittutorial | \
                documented,gittutorial-2 | \
                documented,git-bisect-lk2009 | \
index 3da01a5622c0b6c581a701dbb5421fb84e87df80..0e11dea6d0d1b888ddfc2825bb197aae8027d459 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.7.2.txt
\ No newline at end of file
+Documentation/RelNotes-1.7.3.txt
\ No newline at end of file
diff --git a/alias.c b/alias.c
index 372b7d809301f9e3e936459e405cd6f2627bd4a9..eb9f08b912b2089d434926fc6083ac5ab90c73bc 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -22,6 +22,13 @@ char *alias_lookup(const char *alias)
        return alias_val;
 }
 
+#define SPLIT_CMDLINE_BAD_ENDING 1
+#define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
+static const char *split_cmdline_errors[] = {
+       "cmdline ends with \\",
+       "unclosed quote"
+};
+
 int split_cmdline(char *cmdline, const char ***argv)
 {
        int src, dst, count = 0, size = 16;
@@ -53,7 +60,7 @@ int split_cmdline(char *cmdline, const char ***argv)
                                if (!c) {
                                        free(*argv);
                                        *argv = NULL;
-                                       return error("cmdline ends with \\");
+                                       return -SPLIT_CMDLINE_BAD_ENDING;
                                }
                        }
                        cmdline[dst++] = c;
@@ -66,7 +73,7 @@ int split_cmdline(char *cmdline, const char ***argv)
        if (quoted) {
                free(*argv);
                *argv = NULL;
-               return error("unclosed quote");
+               return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
        }
 
        ALLOC_GROW(*argv, count+1, size);
@@ -75,3 +82,6 @@ int split_cmdline(char *cmdline, const char ***argv)
        return count;
 }
 
+const char *split_cmdline_strerror(int split_cmdline_errno) {
+       return split_cmdline_errors[-split_cmdline_errno-1];
+}
index d700af3b62f35091f9c628a5a2c0d8449e2fe439..edd68534fa847ad839fe8554e6864b39de4fcde0 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -33,6 +33,7 @@ static void format_subst(const struct commit *commit,
        struct strbuf fmt = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
        ctx.date_mode = DATE_NORMAL;
+       ctx.abbrev = DEFAULT_ABBREV;
 
        if (src == buf->buf)
                to_free = strbuf_detach(buf, NULL);
index e459feebbf90c6557dbf3ff913f83a57a8afb210..781b5754f0e533008694e71ac7cfe2e52b2d0ac6 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -7,9 +7,9 @@
 #define say1(a,b) fprintf(stderr, a, b)
 #define say2(a,b,c) fprintf(stderr, a, b, c)
 #else
-#define say(a) do {} while(0)
-#define say1(a,b) do {} while(0)
-#define say2(a,b,c) do {} while(0)
+#define say(a) do { /* nothing */ } while (0)
+#define say1(a,b) do { /* nothing */ } while (0)
+#define say2(a,b,c) do { /* nothing */ } while (0)
 #endif
 
 static const char en85[] = {
index b556b11610480afd80cddd86a81af9737254ee36..060c042f8bcc2d402cb9908be3bdbd3bb180a862 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -141,7 +141,8 @@ static void show_list(const char *debug, int counted, int nr,
                enum object_type type;
                unsigned long size;
                char *buf = read_sha1_file(commit->object.sha1, &type, &size);
-               char *ep, *sp;
+               const char *subject_start;
+               int subject_len;
 
                fprintf(stderr, "%c%c%c ",
                        (flags & TREESAME) ? ' ' : 'T',
@@ -156,13 +157,9 @@ static void show_list(const char *debug, int counted, int nr,
                        fprintf(stderr, " %.*s", 8,
                                sha1_to_hex(pp->item->object.sha1));
 
-               sp = strstr(buf, "\n\n");
-               if (sp) {
-                       sp += 2;
-                       for (ep = sp; *ep && *ep != '\n'; ep++)
-                               ;
-                       fprintf(stderr, " %.*s", (int)(ep - sp), sp);
-               }
+               subject_len = find_commit_subject(buf, &subject_start);
+               if (subject_len)
+                       fprintf(stderr, " %.*s", subject_len, subject_start);
                fprintf(stderr, "\n");
        }
 }
index 2ab42aaf4da38b4ea45ef7f0a0f6b807313d4a22..93dc866f8c09a2da2308c4fd677178b043f2d4d5 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -159,7 +159,7 @@ void create_branch(const char *head,
                        dont_change_ref = 1;
                else if (!force)
                        die("A branch named '%s' already exists.", name);
-               else if (!is_bare_repository() && !strcmp(head, name))
+               else if (!is_bare_repository() && head && !strcmp(head, name))
                        die("Cannot force update the current branch.");
                forcing = 1;
        }
index 12ef9ea8afb0aa1e554e3ce6c6085e97ff7e7466..470520bde7ceec8d01f401e7421927bd48fd69ee 100644 (file)
@@ -2979,8 +2979,7 @@ static void build_fake_ancestor(struct patch *list, const char *filename)
                else if (get_sha1(patch->old_sha1_prefix, sha1))
                        /* git diff has no index line for mode/type changes */
                        if (!patch->lines_added && !patch->lines_deleted) {
-                               if (get_current_sha1(patch->new_name, sha1) ||
-                                   get_current_sha1(patch->old_name, sha1))
+                               if (get_current_sha1(patch->old_name, sha1))
                                        die("mode change for %s, which is not "
                                                "in current HEAD", name);
                                sha1_ptr = sha1;
@@ -3607,11 +3606,11 @@ static int option_parse_directory(const struct option *opt,
        return 0;
 }
 
-int cmd_apply(int argc, const char **argv, const char *unused_prefix)
+int cmd_apply(int argc, const char **argv, const char *prefix_)
 {
        int i;
        int errs = 0;
-       int is_not_gitdir;
+       int is_not_gitdir = !startup_info->have_repository;
        int binary;
        int force_apply = 0;
 
@@ -3684,7 +3683,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
                OPT_END()
        };
 
-       prefix = setup_git_directory_gently(&is_not_gitdir);
+       prefix = prefix_;
        prefix_length = prefix ? strlen(prefix) : 0;
        git_config(git_apply_config, NULL);
        if (apply_default_whitespace)
index 01e62fdeb00b2d085efe96a07f2ee77776081a69..101535448f2b0405c280387b63f7ce82fb7dd350 100644 (file)
@@ -1407,7 +1407,8 @@ static void get_commit_info(struct commit *commit,
                            int detailed)
 {
        int len;
-       char *tmp, *endp, *reencoded, *message;
+       const char *subject;
+       char *reencoded, *message;
        static char author_name[1024];
        static char author_mail[1024];
        static char committer_name[1024];
@@ -1449,22 +1450,13 @@ static void get_commit_info(struct commit *commit,
                    &ret->committer_time, &ret->committer_tz);
 
        ret->summary = summary_buf;
-       tmp = strstr(message, "\n\n");
-       if (!tmp) {
-       error_out:
+       len = find_commit_subject(message, &subject);
+       if (len && len < sizeof(summary_buf)) {
+               memcpy(summary_buf, subject, len);
+               summary_buf[len] = 0;
+       } else {
                sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
-               free(reencoded);
-               return;
        }
-       tmp += 2;
-       endp = strchr(tmp, '\n');
-       if (!endp)
-               endp = tmp + strlen(tmp);
-       len = endp - tmp;
-       if (len >= sizeof(summary_buf) || len == 0)
-               goto error_out;
-       memcpy(summary_buf, tmp, len);
-       summary_buf[len] = 0;
        free(reencoded);
 }
 
@@ -2376,11 +2368,11 @@ parse_done:
         *
         * The remaining are:
         *
-        * (1) if dashdash_pos != 0, its either
+        * (1) if dashdash_pos != 0, it is either
         *     "blame [revisions] -- <path>" or
         *     "blame -- <path> <rev>"
         *
-        * (2) otherwise, its one of the two:
+        * (2) otherwise, it is one of the two:
         *     "blame [revisions] <path>"
         *     "blame <path> <rev>"
         *
index 2006cc5cd5cc4d381189f5cf7bdd09fcdd66b9ca..80649ba0b28846d09b49a791dd25af9a2907f75f 100644 (file)
@@ -18,7 +18,6 @@ static const char builtin_bundle_usage[] =
 int cmd_bundle(int argc, const char **argv, const char *prefix)
 {
        struct bundle_header header;
-       int nongit;
        const char *cmd, *bundle_file;
        int bundle_fd = -1;
        char buffer[PATH_MAX];
@@ -31,7 +30,6 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
        argc -= 2;
        argv += 2;
 
-       prefix = setup_git_directory_gently(&nongit);
        if (prefix && bundle_file[0] != '/') {
                snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
                bundle_file = buffer;
@@ -54,11 +52,11 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
                return !!list_bundle_refs(&header, argc, argv);
        }
        if (!strcmp(cmd, "create")) {
-               if (nongit)
+               if (!startup_info->have_repository)
                        die("Need a repository to create a bundle.");
                return !!create_bundle(&header, bundle_file, argc, argv);
        } else if (!strcmp(cmd, "unbundle")) {
-               if (nongit)
+               if (!startup_info->have_repository)
                        die("Need a repository to unbundle.");
                return !!unbundle(&header, bundle_fd) ||
                        list_bundle_refs(&header, argc, argv);
index b106c65d80dfc8fe794d46e34ea1ff78b04f3056..ae3f28115a7a4d65d8bb6f284cd1ececa4f9c2ef 100644 (file)
@@ -33,28 +33,38 @@ static void collapse_slashes(char *dst, const char *src)
        *dst = '\0';
 }
 
+static int check_ref_format_branch(const char *arg)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int nongit;
+
+       setup_git_directory_gently(&nongit);
+       if (strbuf_check_branch_ref(&sb, arg))
+               die("'%s' is not a valid branch name", arg);
+       printf("%s\n", sb.buf + 11);
+       return 0;
+}
+
+static int check_ref_format_print(const char *arg)
+{
+       char *refname = xmalloc(strlen(arg) + 1);
+
+       if (check_ref_format(arg))
+               return 1;
+       collapse_slashes(refname, arg);
+       printf("%s\n", refname);
+       return 0;
+}
+
 int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 {
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(builtin_check_ref_format_usage);
 
-       if (argc == 3 && !strcmp(argv[1], "--branch")) {
-               struct strbuf sb = STRBUF_INIT;
-
-               if (strbuf_check_branch_ref(&sb, argv[2]))
-                       die("'%s' is not a valid branch name", argv[2]);
-               printf("%s\n", sb.buf + 11);
-               exit(0);
-       }
-       if (argc == 3 && !strcmp(argv[1], "--print")) {
-               char *refname = xmalloc(strlen(argv[2]) + 1);
-
-               if (check_ref_format(argv[2]))
-                       exit(1);
-               collapse_slashes(refname, argv[2]);
-               printf("%s\n", refname);
-               exit(0);
-       }
+       if (argc == 3 && !strcmp(argv[1], "--branch"))
+               return check_ref_format_branch(argv[2]);
+       if (argc == 3 && !strcmp(argv[1], "--print"))
+               return check_ref_format_print(argv[2]);
        if (argc != 2)
                usage(builtin_check_ref_format_usage);
        return !!check_ref_format(argv[1]);
index 1994be92c66257da18c31502bda605be170fd092..ff5ac1e0ff20ad8e5b30289cd3f24f0c64eb0627 100644 (file)
@@ -32,7 +32,11 @@ struct checkout_opts {
        int writeout_stage;
        int writeout_error;
 
+       /* not set by parse_options */
+       int branch_exists;
+
        const char *new_branch;
+       const char *new_branch_force;
        const char *new_orphan_branch;
        int new_branch_log;
        enum branch_track track;
@@ -150,6 +154,10 @@ static int checkout_merged(int pos, struct checkout *state)
        read_mmblob(&ours, active_cache[pos+1]->sha1);
        read_mmblob(&theirs, active_cache[pos+2]->sha1);
 
+       /*
+        * NEEDSWORK: re-create conflicts from merges with
+        * merge.renormalize set, too
+        */
        status = ll_merge(&result_buf, path, &ancestor, "base",
                          &ours, "ours", &theirs, "theirs", 0);
        free(ancestor.ptr);
@@ -279,7 +287,6 @@ static void show_local_changes(struct object *head)
        struct rev_info rev;
        /* I think we want full paths, even if we're in a subdirectory. */
        init_revisions(&rev, NULL);
-       rev.abbrev = 0;
        rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
        if (diff_setup_done(&rev.diffopt) < 0)
                die("diff_setup_done failed");
@@ -373,7 +380,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.src_index = &the_index;
                topts.dst_index = &the_index;
 
-               topts.msgs.not_uptodate_file = "You have local changes to '%s'; cannot switch branches.";
+               set_porcelain_error_msgs(topts.msgs, "checkout");
 
                refresh_cache(REFRESH_QUIET);
 
@@ -392,6 +399,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.dir = xcalloc(1, sizeof(*topts.dir));
                topts.dir->flags |= DIR_SHOW_IGNORED;
                topts.dir->exclude_per_dir = ".gitignore";
+               topts.show_all_errors = 1;
                tree = parse_tree_indirect(old->commit ?
                                           old->commit->object.sha1 :
                                           (unsigned char *)EMPTY_TREE_SHA1_BIN);
@@ -433,6 +441,13 @@ static int merge_working_tree(struct checkout_opts *opts,
                         */
 
                        add_files_to_cache(NULL, NULL, 0);
+                       /*
+                        * NEEDSWORK: carrying over local changes
+                        * when branches have different end-of-line
+                        * normalization (or clean+smudge rules) is
+                        * a pain; plumb in an option to set
+                        * o.renormalize?
+                        */
                        init_merge_options(&o);
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
@@ -511,7 +526,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                        }
                }
                else
-                       create_branch(old->name, opts->new_branch, new->name, 0,
+                       create_branch(old->name, opts->new_branch, new->name,
+                                     opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log, opts->track);
                new->name = opts->new_branch;
                setup_branch_path(new);
@@ -529,9 +545,12 @@ static void update_refs_for_switch(struct checkout_opts *opts,
                        if (old->path && !strcmp(new->path, old->path))
                                fprintf(stderr, "Already on '%s'\n",
                                        new->name);
-                       else
+                       else if (opts->new_branch)
                                fprintf(stderr, "Switched to%s branch '%s'\n",
-                                       opts->new_branch ? " a new" : "",
+                                       opts->branch_exists ? " and reset" : " a new",
+                                       new->name);
+                       else
+                               fprintf(stderr, "Switched to branch '%s'\n",
                                        new->name);
                }
                if (old->path && old->name) {
@@ -657,7 +676,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        int dwim_new_local_branch = 1;
        struct option options[] = {
                OPT__QUIET(&opts.quiet),
-               OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+               OPT_STRING('b', NULL, &opts.new_branch, "branch",
+                          "create and checkout a new branch"),
+               OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
+                          "create/reset and checkout a branch"),
                OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
                OPT_SET_INT('t', "track",  &opts.track, "track",
                        BRANCH_TRACK_EXPLICIT),
@@ -688,6 +710,14 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
 
+       /* we can assume from now on new_branch = !new_branch_force */
+       if (opts.new_branch && opts.new_branch_force)
+               die("-B cannot be used with -b");
+
+       /* copy -B over to -b, so that we can just check the latter */
+       if (opts.new_branch_force)
+               opts.new_branch = opts.new_branch_force;
+
        if (patch_mode && (opts.track > 0 || opts.new_branch
                           || opts.new_branch_log || opts.merge || opts.force))
                die ("--patch is incompatible with all other options");
@@ -709,7 +739,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
 
        if (opts.new_orphan_branch) {
                if (opts.new_branch)
-                       die("--orphan and -b are mutually exclusive");
+                       die("--orphan and -b|-B are mutually exclusive");
                if (opts.track > 0)
                        die("--orphan cannot be used with -t");
                opts.new_branch = opts.new_orphan_branch;
@@ -858,8 +888,12 @@ no_reference:
                if (strbuf_check_branch_ref(&buf, opts.new_branch))
                        die("git checkout: we do not like '%s' as a branch name.",
                            opts.new_branch);
-               if (!get_sha1(buf.buf, rev))
-                       die("git checkout: branch %s already exists", opts.new_branch);
+               if (!get_sha1(buf.buf, rev)) {
+                       opts.branch_exists = 1;
+                       if (!opts.new_branch_force)
+                               die("git checkout: branch %s already exists",
+                                   opts.new_branch);
+               }
                strbuf_release(&buf);
        }
 
index fac64e6cd3717dfffb0f54124de9545f7cdc391a..b508d2cab482dffc97be1e8ee0481a6bb6157cf2 100644 (file)
 #include "cache.h"
 #include "dir.h"
 #include "parse-options.h"
+#include "string-list.h"
 #include "quote.h"
 
 static int force = -1; /* unset */
 
 static const char *const builtin_clean_usage[] = {
-       "git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+       "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
        NULL
 };
 
@@ -26,6 +27,13 @@ static int git_clean_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+       struct string_list *exclude_list = opt->value;
+       string_list_append(exclude_list, arg);
+       return 0;
+}
+
 int cmd_clean(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -36,6 +44,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        struct dir_struct dir;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
+       struct string_list exclude_list = { NULL, 0, 0, 0 };
        const char *qname;
        char *seen = NULL;
        struct option options[] = {
@@ -44,6 +53,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                OPT_BOOLEAN('f', "force", &force, "force"),
                OPT_BOOLEAN('d', NULL, &remove_directories,
                                "remove whole directories"),
+               { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+                 "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
                OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
                OPT_BOOLEAN('X', NULL, &ignored_only,
                                "remove only ignored files"),
@@ -81,6 +92,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        if (!ignored)
                setup_standard_excludes(&dir);
 
+       for (i = 0; i < exclude_list.nr; i++)
+               add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
+
        pathspec = get_pathspec(prefix, argv);
 
        fill_directory(&dir, pathspec);
@@ -167,5 +181,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        free(seen);
 
        strbuf_release(&directory);
+       string_list_clear(&exclude_list, 0);
        return (errors != 0);
 }
index a78dbd83bf04c7080487b8508321d79d848e9b1f..66fdd2202495b2893c79b4f92b0fc2e715f5b7bb 100644 (file)
@@ -25,6 +25,7 @@
 #include "rerere.h"
 #include "unpack-trees.h"
 #include "quote.h"
+#include "submodule.h"
 
 static const char * const builtin_commit_usage[] = {
        "git commit [options] [--] <filepattern>...",
@@ -147,7 +148,7 @@ static struct option builtin_commit_options[] = {
                    "terminate entries with NUL"),
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        OPT_BOOLEAN(0, "no-post-rewrite", &no_post_rewrite, "bypass post-rewrite hook"),
-       { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
+       { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        /* end commit contents options */
 
        { OPTION_BOOLEAN, 0, "allow-empty", &allow_empty, NULL,
@@ -1073,6 +1074,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
                status_format = STATUS_FORMAT_PORCELAIN;
 
        wt_status_prepare(&s);
+       gitmodules_config();
        git_config(git_status_config, &s);
        in_merge = file_exists(git_path("MERGE_HEAD"));
        argc = parse_options(argc, argv, prefix,
@@ -1163,7 +1165,6 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
        init_revisions(&rev, prefix);
        setup_revisions(0, NULL, &rev, NULL);
 
-       rev.abbrev = 0;
        rev.diff = 1;
        rev.diffopt.output_format =
                DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
index f3d1660d023f7893373436617a9f729ce1d60f4d..ca4a0db4a79241d7ded005483b4693967eea636f 100644 (file)
@@ -20,7 +20,7 @@ static char delim = '=';
 static char key_delim = ' ';
 static char term = '\n';
 
-static int use_global_config, use_system_config;
+static int use_global_config, use_system_config, use_local_config;
 static const char *given_config_file;
 static int actions, types;
 static const char *get_color_slot, *get_colorbool_slot;
@@ -51,6 +51,7 @@ static struct option builtin_config_options[] = {
        OPT_GROUP("Config file location"),
        OPT_BOOLEAN(0, "global", &use_global_config, "use global config file"),
        OPT_BOOLEAN(0, "system", &use_system_config, "use system config file"),
+       OPT_BOOLEAN(0, "local", &use_local_config, "use repository config file"),
        OPT_STRING('f', "file", &given_config_file, "FILE", "use given config file"),
        OPT_GROUP("Action"),
        OPT_BIT(0, "get", &actions, "get value: name [value-regex]", ACTION_GET),
@@ -330,11 +331,10 @@ static int get_colorbool(int print)
                return get_colorbool_found ? 0 : 1;
 }
 
-int cmd_config(int argc, const char **argv, const char *unused_prefix)
+int cmd_config(int argc, const char **argv, const char *prefix)
 {
-       int nongit;
+       int nongit = !startup_info->have_repository;
        char *value;
-       const char *prefix = setup_git_directory_gently(&nongit);
 
        config_exclusive_filename = getenv(CONFIG_ENVIRONMENT);
 
@@ -342,7 +342,7 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
 
-       if (use_global_config + use_system_config + !!given_config_file > 1) {
+       if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
                error("only one config file at a time.");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
@@ -358,6 +358,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
        }
        else if (use_system_config)
                config_exclusive_filename = git_etc_gitconfig();
+       else if (use_local_config)
+               config_exclusive_filename = git_pathdup("config");
        else if (given_config_file) {
                if (!is_absolute_path(given_config_file) && prefix)
                        config_exclusive_filename = prefix_filename(prefix,
index 5b64011de8222f06b5c772a6461278dea152919e..951c7c8994704543fc1784c87a17a1aa47ede257 100644 (file)
@@ -8,6 +8,7 @@
 #include "commit.h"
 #include "revision.h"
 #include "builtin.h"
+#include "submodule.h"
 
 static const char diff_files_usage[] =
 "git diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
@@ -20,6 +21,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
        unsigned options = 0;
 
        init_revisions(&rev, prefix);
+       gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
 
index 04837494feba401c7f689eab5574768d3fd126de..2eb32bd9da8cfa4d7b1b99f9d22ffa5b3d6202e5 100644 (file)
@@ -3,6 +3,7 @@
 #include "commit.h"
 #include "revision.h"
 #include "builtin.h"
+#include "submodule.h"
 
 static const char diff_cache_usage[] =
 "git diff-index [-m] [--cached] "
@@ -17,6 +18,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        int result;
 
        init_revisions(&rev, prefix);
+       gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        rev.abbrev = 0;
 
index 3c78bda5664e20086bcd500105b039d6241e3782..0d2a3e9fa2023cc673f1f6d9e17d3deeca1c7e9d 100644 (file)
@@ -3,6 +3,7 @@
 #include "commit.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "submodule.h"
 
 static struct rev_info log_tree_opt;
 
@@ -112,6 +113,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        int read_stdin = 0;
 
        init_revisions(opt, prefix);
+       gitmodules_config();
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        opt->abbrev = 0;
        opt->diff = 1;
index 89ae89cde1e6d8a0a2e02209f5396263a81bda5b..a43d3263638daf083c52f37f3bd0872327d7459b 100644 (file)
@@ -13,6 +13,7 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "submodule.h"
 
 struct blobinfo {
        unsigned char sha1[20];
@@ -279,6 +280,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
         */
 
        prefix = setup_git_directory_gently(&nongit);
+       gitmodules_config();
        git_config(git_diff_ui_config, NULL);
 
        if (diff_use_color_default == -1)
index 9fe25ff0b3dc0ae89c668b146b46a4ccbd457361..a9bbf8653d1a8fa704a38c06102ec3ace4a59a28 100644 (file)
@@ -27,6 +27,7 @@ static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
 static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
 static int fake_missing_tagger;
 static int no_data;
+static int full_tree;
 
 static int parse_opt_signed_tag_mode(const struct option *opt,
                                     const char *arg, int unset)
@@ -147,10 +148,39 @@ static void handle_object(const unsigned char *sha1)
        free(buf);
 }
 
+static int depth_first(const void *a_, const void *b_)
+{
+       const struct diff_filepair *a = *((const struct diff_filepair **)a_);
+       const struct diff_filepair *b = *((const struct diff_filepair **)b_);
+       const char *name_a, *name_b;
+       int len_a, len_b, len;
+       int cmp;
+
+       name_a = a->one ? a->one->path : a->two->path;
+       name_b = b->one ? b->one->path : b->two->path;
+
+       len_a = strlen(name_a);
+       len_b = strlen(name_b);
+       len = (len_a < len_b) ? len_a : len_b;
+
+       /* strcmp will sort 'd' before 'd/e', we want 'd/e' before 'd' */
+       cmp = memcmp(name_a, name_b, len);
+       if (cmp)
+               return cmp;
+       return (len_b - len_a);
+}
+
 static void show_filemodify(struct diff_queue_struct *q,
                            struct diff_options *options, void *data)
 {
        int i;
+
+       /*
+        * Handle files below a directory first, in case they are all deleted
+        * and the directory changes to a file or symlink.
+        */
+       qsort(q->queue, q->nr, sizeof(q->queue[0]), depth_first);
+
        for (i = 0; i < q->nr; i++) {
                struct diff_filespec *ospec = q->queue[i]->one;
                struct diff_filespec *spec = q->queue[i]->two;
@@ -241,7 +271,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
                message += 2;
 
        if (commit->parents &&
-           get_object_mark(&commit->parents->item->object) != 0) {
+           get_object_mark(&commit->parents->item->object) != 0 &&
+           !full_tree) {
                parse_commit(commit->parents->item);
                diff_tree_sha1(commit->parents->item->tree->object.sha1,
                               commit->tree->object.sha1, "", &rev->diffopt);
@@ -281,6 +312,8 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
                i++;
        }
 
+       if (full_tree)
+               printf("deleteall\n");
        log_tree_diff_flush(rev);
        rev->diffopt.output_format = saved_output_format;
 
@@ -565,8 +598,8 @@ static void import_marks(char *input_file)
 int cmd_fast_export(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
-       struct object_array commits = { 0, 0, NULL };
-       struct string_list extra_refs = { NULL, 0, 0, 0 };
+       struct object_array commits = OBJECT_ARRAY_INIT;
+       struct string_list extra_refs = STRING_LIST_INIT_NODUP;
        struct commit *commit;
        char *export_filename = NULL, *import_filename = NULL;
        struct option options[] = {
@@ -584,6 +617,8 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
                             "Import marks from this file"),
                OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
                             "Fake a tagger when tags lack one"),
+               OPT_BOOLEAN(0, "full-tree", &full_tree,
+                            "Output full tree for each commit"),
                { OPTION_NEGBIT, 0, "data", &no_data, NULL,
                        "Skip output of blob data",
                        PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
@@ -608,6 +643,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
        if (import_filename)
                import_marks(import_filename);
 
+       if (import_filename && revs.prune_data)
+               full_tree = 1;
+
        get_tags_and_duplicates(&revs.pending, &extra_refs);
 
        if (prepare_revision_walk(&revs))
index 6eb1dfea092e2873c5910bf4389d3b071786d8b0..fab3fce5122fc8ea8da0688e4b0e207ce4097127 100644 (file)
@@ -544,40 +544,14 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
        return 0;
 }
 
-struct tag_data {
-       struct ref **head;
-       struct ref ***tail;
-};
-
-static int add_to_tail(struct string_list_item *item, void *cb_data)
-{
-       struct tag_data *data = (struct tag_data *)cb_data;
-       struct ref *rm = NULL;
-
-       /* We have already decided to ignore this item */
-       if (!item->util)
-               return 0;
-
-       rm = alloc_ref(item->string);
-       rm->peer_ref = alloc_ref(item->string);
-       hashcpy(rm->old_sha1, item->util);
-
-       **data->tail = rm;
-       *data->tail = &rm->next;
-
-       return 0;
-}
-
 static void find_non_local_tags(struct transport *transport,
                        struct ref **head,
                        struct ref ***tail)
 {
-       struct string_list existing_refs = { NULL, 0, 0, 0 };
-       struct string_list remote_refs = { NULL, 0, 0, 0 };
-       struct tag_data data;
+       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+       struct string_list remote_refs = STRING_LIST_INIT_NODUP;
        const struct ref *ref;
        struct string_list_item *item = NULL;
-       data.head = head; data.tail = tail;
 
        for_each_ref(add_existing, &existing_refs);
        for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
@@ -631,10 +605,20 @@ static void find_non_local_tags(struct transport *transport,
                item->util = NULL;
 
        /*
-        * For all the tags in the remote_refs string list, call
-        * add_to_tail to add them to the list of refs to be fetched
+        * For all the tags in the remote_refs string list,
+        * add them to the list of refs to be fetched
         */
-       for_each_string_list(&remote_refs, add_to_tail, &data);
+       for_each_string_list_item(item, &remote_refs) {
+               /* Unless we have already decided to ignore this item... */
+               if (item->util)
+               {
+                       struct ref *rm = alloc_ref(item->string);
+                       rm->peer_ref = alloc_ref(item->string);
+                       hashcpy(rm->old_sha1, item->util);
+                       **tail = rm;
+                       *tail = &rm->next;
+               }
+       }
 
        string_list_clear(&remote_refs, 0);
 }
@@ -667,7 +651,7 @@ static int truncate_fetch_head(void)
 static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
 {
-       struct string_list existing_refs = { NULL, 0, 0, 0 };
+       struct string_list existing_refs = STRING_LIST_INIT_NODUP;
        struct string_list_item *peer_item = NULL;
        struct ref *ref_map;
        struct ref *rm;
@@ -675,10 +659,12 @@ static int do_fetch(struct transport *transport,
 
        for_each_ref(add_existing, &existing_refs);
 
-       if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
-               tags = TAGS_SET;
-       if (transport->remote->fetch_tags == -1)
-               tags = TAGS_UNSET;
+       if (tags == TAGS_DEFAULT) {
+               if (transport->remote->fetch_tags == 2)
+                       tags = TAGS_SET;
+               if (transport->remote->fetch_tags == -1)
+                       tags = TAGS_UNSET;
+       }
 
        if (!transport->get_refs_list || !transport->fetch)
                die("Don't know how to fetch from %s", transport->url);
@@ -845,7 +831,8 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
        int exit_code;
 
        if (!remote)
-               die("Where do you want to fetch from today?");
+               die("No remote repository specified.  Please, specify either a URL or a\n"
+                   "remote name from which new revisions should be fetched.");
 
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
@@ -890,7 +877,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 int cmd_fetch(int argc, const char **argv, const char *prefix)
 {
        int i;
-       struct string_list list = { NULL, 0, 0, 0 };
+       struct string_list list = STRING_LIST_INIT_NODUP;
        struct remote *remote;
        int result = 0;
 
index 937d5a717ba3dd71e908f59297a5085e31b48c07..e7e12eea250691a2c0d70964d1fffea0923dd292 100644 (file)
@@ -38,8 +38,8 @@ void init_src_data(struct src_data *data)
        data->generic.strdup_strings = 1;
 }
 
-static struct string_list srcs = { NULL, 0, 0, 1 };
-static struct string_list origins = { NULL, 0, 0, 1 };
+static struct string_list srcs = STRING_LIST_INIT_DUP;
+static struct string_list origins = STRING_LIST_INIT_DUP;
 
 static int handle_line(char *line)
 {
@@ -146,7 +146,7 @@ static void shortlog(const char *name, unsigned char *sha1,
        int i, count = 0;
        struct commit *commit;
        struct object *branch;
-       struct string_list subjects = { NULL, 0, 0, 1 };
+       struct string_list subjects = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
 
index a2b28c6962be8ea6f8882bae3d4fde9d972e78d2..89e75c6894e6fdb2abbe464d825f5c7186a14141 100644 (file)
@@ -228,7 +228,8 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
                        v->s = s;
                }
                else if (!strcmp(name, "objectname:short")) {
-                       v->s = find_unique_abbrev(obj->sha1, DEFAULT_ABBREV);
+                       v->s = xstrdup(find_unique_abbrev(obj->sha1,
+                                                         DEFAULT_ABBREV));
                }
        }
 }
index 597f76bc42f51e0fc1906c287981e3d37dc0f536..da32f3df34da7af3cdcb0a9dd6aa66061bdfe927 100644 (file)
@@ -834,12 +834,12 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        int external_grep_allowed__ignored;
        const char *show_in_pager = NULL, *default_pager = "dummy";
        struct grep_opt opt;
-       struct object_array list = { 0, 0, NULL };
+       struct object_array list = OBJECT_ARRAY_INIT;
        const char **paths = NULL;
-       struct string_list path_list = { NULL, 0, 0, 0 };
+       struct string_list path_list = STRING_LIST_INIT_NODUP;
        int i;
        int dummy;
-       int nongit = 0, use_index = 1;
+       int use_index = 1;
        struct option options[] = {
                OPT_BOOLEAN(0, "cached", &cached,
                        "search in index instead of in the work tree"),
@@ -930,8 +930,6 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
-       prefix = setup_git_directory_gently(&nongit);
-
        /*
         * 'git grep -h', unlike 'git grep -h <pattern>', is a request
         * to show usage information and exit.
@@ -976,7 +974,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                             PARSE_OPT_STOP_AT_NON_OPTION |
                             PARSE_OPT_NO_INTERNAL_HELP);
 
-       if (use_index && nongit)
+       if (use_index && !startup_info->have_repository)
                /* die the same way as if we did it at the beginning */
                setup_git_directory();
 
index a9836b00aec8df68e7a615dce83d3c60f73e242f..61ff79839bc4778451425d0ae1cfd199247954a1 100644 (file)
@@ -120,7 +120,7 @@ static void exec_woman_emacs(const char *path, const char *page)
                if (!path)
                        path = "emacsclient";
                strbuf_addf(&man_page, "(woman \"%s\")", page);
-               execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+               execlp(path, "emacsclient", "-e", man_page.buf, (char *)NULL);
                warning("failed to exec '%s': %s", path, strerror(errno));
        }
 }
@@ -148,7 +148,7 @@ static void exec_man_konqueror(const char *path, const char *page)
                } else
                        path = "kfmclient";
                strbuf_addf(&man_page, "man:%s(1)", page);
-               execlp(path, filename, "newTab", man_page.buf, NULL);
+               execlp(path, filename, "newTab", man_page.buf, (char *)NULL);
                warning("failed to exec '%s': %s", path, strerror(errno));
        }
 }
@@ -157,7 +157,7 @@ static void exec_man_man(const char *path, const char *page)
 {
        if (!path)
                path = "man";
-       execlp(path, "man", page, NULL);
+       execlp(path, "man", page, (char *)NULL);
        warning("failed to exec '%s': %s", path, strerror(errno));
 }
 
@@ -165,7 +165,7 @@ static void exec_man_cmd(const char *cmd, const char *page)
 {
        struct strbuf shell_cmd = STRBUF_INIT;
        strbuf_addf(&shell_cmd, "%s %s", cmd, page);
-       execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+       execl("/bin/sh", "sh", "-c", shell_cmd.buf, (char *)NULL);
        warning("failed to exec '%s': %s", cmd, strerror(errno));
 }
 
@@ -372,7 +372,7 @@ static void show_info_page(const char *git_cmd)
 {
        const char *page = cmd_to_page(git_cmd);
        setenv("INFOPATH", system_path(GIT_INFO_PATH), 1);
-       execlp("info", "info", "gitman", page, NULL);
+       execlp("info", "info", "gitman", page, (char *)NULL);
        die("no info viewer handled the request");
 }
 
@@ -398,7 +398,7 @@ static void get_html_page_path(struct strbuf *page_path, const char *page)
 #ifndef open_html
 static void open_html(const char *path)
 {
-       execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
+       execl_git_cmd("web--browse", "-c", "help.browser", path, (char *)NULL);
 }
 #endif
 
index a89ae831dd6251d7332e06470273d30fd9cb31eb..2e680d7a7ac3495c9d6bd10e0fa6a6ae28bb8670 100644 (file)
@@ -884,25 +884,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(index_pack_usage);
 
-       /*
-        * 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");
-       }
+       read_replace_refs = 0;
+
+       git_config(git_index_pack_config, NULL);
+       if (prefix && chdir(prefix))
+               die("Cannot come back to cwd");
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
index 08b872263cd646ec9d7dc8ad91273c71b1af2b69..eaa1ee0fa71a344af747dec038c6a5907c8273a5 100644 (file)
@@ -125,6 +125,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
                rev->show_decorations = 1;
                load_ref_decorations(decoration_style);
        }
+       setup_pager();
 }
 
 /*
@@ -491,12 +492,6 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
        rev.use_terminator = 1;
        rev.always_show_header = 1;
 
-       /*
-        * We get called through "git reflog", so unlike the other log
-        * routines, we need to set up our pager manually..
-        */
-       setup_pager();
-
        return cmd_log_walk(&rev);
 }
 
index 1b9b8a8b4ac2baff6b9676bcf23b148f147dac6a..bb4f612b3d48c453d551f251b65887beb283ec7b 100644 (file)
@@ -25,6 +25,7 @@ static int show_modified;
 static int show_killed;
 static int show_valid_bit;
 static int line_terminator = '\n';
+static int debug_mode;
 
 static const char *prefix;
 static int max_prefix_len;
@@ -162,35 +163,41 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
                       ce_stage(ce));
        }
        write_name(ce->name, ce_namelen(ce));
-}
-
-static int show_one_ru(struct string_list_item *item, void *cbdata)
-{
-       const char *path = item->string;
-       struct resolve_undo_info *ui = item->util;
-       int i, len;
-
-       len = strlen(path);
-       if (len < max_prefix_len)
-               return 0; /* outside of the prefix */
-       if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
-               return 0; /* uninterested */
-       for (i = 0; i < 3; i++) {
-               if (!ui->mode[i])
-                       continue;
-               printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
-                      find_unique_abbrev(ui->sha1[i], abbrev),
-                      i + 1);
-               write_name(path, len);
+       if (debug_mode) {
+               printf("  ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
+               printf("  mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
+               printf("  dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
+               printf("  uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
+               printf("  size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
        }
-       return 0;
 }
 
 static void show_ru_info(void)
 {
+       struct string_list_item *item;
+
        if (!the_index.resolve_undo)
                return;
-       for_each_string_list(the_index.resolve_undo, show_one_ru, NULL);
+
+       for_each_string_list_item(item, the_index.resolve_undo) {
+               const char *path = item->string;
+               struct resolve_undo_info *ui = item->util;
+               int i, len;
+
+               len = strlen(path);
+               if (len < max_prefix_len)
+                       continue; /* outside of the prefix */
+               if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+                       continue; /* uninterested */
+               for (i = 0; i < 3; i++) {
+                       if (!ui->mode[i])
+                               continue;
+                       printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+                              find_unique_abbrev(ui->sha1[i], abbrev),
+                              i + 1);
+                       write_name(path, len);
+               }
+       }
 }
 
 static void show_files(struct dir_struct *dir)
@@ -519,6 +526,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
                        "pretend that paths removed since <tree-ish> are still present"),
                OPT__ABBREV(&abbrev),
+               OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
                OPT_END()
        };
 
index 34480cfad6a8842d65ae61f6257c597a2a987b33..97eed4012ba23cfe7f13cf2b6c160ade54f6b2d1 100644 (file)
@@ -32,7 +32,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
 {
        int i;
        const char *dest = NULL;
-       int nongit;
        unsigned flags = 0;
        int quiet = 0;
        const char *uploadpack = NULL;
@@ -42,8 +41,6 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        struct transport *transport;
        const struct ref *ref;
 
-       setup_git_directory_gently(&nongit);
-
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
index e4560da19138cd67843f93a05bc6509d51cfe076..99654d02221c13ff98f268ad2dee99f16e794750 100644 (file)
@@ -137,7 +137,7 @@ static int split_maildir(const char *maildir, const char *dir,
        char name[PATH_MAX];
        int ret = -1;
        int i;
-       struct string_list list = {NULL, 0, 0, 1};
+       struct string_list list = STRING_LIST_INIT_DUP;
 
        if (populate_maildir_list(&list, maildir) < 0)
                goto out;
index b8e9e5ba01658bbdd6e9d712b00da902b6f33fbe..b6664d49be9454b703615af4e71f92231e053e07 100644 (file)
@@ -28,7 +28,6 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        xmparam_t xmp = {{0}};
        int ret = 0, i = 0, to_stdout = 0;
        int quiet = 0;
-       int nongit;
        struct option options[] = {
                OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"),
                OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3),
@@ -50,8 +49,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        xmp.style = 0;
        xmp.favor = 0;
 
-       prefix = setup_git_directory_gently(&nongit);
-       if (!nongit) {
+       if (startup_info->have_repository) {
                /* Read the configuration file */
                git_config(git_xmerge_config, NULL);
                if (0 <= git_xmerge_style)
index d8875d589240e0c78a9e241a7d2bdde1d10ab800..78b9db76a0819529e7f6cab44319acd2978c9e9b 100644 (file)
@@ -3,6 +3,9 @@
 #include "tag.h"
 #include "merge-recursive.h"
 
+static const char builtin_merge_recursive_usage[] =
+       "git %s <base>... -- <head> <remote> ...";
+
 static const char *better_branch_name(const char *branch)
 {
        static char githead_env[8 + 40 + 1];
@@ -29,7 +32,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                o.subtree_shift = "";
 
        if (argc < 4)
-               usagef("%s <base>... -- <head> <remote> ...", argv[0]);
+               usagef(builtin_merge_recursive_usage, argv[0]);
 
        for (i = 1; i < argc; ++i) {
                const char *arg = argv[i];
@@ -45,6 +48,10 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
                                o.subtree_shift = "";
                        else if (!prefixcmp(arg+2, "subtree="))
                                o.subtree_shift = arg + 10;
+                       else if (!strcmp(arg+2, "renormalize"))
+                               o.renormalize = 1;
+                       else if (!strcmp(arg+2, "no-renormalize"))
+                               o.renormalize = 0;
                        else
                                die("Unknown option %s", arg);
                        continue;
index 2207f79969975225077ff16c7c368c4652548862..576e81f14578661461d71230c896436fab3023f9 100644 (file)
@@ -54,6 +54,7 @@ static size_t use_strategies_nr, use_strategies_alloc;
 static const char **xopts;
 static size_t xopts_nr, xopts_alloc;
 static const char *branch;
+static int option_renormalize;
 static int verbosity;
 static int allow_rerere_auto;
 
@@ -486,7 +487,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                buf = xstrdup(v);
                argc = split_cmdline(buf, &argv);
                if (argc < 0)
-                       die("Bad branch.%s.mergeoptions string", branch);
+                       die("Bad branch.%s.mergeoptions string: %s", branch,
+                           split_cmdline_strerror(argc));
                argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
                memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
                argc++;
@@ -503,6 +505,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
                return git_config_string(&pull_octopus, k, v);
        else if (!strcmp(k, "merge.log") || !strcmp(k, "merge.summary"))
                option_log = git_config_bool(k, v);
+       else if (!strcmp(k, "merge.renormalize"))
+               option_renormalize = git_config_bool(k, v);
        return git_diff_ui_config(k, v, cb);
 }
 
@@ -624,6 +628,11 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                if (!strcmp(strategy, "subtree"))
                        o.subtree_shift = "";
 
+               o.renormalize = option_renormalize;
+
+               /*
+                * NEEDSWORK: merge with table in builtin/merge-recursive
+                */
                for (x = 0; x < xopts_nr; x++) {
                        if (!strcmp(xopts[x], "ours"))
                                o.recursive_variant = MERGE_RECURSIVE_OURS;
@@ -633,6 +642,10 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                                o.subtree_shift = "";
                        else if (!prefixcmp(xopts[x], "subtree="))
                                o.subtree_shift = xopts[x]+8;
+                       else if (!strcmp(xopts[x], "renormalize"))
+                               o.renormalize = 1;
+                       else if (!strcmp(xopts[x], "no-renormalize"))
+                               o.renormalize = 0;
                        else
                                die("Unknown option for merge-recursive: -X%s", xopts[x]);
                }
@@ -704,7 +717,8 @@ int checkout_fast_forward(const unsigned char *head, const unsigned char *remote
        opts.verbose_update = 1;
        opts.merge = 1;
        opts.fn = twoway_merge;
-       opts.msgs = get_porcelain_error_msgs();
+       opts.show_all_errors = 1;
+       set_porcelain_error_msgs(opts.msgs, "merge");
 
        trees[nr_trees] = parse_tree_indirect(head);
        if (!trees[nr_trees++])
@@ -816,7 +830,7 @@ static int finish_automerge(struct commit_list *common,
        return 0;
 }
 
-static int suggest_conflicts(void)
+static int suggest_conflicts(int renormalizing)
 {
        FILE *fp;
        int pos;
@@ -1301,5 +1315,5 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        "stopped before committing as requested\n");
                return 0;
        } else
-               return suggest_conflicts();
+               return suggest_conflicts(option_renormalize);
 }
index 38574b89f7cfc127b2ea5044b3ebb5dabe726fb4..cdbb09473c0c45efff23a63e78b7623cad523350 100644 (file)
@@ -63,7 +63,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
        const char **source, **destination, **dest_path;
        enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
        struct stat st;
-       struct string_list src_for_dst = {NULL, 0, 0, 0};
+       struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
 
        git_config(git_default_config, NULL);
 
index 06a38ac8c10126085e6477c205d450da089faae2..31f5c1c971381a9490b717e0413ee9a40260ef39 100644 (file)
@@ -220,7 +220,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
 {
-       struct object_array revs = { 0, 0, NULL };
+       struct object_array revs = OBJECT_ARRAY_INIT;
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
        struct name_ref_data data = { 0, 0, NULL };
        struct option opts[] = {
index 190005f3cd44a35a6fffb20b1e4c74622b4d93f7..fbc347c9f09e0da4ba43982a5f7b29882080f150 100644 (file)
@@ -798,8 +798,9 @@ static int prune(int argc, const char **argv, const char *prefix)
        struct notes_tree *t;
        int show_only = 0, verbose = 0;
        struct option options[] = {
-               OPT_BOOLEAN('n', NULL, &show_only, "do not remove, show only"),
-               OPT_BOOLEAN('v', NULL, &verbose, "report pruned notes"),
+               OPT_BOOLEAN('n', "dry-run", &show_only,
+                           "do not remove, show only"),
+               OPT_BOOLEAN('v', "verbose", &verbose, "report pruned notes"),
                OPT_END()
        };
 
index 81f915ec315eb75e529fed8f2f6bab436755f598..99218ba49e2c6ea7ff7185e6397bb73bca213a66 100644 (file)
@@ -125,10 +125,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
        const struct option options[] = {
-               OPT_BOOLEAN('n', NULL, &show_only,
+               OPT_BOOLEAN('n', "dry-run", &show_only,
                            "do not remove, show only"),
-               OPT_BOOLEAN('v', NULL, &verbose,
-                       "report pruned objects"),
+               OPT_BOOLEAN('v', "verbose", &verbose, "report pruned objects"),
                OPT_DATE(0, "expire", &expire,
                         "expire objects older than <time>"),
                OPT_END()
index f4358b9d230f6d8d7a9a67fdfbc60279c5ec71ee..e655eb7695faba13c4d9e8f25b9649ffec7195be 100644 (file)
@@ -22,13 +22,13 @@ static int progress;
 
 static const char **refspec;
 static int refspec_nr;
+static int refspec_alloc;
 
 static void add_refspec(const char *ref)
 {
-       int nr = refspec_nr + 1;
-       refspec = xrealloc(refspec, nr * sizeof(char *));
-       refspec[nr-1] = ref;
-       refspec_nr = nr;
+       refspec_nr++;
+       ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
+       refspec[refspec_nr-1] = ref;
 }
 
 static void set_refspecs(const char **refs, int nr)
@@ -130,8 +130,8 @@ static int push_with_options(struct transport *transport, int flags)
 
        if (nonfastforward && advice_push_nonfastforward) {
                fprintf(stderr, "To prevent you from losing history, non-fast-forward updates were rejected\n"
-                               "Merge the remote changes before pushing again.  See the 'Note about\n"
-                               "fast-forwards' section of 'git push --help' for details.\n");
+                               "Merge the remote changes (e.g. 'git pull') before pushing again.  See the\n"
+                               "'Note about fast-forwards' section of 'git push --help' for details.\n");
        }
 
        return 1;
index d634b5a3d5212b8e49217d94430c294b911dfc7d..760817dbd7fec0086a0a1b62e58ab4438f227b7b 100644 (file)
@@ -530,7 +530,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
 static void check_aliased_updates(struct command *commands)
 {
        struct command *cmd;
-       struct string_list ref_list = { NULL, 0, 0, 0 };
+       struct string_list ref_list = STRING_LIST_INIT_NODUP;
 
        for (cmd = commands; cmd; cmd = cmd->next) {
                struct string_list_item *item =
index 6699bc571235167f5cae60106522c09a10ae341d..48e0a6bf260cab18d44058ade554e3a9d5946b93 100644 (file)
@@ -134,7 +134,7 @@ static int add_branch(const char *key, const char *branchname,
 static int add(int argc, const char **argv)
 {
        int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
-       struct string_list track = { NULL, 0, 0 };
+       struct string_list track = STRING_LIST_INIT_NODUP;
        const char *master = NULL;
        struct remote *remote;
        struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
@@ -596,7 +596,7 @@ static int mv(int argc, const char **argv)
        };
        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 string_list remote_branches = STRING_LIST_INIT_NODUP;
        struct rename_info rename;
        int i;
 
@@ -734,8 +734,8 @@ static int rm(int argc, const char **argv)
        struct remote *remote;
        struct strbuf buf = STRBUF_INIT;
        struct known_remotes known_remotes = { NULL, NULL };
-       struct string_list branches = { NULL, 0, 0, 1 };
-       struct string_list skipped = { NULL, 0, 0, 1 };
+       struct string_list branches = STRING_LIST_INIT_DUP;
+       struct string_list skipped = STRING_LIST_INIT_DUP;
        struct branches_for_remote cb_data;
        int i, result;
 
@@ -1044,7 +1044,7 @@ static int show(int argc, const char **argv)
                OPT_END()
        };
        struct ref_states states;
-       struct string_list info_list = { NULL, 0, 0, 0 };
+       struct string_list info_list = STRING_LIST_INIT_NODUP;
        struct show_info info;
 
        argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
@@ -1483,7 +1483,7 @@ static int get_one_entry(struct remote *remote, void *priv)
 
 static int show_all(void)
 {
-       struct string_list list = { NULL, 0, 0 };
+       struct string_list list = STRING_LIST_INIT_NODUP;
        int result;
 
        list.strdup_strings = 1;
index 39ad60169d6f4cd53e560e922410b48cc01f7886..642bf35587ed994948d3eeac0a189ae29e39bf11 100644 (file)
@@ -1,13 +1,16 @@
 #include "builtin.h"
 #include "cache.h"
 #include "dir.h"
+#include "parse-options.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff/xdiff.h"
 #include "xdiff-interface.h"
 
-static const char git_rerere_usage[] =
-"git rerere [clear | status | diff | gc]";
+static const char * const rerere_usage[] = {
+       "git rerere [clear | status | diff | gc]",
+       NULL,
+};
 
 /* these values are days */
 static int cutoff_noresolve = 15;
@@ -19,6 +22,12 @@ static time_t rerere_created_at(const char *name)
        return stat(rerere_path(name, "preimage"), &st) ? (time_t) 0 : st.st_mtime;
 }
 
+static time_t rerere_last_used_at(const char *name)
+{
+       struct stat st;
+       return stat(rerere_path(name, "postimage"), &st) ? (time_t) 0 : st.st_mtime;
+}
+
 static void unlink_rr_item(const char *name)
 {
        unlink(rerere_path(name, "thisimage"));
@@ -40,7 +49,7 @@ static int git_rerere_gc_config(const char *var, const char *value, void *cb)
 
 static void garbage_collect(struct string_list *rr)
 {
-       struct string_list to_remove = { NULL, 0, 0, 1 };
+       struct string_list to_remove = STRING_LIST_INIT_DUP;
        DIR *dir;
        struct dirent *e;
        int i, cutoff;
@@ -53,11 +62,16 @@ static void garbage_collect(struct string_list *rr)
        while ((e = readdir(dir))) {
                if (is_dot_or_dotdot(e->d_name))
                        continue;
-               then = rerere_created_at(e->d_name);
-               if (!then)
-                       continue;
-               cutoff = (has_rerere_resolution(e->d_name)
-                         ? cutoff_resolve : cutoff_noresolve);
+
+               then = rerere_last_used_at(e->d_name);
+               if (then) {
+                       cutoff = cutoff_resolve;
+               } else {
+                       then = rerere_created_at(e->d_name);
+                       if (!then)
+                               continue;
+                       cutoff = cutoff_noresolve;
+               }
                if (then < now - cutoff * 86400)
                        string_list_append(&to_remove, e->d_name);
        }
@@ -102,26 +116,27 @@ static int diff_two(const char *file1, const char *label1,
 
 int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
-       struct string_list merge_rr = { NULL, 0, 0, 1 };
-       int i, fd, flags = 0;
-
-       if (2 < argc) {
-               if (!strcmp(argv[1], "-h"))
-                       usage(git_rerere_usage);
-               if (!strcmp(argv[1], "--rerere-autoupdate"))
-                       flags = RERERE_AUTOUPDATE;
-               else if (!strcmp(argv[1], "--no-rerere-autoupdate"))
-                       flags = RERERE_NOAUTOUPDATE;
-               if (flags) {
-                       argc--;
-                       argv++;
-               }
-       }
-       if (argc < 2)
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
+       int i, fd, autoupdate = -1, flags = 0;
+
+       struct option options[] = {
+               OPT_SET_INT(0, "rerere-autoupdate", &autoupdate,
+                       "register clean resolutions in index", 1),
+               OPT_END(),
+       };
+
+       argc = parse_options(argc, argv, prefix, options, rerere_usage, 0);
+
+       if (autoupdate == 1)
+               flags = RERERE_AUTOUPDATE;
+       if (autoupdate == 0)
+               flags = RERERE_NOAUTOUPDATE;
+
+       if (argc < 1)
                return rerere(flags);
 
-       if (!strcmp(argv[1], "forget")) {
-               const char **pathspec = get_pathspec(prefix, argv + 2);
+       if (!strcmp(argv[0], "forget")) {
+               const char **pathspec = get_pathspec(prefix, argv + 1);
                return rerere_forget(pathspec);
        }
 
@@ -129,26 +144,26 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
        if (fd < 0)
                return 0;
 
-       if (!strcmp(argv[1], "clear")) {
+       if (!strcmp(argv[0], "clear")) {
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *name = (const char *)merge_rr.items[i].util;
                        if (!has_rerere_resolution(name))
                                unlink_rr_item(name);
                }
                unlink_or_warn(git_path("MERGE_RR"));
-       } else if (!strcmp(argv[1], "gc"))
+       } else if (!strcmp(argv[0], "gc"))
                garbage_collect(&merge_rr);
-       else if (!strcmp(argv[1], "status"))
+       else if (!strcmp(argv[0], "status"))
                for (i = 0; i < merge_rr.nr; i++)
                        printf("%s\n", merge_rr.items[i].string);
-       else if (!strcmp(argv[1], "diff"))
+       else if (!strcmp(argv[0], "diff"))
                for (i = 0; i < merge_rr.nr; i++) {
                        const char *path = merge_rr.items[i].string;
                        const char *name = (const char *)merge_rr.items[i].util;
                        diff_two(rerere_path(name, "preimage"), path, path, path);
                }
        else
-               usage(git_rerere_usage);
+               usage_with_options(rerere_usage, options);
 
        string_list_clear(&merge_rr, 1);
        return 0;
index 1283068fd24c371e39f0271d01dd6b3aac437d4b..0037be4693bbd875e644df13ce4a9fae31e7378c 100644 (file)
@@ -318,7 +318,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
         * affecting the working tree nor HEAD. */
        if (i < argc) {
                if (reset_type == MIXED)
-                       warning("--mixed option is deprecated with paths.");
+                       warning("--mixed with paths is deprecated; use 'git reset -- <paths>' instead.");
                else if (reset_type != NONE)
                        die("Cannot do %s reset with paths.",
                                        reset_type_names[reset_type]);
index 8b9d829a73a0862dfdf2c2499b18878489620f16..4b47ace36b51566acb5b748f679070f02cbbf568 100644 (file)
@@ -102,9 +102,9 @@ struct commit_message {
 static int get_message(const char *raw_message, struct commit_message *out)
 {
        const char *encoding;
-       const char *p, *abbrev, *eol;
+       const char *abbrev, *subject;
+       int abbrev_len, subject_len;
        char *q;
-       int abbrev_len, oneline_len;
 
        if (!raw_message)
                return -1;
@@ -125,27 +125,17 @@ static int get_message(const char *raw_message, struct commit_message *out)
        abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
-       /* Find beginning and end of commit subject. */
-       p = out->message;
-       while (*p && (*p != '\n' || p[1] != '\n'))
-               p++;
-       if (*p) {
-               p += 2;
-               for (eol = p + 1; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
-       } else
-               eol = p;
-       oneline_len = eol - p;
+       subject_len = find_commit_subject(out->message, &subject);
 
        out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
-                             strlen("... ") + oneline_len + 1);
+                             strlen("... ") + subject_len + 1);
        q = out->parent_label;
        q = mempcpy(q, "parent of ", strlen("parent of "));
        out->label = q;
        q = mempcpy(q, abbrev, abbrev_len);
        q = mempcpy(q, "... ", strlen("... "));
        out->subject = q;
-       q = mempcpy(q, p, oneline_len);
+       q = mempcpy(q, subject, subject_len);
        *q = '\0';
        return 0;
 }
@@ -241,27 +231,30 @@ static void set_author_ident_env(const char *message)
                        sha1_to_hex(commit->object.sha1));
 }
 
-static char *help_msg(void)
+static void advise(const char *advice, ...)
 {
-       struct strbuf helpbuf = STRBUF_INIT;
-       char *msg = getenv("GIT_CHERRY_PICK_HELP");
+       va_list params;
 
-       if (msg)
-               return msg;
+       va_start(params, advice);
+       vreportf("hint: ", advice, params);
+       va_end(params);
+}
 
-       strbuf_addstr(&helpbuf, "  After resolving the conflicts,\n"
-               "mark the corrected paths with 'git add <paths>' or 'git rm <paths>'\n"
-               "and commit the result");
+static void print_advice(void)
+{
+       char *msg = getenv("GIT_CHERRY_PICK_HELP");
 
-       if (action == CHERRY_PICK) {
-               strbuf_addf(&helpbuf, " with: \n"
-                       "\n"
-                       "        git commit -c %s\n",
-                           sha1_to_hex(commit->object.sha1));
+       if (msg) {
+               fprintf(stderr, "%s\n", msg);
+               return;
        }
-       else
-               strbuf_addch(&helpbuf, '.');
-       return strbuf_detach(&helpbuf, NULL);
+
+       advise("after resolving the conflicts, mark the corrected paths");
+       advise("with 'git add <paths>' or 'git rm <paths>'");
+
+       if (action == CHERRY_PICK)
+               advise("and commit the result with 'git commit -c %s'",
+                      find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
 }
 
 static void write_message(struct strbuf *msgbuf, const char *filename)
@@ -311,10 +304,9 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from)
        return write_ref_sha1(ref_lock, to, "cherry-pick");
 }
 
-static void do_recursive_merge(struct commit *base, struct commit *next,
-                              const char *base_label, const char *next_label,
-                              unsigned char *head, struct strbuf *msgbuf,
-                              char *defmsg)
+static int do_recursive_merge(struct commit *base, struct commit *next,
+                             const char *base_label, const char *next_label,
+                             unsigned char *head, struct strbuf *msgbuf)
 {
        struct merge_options o;
        struct tree *result, *next_tree, *base_tree, *head_tree;
@@ -324,6 +316,13 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
        index_fd = hold_locked_index(&index_lock, 1);
 
        read_cache();
+
+       /*
+        * NEEDSWORK: cherry-picking between branches with
+        * different end-of-line normalization is a pain;
+        * plumb in an option to set o.renormalize?
+        * (or better: arbitrary -X options)
+        */
        init_merge_options(&o);
        o.ancestor = base ? base_label : "(empty tree)";
        o.branch1 = "HEAD";
@@ -357,14 +356,35 @@ static void do_recursive_merge(struct commit *base, struct commit *next,
                                        i++;
                        }
                }
-               write_message(msgbuf, defmsg);
-               fprintf(stderr, "Automatic %s failed.%s\n",
-                       me, help_msg());
-               rerere(allow_rerere_auto);
-               exit(1);
        }
-       write_message(msgbuf, defmsg);
-       fprintf(stderr, "Finished one %s.\n", me);
+
+       return !clean;
+}
+
+/*
+ * If we are cherry-pick, and if the merge did not result in
+ * hand-editing, we will hit this commit and inherit the original
+ * author date and name.
+ * If we are revert, or if our cherry-pick results in a hand merge,
+ * we had better say that the current user is responsible for that.
+ */
+static int run_git_commit(const char *defmsg)
+{
+       /* 6 is max possible length of our args array including NULL */
+       const char *args[6];
+       int i = 0;
+
+       args[i++] = "commit";
+       args[i++] = "-n";
+       if (signoff)
+               args[i++] = "-s";
+       if (!edit) {
+               args[i++] = "-F";
+               args[i++] = defmsg;
+       }
+       args[i] = NULL;
+
+       return run_command_v_opt(args, RUN_GIT_CMD);
 }
 
 static int do_pick_commit(void)
@@ -375,6 +395,7 @@ static int do_pick_commit(void)
        struct commit_message msg = { NULL, NULL, NULL, NULL, NULL };
        char *defmsg = NULL;
        struct strbuf msgbuf = STRBUF_INIT;
+       int res;
 
        if (no_commit) {
                /*
@@ -470,63 +491,40 @@ static int do_pick_commit(void)
                }
        }
 
-       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT)
-               do_recursive_merge(base, next, base_label, next_label,
-                                  head, &msgbuf, defmsg);
-       else {
-               int res;
+       if (!strategy || !strcmp(strategy, "recursive") || action == REVERT) {
+               res = do_recursive_merge(base, next, base_label, next_label,
+                                        head, &msgbuf);
+               write_message(&msgbuf, defmsg);
+       } else {
                struct commit_list *common = NULL;
                struct commit_list *remotes = NULL;
+
                write_message(&msgbuf, defmsg);
+
                commit_list_insert(base, &common);
                commit_list_insert(next, &remotes);
                res = try_merge_command(strategy, common,
                                        sha1_to_hex(head), remotes);
                free_commit_list(common);
                free_commit_list(remotes);
-               if (res) {
-                       fprintf(stderr, "Automatic %s with strategy %s failed.%s\n",
-                               me, strategy, help_msg());
-                       rerere(allow_rerere_auto);
-                       exit(1);
-               }
        }
 
-       free_message(&msg);
-
-       /*
-        *
-        * If we are cherry-pick, and if the merge did not result in
-        * hand-editing, we will hit this commit and inherit the original
-        * author date and name.
-        * If we are revert, or if our cherry-pick results in a hand merge,
-        * we had better say that the current user is responsible for that.
-        */
-
-       if (!no_commit) {
-               /* 6 is max possible length of our args array including NULL */
-               const char *args[6];
-               int res;
-               int i = 0;
-
-               args[i++] = "commit";
-               args[i++] = "-n";
-               if (signoff)
-                       args[i++] = "-s";
-               if (!edit) {
-                       args[i++] = "-F";
-                       args[i++] = defmsg;
-               }
-               args[i] = NULL;
-               res = run_command_v_opt(args, RUN_GIT_CMD);
-               free(defmsg);
-
-               return res;
+       if (res) {
+               error("could not %s %s... %s",
+                     action == REVERT ? "revert" : "apply",
+                     find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+                     msg.subject);
+               print_advice();
+               rerere(allow_rerere_auto);
+       } else {
+               if (!no_commit)
+                       res = run_git_commit(defmsg);
        }
 
+       free_message(&msg);
        free(defmsg);
 
-       return 0;
+       return res;
 }
 
 static void prepare_revs(struct rev_info *revs)
index 0a9681ba7ece52e751b1b54d53f3e8fb4c19464e..2135b0dde11faa0c501682ed074e0ba96bf28b42 100644 (file)
@@ -249,7 +249,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 {
        static struct shortlog log;
        static struct rev_info rev;
-       int nongit;
+       int nongit = !startup_info->have_repository;
 
        static const struct option options[] = {
                OPT_BOOLEAN('n', "numbered", &log.sort_by_number,
@@ -265,7 +265,6 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 
        struct parse_opt_ctx_t ctx;
 
-       prefix = setup_git_directory_gently(&nongit);
        git_config(git_default_config, NULL);
        shortlog_init(&log);
        init_revisions(&rev, prefix);
index 0b2a9ad1a9a8a825c2e2104f8a9f729a9cb03d1a..be9b512eebc72984e6ff4cdd4859ab7951539e19 100644 (file)
@@ -120,7 +120,7 @@ static int add_existing(const char *refname, const unsigned char *sha1, int flag
  */
 static int exclude_existing(const char *match)
 {
-       static struct string_list existing_refs = { NULL, 0, 0, 0 };
+       static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
        char buf[1024];
        int matchlen = match ? strlen(match) : 0;
 
index 70fdb4dec7e8b0c56ded90ee7f086cc9f16293a1..0744bb83185289e26bd32b3b2ecaec717d8c75d7 100644 (file)
@@ -74,14 +74,9 @@ static int show_config(const char *var, const char *value, void *cb)
 
 int cmd_var(int argc, const char **argv, const char *prefix)
 {
-       const char *val;
-       int nongit;
-       if (argc != 2) {
+       const char *val = NULL;
+       if (argc != 2)
                usage(var_usage);
-       }
-
-       setup_git_directory_gently(&nongit);
-       val = NULL;
 
        if (strcmp(argv[1], "-l") == 0) {
                git_config(show_config, NULL);
index d91743775dfbe98d99d8ab25a270af320c8b984e..c60cf9140dd18254886ea14de763738630f95973 100644 (file)
@@ -328,9 +328,11 @@ static int update_one(struct cache_tree *it,
                        mode = ce->ce_mode;
                        entlen = pathlen - baselen;
                }
-               if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
+               if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
+                       strbuf_release(&buffer);
                        return error("invalid object %06o %s for '%.*s'",
                                mode, sha1_to_hex(sha1), entlen+baselen, path);
+               }
 
                if (ce->ce_flags & CE_REMOVE)
                        continue; /* entry being removed */
diff --git a/cache.h b/cache.h
index c9fa3df7f5b343ecea980ceb423e7c23d2eb22b2..be02a422d199b8269771ddedb4f13378f082e003 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -179,8 +179,7 @@ struct cache_entry {
 #define CE_UNHASHED  (0x200000)
 #define CE_CONFLICTED (0x800000)
 
-/* Only remove in work directory, not index */
-#define CE_WT_REMOVE (0x400000)
+#define CE_WT_REMOVE (0x400000) /* remove in work directory */
 
 #define CE_UNPACKED  (0x1000000)
 
@@ -449,7 +448,7 @@ extern int init_db(const char *template_dir, unsigned int flags);
                                alloc = alloc_nr(alloc); \
                        x = xrealloc((x), alloc * sizeof(*(x))); \
                } \
-       } while(0)
+       } while (0)
 
 /* Initialize and use the cache information */
 extern int read_index(struct index_state *);
@@ -641,6 +640,9 @@ extern char *git_pathdup(const char *fmt, ...)
 /* 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)));
+extern char *git_path_submodule(const char *path, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
+
 extern char *sha1_file_name(const unsigned char *sha1);
 extern char *sha1_pack_name(const unsigned char *sha1);
 extern char *sha1_pack_index_name(const unsigned char *sha1);
@@ -811,6 +813,7 @@ const char *show_date_relative(unsigned long time, int tz,
                               char *timebuf,
                               size_t timebuf_size);
 int parse_date(const char *date, char *buf, int bufsize);
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
 void datestamp(char *buf, int bufsize);
 #define approxidate(s) approxidate_careful((s), NULL)
 unsigned long approxidate_careful(const char *, int *);
@@ -1054,6 +1057,7 @@ extern void trace_argv_printf(const char **argv, const char *format, ...);
 extern int convert_to_git(const char *path, const char *src, size_t len,
                           struct strbuf *dst, enum safe_crlf checksafe);
 extern int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst);
+extern int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst);
 
 /* add */
 /*
@@ -1096,6 +1100,14 @@ void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
+/* Takes a negative value returned by split_cmdline */
+const char *split_cmdline_strerror(int cmdline_errno);
+
+/* git.c */
+struct startup_info {
+       int have_repository;
+};
+extern struct startup_info *startup_info;
 
 /* builtin/merge.c */
 int checkout_fast_forward(const unsigned char *from, const unsigned char *to);
index e9b07509678f5a4f61e5bedadea14b726e290ed1..0094ec1c9278332f736d2814c847b272788a7dd3 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -315,6 +315,25 @@ int parse_commit(struct commit *item)
        return ret;
 }
 
+int find_commit_subject(const char *commit_buffer, const char **subject)
+{
+       const char *eol;
+       const char *p = commit_buffer;
+
+       while (*p && (*p != '\n' || p[1] != '\n'))
+               p++;
+       if (*p) {
+               p += 2;
+               for (eol = p; *eol && *eol != '\n'; eol++)
+                       ; /* do nothing */
+       } else
+               eol = p;
+
+       *subject = p;
+
+       return eol - p;
+}
+
 struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
 {
        struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
index eb2b8ac3cd5f375e70354e8c364abd036b0966ed..9113bbe4889d71e824348edcb920110598db18d2 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -41,6 +41,9 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
 
 int parse_commit(struct commit *item);
 
+/* Find beginning and length of commit subject. */
+int find_commit_subject(const char *commit_buffer, const char **subject);
+
 struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
 unsigned commit_list_count(const struct commit_list *l);
 struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
index 9a8e3365827d303c6513475726113a3952fe0040..f2d9e1fd974b7271366da09370e10fafd2c50f08 100644 (file)
@@ -304,8 +304,13 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
                goto revert_attrs;
        }
 
-       time_t_to_filetime(times->modtime, &mft);
-       time_t_to_filetime(times->actime, &aft);
+       if (times) {
+               time_t_to_filetime(times->modtime, &mft);
+               time_t_to_filetime(times->actime, &aft);
+       } else {
+               GetSystemTimeAsFileTime(&mft);
+               aft = mft;
+       }
        if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) {
                errno = EINVAL;
                rc = -1;
@@ -641,7 +646,7 @@ static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_on
 }
 
 /*
- * Determines the absolute path of cmd using the the split path in path.
+ * Determines the absolute path of cmd using the split path in path.
  * If cmd contains a slash or backslash, no lookup is performed.
  */
 static char *path_lookup(const char *cmd, char **path, int exe_only)
index 74c42e31626480900898aba8e12704335330e11e..87260d26425dbb167f64710e71235f60e467a9b5 100644 (file)
@@ -2069,7 +2069,7 @@ static void init_malloc_global_mutex() {
   Each freshly allocated chunk must have both cinuse and pinuse set.
   That is, each allocated chunk borders either a previously allocated
   and still in-use chunk, or the base of its memory arena. This is
-  ensured by making all allocations from the the `lowest' part of any
+  ensured by making all allocations from the `lowest' part of any
   found chunk.  Further, no free chunk physically borders another one,
   so each free chunk is known to be preceded and followed by either
   inuse chunks or the ends of memory.
index 556d8ab11f40550335066f491d08383557e02a00..be851fc50267b10ae311dce042eec27c0f498461 100644 (file)
@@ -3122,7 +3122,7 @@ re_match (bufp, string, size, pos, regs)
 
 
 /* re_match_2 matches the compiled pattern in BUFP against the
-   the (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
+   (virtual) concatenation of STRING1 and STRING2 (of length SIZE1
    and SIZE2, respectively).  We start matching at POS, and stop
    matching at STOP.
 
diff --git a/compat/strtok_r.c b/compat/strtok_r.c
new file mode 100644 (file)
index 0000000..7b5d568
--- /dev/null
@@ -0,0 +1,61 @@
+/* Reentrant string tokenizer.  Generic version.
+   Copyright (C) 1991,1996-1999,2001,2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include "../git-compat-util.h"
+
+/* Parse S into tokens separated by characters in DELIM.
+   If S is NULL, the saved pointer in SAVE_PTR is used as
+   the next starting point.  For example:
+       char s[] = "-abc-=-def";
+       char *sp;
+       x = strtok_r(s, "-", &sp);      // x = "abc", sp = "=-def"
+       x = strtok_r(NULL, "-=", &sp);  // x = "def", sp = NULL
+       x = strtok_r(NULL, "=", &sp);   // x = NULL
+               // s = "abc\0-def\0"
+*/
+char *
+gitstrtok_r (char *s, const char *delim, char **save_ptr)
+{
+  char *token;
+
+  if (s == NULL)
+    s = *save_ptr;
+
+  /* Scan leading delimiters.  */
+  s += strspn (s, delim);
+  if (*s == '\0')
+    {
+      *save_ptr = s;
+      return NULL;
+    }
+
+  /* Find the end of the token.  */
+  token = s;
+  s = strpbrk (token, delim);
+  if (s == NULL)
+    /* This token finishes the string.  */
+    *save_ptr = token + strlen (token);
+  else
+    {
+      /* Terminate the token and make *SAVE_PTR point past it.  */
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  return token;
+}
index b4e65c32b235eafafefeed1c755be7e1ad5c71ae..4ffd77420f089452edf23ec63b94ad88cf0f37f5 100644 (file)
@@ -46,6 +46,7 @@ NO_IPV6=@NO_IPV6@
 NO_C99_FORMAT=@NO_C99_FORMAT@
 NO_HSTRERROR=@NO_HSTRERROR@
 NO_STRCASESTR=@NO_STRCASESTR@
+NO_STRTOK_R=@NO_STRTOK_R@
 NO_MEMMEM=@NO_MEMMEM@
 NO_STRLCPY=@NO_STRLCPY@
 NO_UINTMAX_T=@NO_UINTMAX_T@
index 5601e8bac953c670e35f32ffe48d157dd5694ce7..708e7b86ce2a55dffc162025c7a009a88b0d8c38 100644 (file)
@@ -783,6 +783,12 @@ GIT_CHECK_FUNC(strcasestr,
 [NO_STRCASESTR=YesPlease])
 AC_SUBST(NO_STRCASESTR)
 #
+# Define NO_STRTOK_R if you don't have strtok_r
+GIT_CHECK_FUNC(strtok_r,
+[NO_STRTOK_R=],
+[NO_STRTOK_R=YesPlease])
+AC_SUBST(NO_STRTOK_R)
+#
 # Define NO_MEMMEM if you don't have memmem.
 GIT_CHECK_FUNC(memmem,
 [NO_MEMMEM=],
index 5c72f655c7e4fb1bd18e979d33bd94062fce8c1a..23ffb028d1ece96d8c363ddeacca83d2b20b628f 100755 (executable)
@@ -631,7 +631,7 @@ then
        if test -z "$quiet"
        then
                commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
-                      --summary --root HEAD --`
+                      --abbrev --summary --root HEAD --`
                echo "Created${initial_commit:+ initial} commit $commit"
        fi
 fi
index 49f00321b28833c24ebb78ea2104f34091d43017..60a05a8b978345224c0313edb8060f5a7c2ccb54 100755 (executable)
@@ -181,7 +181,6 @@ Conflicts:
        esac
        exit 1
 }
-echo >&2 "Finished one $me."
 
 # If we are cherry-pick, and if the merge did not result in
 # hand-editing, we will hit this commit and inherit the original
index 30ae63d74da065a31cced0b161708680f39c04c0..00850864371e16d1fa572b0d0e5832b8a88d8666 100755 (executable)
 #     "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
 #   Be careful if "..." contains things that will be expanded by shell "eval"
 #   or printf.
+# hooks.emailmaxlines
+#   The maximum number of lines that should be included in the generated
+#   email body. If not specified, there is no limit.
+#   Lines beyond the limit are suppressed and counted, and a final
+#   line is added indicating the number of suppressed lines.
 #
 # Notes
 # -----
@@ -84,6 +89,7 @@ generate_email()
        oldrev=$(git rev-parse $1)
        newrev=$(git rev-parse $2)
        refname="$3"
+       maxlines=$4
 
        # --- Interpret
        # 0000->1234 (create)
@@ -192,7 +198,12 @@ generate_email()
                fn_name=atag
                ;;
        esac
-       generate_${change_type}_${fn_name}_email
+
+       if [ -z "$maxlines" ]; then
+               generate_${change_type}_${fn_name}_email
+       else
+               generate_${change_type}_${fn_name}_email | limit_lines $maxlines
+       fi
 
        generate_email_footer
 }
@@ -203,7 +214,7 @@ generate_email_header()
        # Generate header
        cat <<-EOF
        To: $recipients
-       Subject: ${emailprefix}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
+       Subject: ${emailprefix}$projectdesc $refname_type $short_refname ${change_type}d. $describe
        X-Git-Refname: $refname
        X-Git-Reftype: $refname_type
        X-Git-Oldrev: $oldrev
@@ -642,6 +653,24 @@ show_new_revisions()
 }
 
 
+limit_lines()
+{
+       lines=0
+       skipped=0
+       while IFS="" read -r line; do
+               lines=$((lines + 1))
+               if [ $lines -gt $1 ]; then
+                       skipped=$((skipped + 1))
+               else
+                       printf "%s\n" "$line"
+               fi
+       done
+       if [ $skipped -ne 0 ]; then
+               echo "... $skipped lines suppressed ..."
+       fi
+}
+
+
 send_mail()
 {
        if [ -n "$envelopesender" ]; then
@@ -679,6 +708,7 @@ announcerecipients=$(git config hooks.announcelist)
 envelopesender=$(git config hooks.envelopesender)
 emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
 custom_showrev=$(git config hooks.showrev)
+maxlines=$(git config hooks.emailmaxlines)
 
 # --- Main loop
 # Allow dual mode: run from the command line just like the update hook, or
@@ -691,6 +721,6 @@ if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
 else
        while read oldrev newrev refname
        do
-               generate_email $oldrev $newrev $refname | send_mail
+               generate_email $oldrev $newrev $refname $maxlines | send_mail
        done
 fi
index 27a33b669edd49ab2386337d16602de6a319d97e..02a779158572b5361396c5c093d9212126d9543d 100644 (file)
@@ -1,3 +1,4 @@
 /*.xml
 /*.1
 /*.html
+/svn-fe
index 4cc8d1582749687859c9d4db9ab02c85fa0b7d15..360d8da41754edf251b5441fcb89176ca3d4cda8 100644 (file)
@@ -38,7 +38,7 @@ svn-fe$X: svn-fe.o $(VCSSVN_LIB) $(GIT_LIB)
                $(ALL_LDFLAGS) $(LIBS)
 
 svn-fe.o: svn-fe.c ../../vcs-svn/svndump.h
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
+       $(QUIET_CC)$(CC) -I../../vcs-svn -o $*.o -c $(ALL_CFLAGS) $<
 
 svn-fe.html: svn-fe.txt
        $(QUIET_SUBDIR0)../../Documentation $(QUIET_SUBDIR1) \
index 43c4320cace0beec397ae4a05a9a8a0228fff010..a2677b03e0ff8347d13a5d56f4fa2e1aba18824a 100644 (file)
@@ -4,12 +4,13 @@
  */
 
 #include <stdlib.h>
-#include "vcs-svn/svndump.h"
+#include "svndump.h"
 
 int main(int argc, char **argv)
 {
        svndump_init(NULL);
        svndump_read((argc > 1) ? argv[1] : NULL);
+       svndump_deinit();
        svndump_reset();
        return 0;
 }
index de30f83a1f94aa8dd2bf442e22634524e2689187..35f84bd9e7577b58bc9905c8b46806bfb37963ff 100644 (file)
@@ -12,7 +12,7 @@ svnadmin dump --incremental REPO | svn-fe [url] | git fast-import
 DESCRIPTION
 -----------
 
-Converts a Subversion dumpfile (version: 2) into input suitable for
+Converts a Subversion dumpfile into input suitable for
 git-fast-import(1) and similar importers. REPO is a path to a
 Subversion repository mirrored on the local disk. Remote Subversion
 repositories can be mirrored on local disk using the `svnsync`
@@ -25,6 +25,9 @@ Subversion's repository dump format is documented in full in
 Files in this format can be generated using the 'svnadmin dump' or
 'svk admin dump' command.
 
+Dumps produced with 'svnadmin dump --deltas' (dumpfile format v3)
+are not supported.
+
 OUTPUT FORMAT
 -------------
 The fast-import format is documented by the git-fast-import(1)
@@ -43,11 +46,9 @@ user <user@UUID>
 as committer, where 'user' is the value of the `svn:author` property
 and 'UUID' the repository's identifier.
 
-To support incremental imports, 'svn-fe' will put a `git-svn-id`
-line at the end of each commit log message if passed an url on the
-command line.  This line has the form `git-svn-id: URL@REVNO UUID`.
-
-Empty directories and unknown properties are silently discarded.
+To support incremental imports, 'svn-fe' puts a `git-svn-id` line at
+the end of each commit log message if passed an url on the command
+line.  This line has the form `git-svn-id: URL@REVNO UUID`.
 
 The resulting repository will generally require further processing
 to put each project in its own repository and to separate the history
@@ -56,9 +57,9 @@ may be useful for this purpose.
 
 BUGS
 ----
-Litters the current working directory with .bin files for
-persistence. Will be fixed when the svn-fe infrastructure is aware of
-a Git working directory.
+Empty directories and unknown properties are silently discarded.
+
+The exit status does not reflect whether an error was detected.
 
 SEE ALSO
 --------
index 993cacf324b8595e5be583ff372b25353c7af95c..3ad2c0cea56392fb941282ec6225c5f9a3ce05ea 100755 (executable)
@@ -54,13 +54,13 @@ then
        die "destination directory '$new_workdir' already exists."
 fi
 
-# make sure the the links use full paths
+# make sure the links use full paths
 git_dir=$(cd "$git_dir"; pwd)
 
 # create the workdir
 mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
 
-# create the links to the original repo.  explictly exclude index, HEAD and
+# create the links to the original repo.  explicitly exclude index, HEAD and
 # logs/HEAD from the list since they are purely related to the current working
 # directory, and should not be shared.
 for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn
index e41a31e4807e92e210854214d46767b9752c5181..01de9a84c21b31a0120065a32a386f27321cdf7b 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -93,7 +93,8 @@ static int is_binary(unsigned long size, struct text_stat *stats)
        return 0;
 }
 
-static enum eol determine_output_conversion(enum action action) {
+static enum eol determine_output_conversion(enum action action)
+{
        switch (action) {
        case CRLF_BINARY:
                return EOL_UNSET;
@@ -693,7 +694,8 @@ static int git_path_check_ident(const char *path, struct git_attr_check *check)
        return !!ATTR_TRUE(value);
 }
 
-enum action determine_action(enum action text_attr, enum eol eol_attr) {
+static enum action determine_action(enum action text_attr, enum eol eol_attr)
+{
        if (text_attr == CRLF_BINARY)
                return CRLF_BINARY;
        if (eol_attr == EOL_LF)
@@ -739,7 +741,9 @@ int convert_to_git(const char *path, const char *src, size_t len,
        return ret | ident_to_git(path, src, len, dst, ident);
 }
 
-int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
+static int convert_to_working_tree_internal(const char *path, const char *src,
+                                           size_t len, struct strbuf *dst,
+                                           int normalizing)
 {
        struct git_attr_check check[5];
        enum action action = CRLF_GUESS;
@@ -765,11 +769,32 @@ int convert_to_working_tree(const char *path, const char *src, size_t len, struc
                src = dst->buf;
                len = dst->len;
        }
-       action = determine_action(action, eol_attr);
-       ret |= crlf_to_worktree(path, src, len, dst, action);
+       /*
+        * CRLF conversion can be skipped if normalizing, unless there
+        * is a smudge filter.  The filter might expect CRLFs.
+        */
+       if (filter || !normalizing) {
+               action = determine_action(action, eol_attr);
+               ret |= crlf_to_worktree(path, src, len, dst, action);
+               if (ret) {
+                       src = dst->buf;
+                       len = dst->len;
+               }
+       }
+       return ret | apply_filter(path, src, len, dst, filter);
+}
+
+int convert_to_working_tree(const char *path, const char *src, size_t len, struct strbuf *dst)
+{
+       return convert_to_working_tree_internal(path, src, len, dst, 0);
+}
+
+int renormalize_buffer(const char *path, const char *src, size_t len, struct strbuf *dst)
+{
+       int ret = convert_to_working_tree_internal(path, src, len, dst, 1);
        if (ret) {
                src = dst->buf;
                len = dst->len;
        }
-       return ret | apply_filter(path, src, len, dst, filter);
+       return ret | convert_to_git(path, src, len, dst, 0);
 }
diff --git a/date.c b/date.c
index 3c981f7eb5ebaed1ba621f3d4cd960f14145b97a..00f9eb5d0b9730107a8e08e92eb04d4cc9233595 100644 (file)
--- a/date.c
+++ b/date.c
@@ -586,7 +586,7 @@ static int date_string(unsigned long date, int offset, char *buf, int len)
 
 /* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
    (i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
 {
        struct tm tm;
        int tm_gmt;
@@ -642,17 +642,16 @@ int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
 
        if (!tm_gmt)
                *timestamp -= *offset * 60;
-       return 1; /* success */
+       return 0; /* success */
 }
 
 int parse_date(const char *date, char *result, int maxlen)
 {
        unsigned long timestamp;
        int offset;
-       if (parse_date_toffset(date, &timestamp, &offset) > 0)
-               return date_string(timestamp, offset, result, maxlen);
-       else
+       if (parse_date_basic(date, &timestamp, &offset))
                return -1;
+       return date_string(timestamp, offset, result, maxlen);
 }
 
 enum date_mode parse_date_format(const char *format)
@@ -1004,9 +1003,8 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv)
        int offset;
        int errors = 0;
 
-       if (parse_date_toffset(date, &timestamp, &offset) > 0)
+       if (!parse_date_basic(date, &timestamp, &offset))
                return timestamp;
-
        return approxidate_str(date, tv, &errors);
 }
 
@@ -1019,7 +1017,7 @@ unsigned long approxidate_careful(const char *date, int *error_ret)
        if (!error_ret)
                error_ret = &dummy;
 
-       if (parse_date_toffset(date, &timestamp, &offset) > 0) {
+       if (!parse_date_basic(date, &timestamp, &offset)) {
                *error_ret = 0;
                return timestamp;
        }
index 464ac3ffc0a45e95637e2cecdf97b3d39e5c7933..93385e12baa0d90ae475bd02500edf5ddcce320c 100644 (file)
@@ -146,7 +146,14 @@ struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
        /* Determine index hash size.  Note that indexing skips the
           first byte to allow for optimizing the Rabin's polynomial
           initialization in create_delta(). */
-       entries = (bufsize - 1)  / RABIN_WINDOW;
+       entries = (bufsize - 1) / RABIN_WINDOW;
+       if (bufsize >= 0xffffffffUL) {
+               /*
+                * Current delta format can't encode offsets into
+                * reference buffer with more than 32 bits.
+                */
+               entries = 0xfffffffeU / RABIN_WINDOW;
+       }
        hsize = entries / 4;
        for (i = 4; (1u << i) < hsize && i < 31; i++);
        hsize = 1 << i;
index 8b8978ae6d1b4d947952b7fe9ec9cea013aaa8c3..392ce2bef05746cea7922d39da67bf25d1d3d192 100644 (file)
@@ -68,11 +68,16 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
                                      unsigned ce_option, unsigned *dirty_submodule)
 {
        int changed = ce_match_stat(ce, st, ce_option);
-       if (S_ISGITLINK(ce->ce_mode)
-           && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
-           && !DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
-           && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
-               *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+       if (S_ISGITLINK(ce->ce_mode)) {
+               unsigned orig_flags = diffopt->flags;
+               if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+                       set_diffopt_flags_from_submodule_config(diffopt, ce->name);
+               if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+                       changed = 0;
+               else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
+                   && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
+                       *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+               diffopt->flags = orig_flags;
        }
        return changed;
 }
index 43aeeba2e0fd9c3c175dbc74a6f488e2c352c928..ce9e783407437bb1e0efc6d5bc2392af26da5a41 100644 (file)
@@ -64,7 +64,8 @@ static int queue_diff(struct diff_options *o,
 
        if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
                char buffer1[PATH_MAX], buffer2[PATH_MAX];
-               struct string_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
+               struct string_list p1 = STRING_LIST_INIT_DUP;
+               struct string_list p2 = STRING_LIST_INIT_DUP;
                int len1 = 0, len2 = 0, i1, i2, ret = 0;
 
                if (name1 && read_directory(name1, &p1))
diff --git a/diff.c b/diff.c
index 17873f3d9e88fccbf98e790d243397dcc4ccb24b..144f2aaba85682ef44d307d2b015fd918336879b 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
 static int diff_mnemonic_prefix;
 static int diff_no_prefix;
+static struct diff_options default_diff_options;
 
 static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
@@ -107,6 +108,9 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
        if (!strcmp(var, "diff.wordregex"))
                return git_config_string(&diff_word_regex_cfg, var, value);
 
+       if (!strcmp(var, "diff.ignoresubmodules"))
+               handle_ignore_submodules_arg(&default_diff_options, value);
+
        return git_diff_basic_config(var, value, cb);
 }
 
@@ -141,6 +145,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!prefixcmp(var, "submodule."))
+               return parse_submodule_config_option(var, value);
+
        return git_color_default_config(var, value, cb);
 }
 
@@ -2704,10 +2711,16 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
 static void strip_prefix(int prefix_length, const char **namep, const char **otherp)
 {
        /* Strip the prefix but do not molest /dev/null and absolute paths */
-       if (*namep && **namep != '/')
+       if (*namep && **namep != '/') {
                *namep += prefix_length;
-       if (*otherp && **otherp != '/')
+               if (**namep == '/')
+                       ++*namep;
+       }
+       if (*otherp && **otherp != '/') {
                *otherp += prefix_length;
+               if (**otherp == '/')
+                       ++*otherp;
+       }
 }
 
 static void run_diff(struct diff_filepair *p, struct diff_options *o)
@@ -2813,8 +2826,7 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
 
 void diff_setup(struct diff_options *options)
 {
-       memset(options, 0, sizeof(*options));
-       memset(&diff_queued_diff, 0, sizeof(diff_queued_diff));
+       memcpy(options, &default_diff_options, sizeof(*options));
 
        options->file = stdout;
 
@@ -2990,9 +3002,100 @@ static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *va
 
 static int diff_scoreopt_parse(const char *opt);
 
+static inline int short_opt(char opt, const char **argv,
+                           const char **optarg)
+{
+       const char *arg = argv[0];
+       if (arg[0] != '-' || arg[1] != opt)
+               return 0;
+       if (arg[2] != '\0') {
+               *optarg = arg + 2;
+               return 1;
+       }
+       if (!argv[1])
+               die("Option '%c' requires a value", opt);
+       *optarg = argv[1];
+       return 2;
+}
+
+int parse_long_opt(const char *opt, const char **argv,
+                  const char **optarg)
+{
+       const char *arg = argv[0];
+       if (arg[0] != '-' || arg[1] != '-')
+               return 0;
+       arg += strlen("--");
+       if (prefixcmp(arg, opt))
+               return 0;
+       arg += strlen(opt);
+       if (*arg == '=') { /* sticked form: --option=value */
+               *optarg = arg + 1;
+               return 1;
+       }
+       if (*arg != '\0')
+               return 0;
+       /* separate form: --option value */
+       if (!argv[1])
+               die("Option '--%s' requires a value", opt);
+       *optarg = argv[1];
+       return 2;
+}
+
+static int stat_opt(struct diff_options *options, const char **av)
+{
+       const char *arg = av[0];
+       char *end;
+       int width = options->stat_width;
+       int name_width = options->stat_name_width;
+       int argcount = 1;
+
+       arg += strlen("--stat");
+       end = (char *)arg;
+
+       switch (*arg) {
+       case '-':
+               if (!prefixcmp(arg, "-width")) {
+                       arg += strlen("-width");
+                       if (*arg == '=')
+                               width = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-width' requires a value");
+                       else if (!*arg) {
+                               width = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
+               } else if (!prefixcmp(arg, "-name-width")) {
+                       arg += strlen("-name-width");
+                       if (*arg == '=')
+                               name_width = strtoul(arg + 1, &end, 10);
+                       else if (!*arg && !av[1])
+                               die("Option '--stat-name-width' requires a value");
+                       else if (!*arg) {
+                               name_width = strtoul(av[1], &end, 10);
+                               argcount = 2;
+                       }
+               }
+               break;
+       case '=':
+               width = strtoul(arg+1, &end, 10);
+               if (*end == ',')
+                       name_width = strtoul(end+1, &end, 10);
+       }
+
+       /* Important! This checks all the error cases! */
+       if (*end)
+               return 0;
+       options->output_format |= DIFF_FORMAT_DIFFSTAT;
+       options->stat_name_width = name_width;
+       options->stat_width = width;
+       return argcount;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
        const char *arg = av[0];
+       const char *optarg;
+       int argcount;
 
        /* Output format options */
        if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
@@ -3029,33 +3132,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->output_format |= DIFF_FORMAT_NAME_STATUS;
        else if (!strcmp(arg, "-s"))
                options->output_format |= DIFF_FORMAT_NO_OUTPUT;
-       else if (!prefixcmp(arg, "--stat")) {
-               char *end;
-               int width = options->stat_width;
-               int name_width = options->stat_name_width;
-               arg += 6;
-               end = (char *)arg;
-
-               switch (*arg) {
-               case '-':
-                       if (!prefixcmp(arg, "-width="))
-                               width = strtoul(arg + 7, &end, 10);
-                       else if (!prefixcmp(arg, "-name-width="))
-                               name_width = strtoul(arg + 12, &end, 10);
-                       break;
-               case '=':
-                       width = strtoul(arg+1, &end, 10);
-                       if (*end == ',')
-                               name_width = strtoul(end+1, &end, 10);
-               }
-
-               /* Important! This checks all the error cases! */
-               if (*end)
-                       return 0;
-               options->output_format |= DIFF_FORMAT_DIFFSTAT;
-               options->stat_name_width = name_width;
-               options->stat_width = width;
-       }
+       else if (!prefixcmp(arg, "--stat"))
+               /* --stat, --stat-width, or --stat-name-width */
+               return stat_opt(options, av);
 
        /* renames options */
        else if (!prefixcmp(arg, "-B")) {
@@ -3149,10 +3228,11 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else
                        die("bad --word-diff argument: %s", type);
        }
-       else if (!prefixcmp(arg, "--word-diff-regex=")) {
+       else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
                if (options->word_diff == DIFF_WORDS_NONE)
                        options->word_diff = DIFF_WORDS_PLAIN;
-               options->word_regex = arg + 18;
+               options->word_regex = optarg;
+               return argcount;
        }
        else if (!strcmp(arg, "--exit-code"))
                DIFF_OPT_SET(options, EXIT_WITH_STATUS);
@@ -3166,11 +3246,13 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                DIFF_OPT_SET(options, ALLOW_TEXTCONV);
        else if (!strcmp(arg, "--no-textconv"))
                DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
-       else if (!strcmp(arg, "--ignore-submodules"))
+       else if (!strcmp(arg, "--ignore-submodules")) {
+               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, "all");
-       else if (!prefixcmp(arg, "--ignore-submodules="))
+       } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+               DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(options, arg + 20);
-       else if (!strcmp(arg, "--submodule"))
+       else if (!strcmp(arg, "--submodule"))
                DIFF_OPT_SET(options, SUBMODULE_LOG);
        else if (!prefixcmp(arg, "--submodule=")) {
                if (!strcmp(arg + 12, "log"))
@@ -3180,18 +3262,26 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        /* misc options */
        else if (!strcmp(arg, "-z"))
                options->line_termination = 0;
-       else if (!prefixcmp(arg, "-l"))
-               options->rename_limit = strtoul(arg+2, NULL, 10);
-       else if (!prefixcmp(arg, "-S"))
-               options->pickaxe = arg + 2;
+       else if ((argcount = short_opt('l', av, &optarg))) {
+               options->rename_limit = strtoul(optarg, NULL, 10);
+               return argcount;
+       }
+       else if ((argcount = short_opt('S', av, &optarg))) {
+               options->pickaxe = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--pickaxe-all"))
                options->pickaxe_opts = DIFF_PICKAXE_ALL;
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts = DIFF_PICKAXE_REGEX;
-       else if (!prefixcmp(arg, "-O"))
-               options->orderfile = arg + 2;
-       else if (!prefixcmp(arg, "--diff-filter="))
-               options->filter = arg + 14;
+       else if ((argcount = short_opt('O', av, &optarg))) {
+               options->orderfile = optarg;
+               return argcount;
+       }
+       else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
+               options->filter = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--abbrev"))
                options->abbrev = DEFAULT_ABBREV;
        else if (!prefixcmp(arg, "--abbrev=")) {
@@ -3201,20 +3291,25 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                else if (40 < options->abbrev)
                        options->abbrev = 40;
        }
-       else if (!prefixcmp(arg, "--src-prefix="))
-               options->a_prefix = arg + 13;
-       else if (!prefixcmp(arg, "--dst-prefix="))
-               options->b_prefix = arg + 13;
+       else if ((argcount = parse_long_opt("src-prefix", av, &optarg))) {
+               options->a_prefix = optarg;
+               return argcount;
+       }
+       else if ((argcount = parse_long_opt("dst-prefix", av, &optarg))) {
+               options->b_prefix = optarg;
+               return argcount;
+       }
        else if (!strcmp(arg, "--no-prefix"))
                options->a_prefix = options->b_prefix = "";
        else if (opt_arg(arg, '\0', "inter-hunk-context",
                         &options->interhunkcontext))
                ;
-       else if (!prefixcmp(arg, "--output=")) {
-               options->file = fopen(arg + strlen("--output="), "w");
+       else if ((argcount = parse_long_opt("output", av, &optarg))) {
+               options->file = fopen(optarg, "w");
                if (!options->file)
                        die_errno("Could not open '%s'", arg + strlen("--output="));
                options->close_file = 1;
+               return argcount;
        } else
                return 0;
        return 1;
@@ -3758,6 +3853,13 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                                        len2, p->two->path);
                git_SHA1_Update(&ctx, buffer, len1);
 
+               if (diff_filespec_is_binary(p->one) ||
+                   diff_filespec_is_binary(p->two)) {
+                       git_SHA1_Update(&ctx, sha1_to_hex(p->one->sha1), 40);
+                       git_SHA1_Update(&ctx, sha1_to_hex(p->two->sha1), 40);
+                       continue;
+               }
+
                xpp.flags = 0;
                xecfg.ctxlen = 3;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -4059,25 +4161,24 @@ void diffcore_fix_diff_index(struct diff_options *options)
 
 void diffcore_std(struct diff_options *options)
 {
-       /* We never run this function more than one time, because the
-        * rename/copy detection logic can only run once.
-        */
-       if (diff_queued_diff.run)
-               return;
-
        if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
-       if (options->break_opt != -1)
-               diffcore_break(options->break_opt);
-       if (options->detect_rename)
-               diffcore_rename(options);
-       if (options->break_opt != -1)
-               diffcore_merge_broken();
+       if (!options->found_follow) {
+               /* See try_to_follow_renames() in tree-diff.c */
+               if (options->break_opt != -1)
+                       diffcore_break(options->break_opt);
+               if (options->detect_rename)
+                       diffcore_rename(options);
+               if (options->break_opt != -1)
+                       diffcore_merge_broken();
+       }
        if (options->pickaxe)
                diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
        if (options->orderfile)
                diffcore_order(options->orderfile);
-       diff_resolve_rename_copy();
+       if (!options->found_follow)
+               /* See try_to_follow_renames() in tree-diff.c */
+               diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
 
        if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
@@ -4085,7 +4186,7 @@ void diffcore_std(struct diff_options *options)
        else
                DIFF_OPT_CLR(options, HAS_CHANGES);
 
-       diff_queued_diff.run = 1;
+       options->found_follow = 0;
 }
 
 int diff_result_code(struct diff_options *opt, int status)
@@ -4103,6 +4204,24 @@ int diff_result_code(struct diff_options *opt, int status)
        return result;
 }
 
+/*
+ * Shall changes to this submodule be ignored?
+ *
+ * Submodule changes can be configured to be ignored separately for each path,
+ * but that configuration can be overridden from the command line.
+ */
+static int is_submodule_ignored(const char *path, struct diff_options *options)
+{
+       int ignored = 0;
+       unsigned orig_flags = options->flags;
+       if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+               set_diffopt_flags_from_submodule_config(options, path);
+       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+               ignored = 1;
+       options->flags = orig_flags;
+       return ignored;
+}
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -4110,7 +4229,7 @@ void diff_addremove(struct diff_options *options,
 {
        struct diff_filespec *one, *two;
 
-       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
+       if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
                return;
 
        /* This may look odd, but it is a preparation for
@@ -4157,8 +4276,8 @@ void diff_change(struct diff_options *options,
 {
        struct diff_filespec *one, *two;
 
-       if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
-                       && S_ISGITLINK(new_mode))
+       if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
+           is_submodule_ignored(concatpath, options))
                return;
 
        if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
diff --git a/diff.h b/diff.h
index 063d10ac2216071ef218fab2ad51c13787614acf..bf2f44d840735684c89f654ee6f3b3a935af2c55 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -77,6 +77,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
 #define DIFF_OPT_DIRTY_SUBMODULES    (1 << 24)
 #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
 #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
+#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
 
 #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)
 #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)
@@ -126,6 +127,9 @@ struct diff_options {
        /* this is set by diffcore for DIFF_FORMAT_PATCH */
        int found_changes;
 
+       /* to support internal diff recursion by --follow hack*/
+       int found_follow;
+
        FILE *file;
        int close_file;
 
@@ -214,6 +218,13 @@ extern void diff_unmerge(struct diff_options *,
 #define DIFF_SETUP_USE_CACHE           2
 #define DIFF_SETUP_USE_SIZE_CACHE      4
 
+/*
+ * Poor man's alternative to parse-option, to allow both sticked form
+ * (--option=value) and separate form (--option value).
+ */
+extern int parse_long_opt(const char *opt, const char **argv,
+                        const char **optarg);
+
 extern int git_diff_basic_config(const char *var, const char *value, void *cb);
 extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern int diff_use_color_default;
index 491bea0b44963461cfce30b07ec96a9005a3c910..8b3241ad137f5934e32336cd1caf8d99ca11d1f5 100644 (file)
@@ -18,7 +18,7 @@
 #define MAX_SCORE 60000.0
 #define DEFAULT_RENAME_SCORE 30000 /* rename/copy similarity minimum (50%) */
 #define DEFAULT_BREAK_SCORE  30000 /* minimum for break to happen (50%) */
-#define DEFAULT_MERGE_SCORE  36000 /* maximum for break-merge to happen 60%) */
+#define DEFAULT_MERGE_SCORE  36000 /* maximum for break-merge to happen (60%) */
 
 #define MINIMUM_BREAK_SIZE     400 /* do not break a file smaller than this */
 
@@ -91,14 +91,12 @@ struct diff_queue_struct {
        struct diff_filepair **queue;
        int alloc;
        int nr;
-       int run;
 };
 #define DIFF_QUEUE_CLEAR(q) \
        do { \
                (q)->queue = NULL; \
                (q)->nr = (q)->alloc = 0; \
-               (q)->run = 0; \
-       } while(0);
+       } while (0)
 
 extern struct diff_queue_struct diff_queued_diff;
 extern struct diff_filepair *diff_queue(struct diff_queue_struct *,
@@ -118,9 +116,9 @@ void diff_debug_filespec(struct diff_filespec *, int, const char *);
 void diff_debug_filepair(const struct diff_filepair *, int);
 void diff_debug_queue(const char *, struct diff_queue_struct *);
 #else
-#define diff_debug_filespec(a,b,c) do {} while(0)
-#define diff_debug_filepair(a,b) do {} while(0)
-#define diff_debug_queue(a,b) do {} while(0)
+#define diff_debug_filespec(a,b,c) do { /* nothing */ } while (0)
+#define diff_debug_filepair(a,b) do { /* nothing */ } while (0)
+#define diff_debug_queue(a,b) do { /* nothing */ } while (0)
 #endif
 
 extern int diffcore_count_changes(struct diff_filespec *src,
index 83d38d3c2354e8582d5af91c6d529a2f2836dc2c..eeb26876a181f57fcdf5fd324c06a90c705623dc 100644 (file)
@@ -53,6 +53,7 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
 char *notes_ref_name;
 int grafts_replace_parents = 1;
 int core_apply_sparse_checkout;
+struct startup_info *startup_info;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
index 1e5d66ed0ab3b605de670758887b13f146bdd8a5..2317b0fe7509b957577234890135509143c0872b 100644 (file)
@@ -1528,6 +1528,14 @@ static int tree_content_remove(
        for (i = 0; i < t->entry_count; i++) {
                e = t->entries[i];
                if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+                       if (slash1 && !S_ISDIR(e->versions[1].mode))
+                               /*
+                                * If p names a file in some subdirectory, and a
+                                * file or symlink matching the name of the
+                                * parent directory of p exists, then p cannot
+                                * exist and need not be deleted.
+                                */
+                               return 1;
                        if (!slash1 || !S_ISDIR(e->versions[1].mode))
                                goto del_entry;
                        if (!e->tree)
@@ -1666,7 +1674,7 @@ static void dump_marks_helper(FILE *f,
        if (m->shift) {
                for (k = 0; k < 1024; k++) {
                        if (m->data.sets[k])
-                               dump_marks_helper(f, (base + k) << m->shift,
+                               dump_marks_helper(f, base + (k << m->shift),
                                        m->data.sets[k]);
                }
        } else {
@@ -2131,6 +2139,7 @@ static void file_change_m(struct branch *b)
        case S_IFREG | 0644:
        case S_IFREG | 0755:
        case S_IFLNK:
+       case S_IFDIR:
        case S_IFGITLINK:
                /* ok */
                break;
@@ -2176,23 +2185,28 @@ static void file_change_m(struct branch *b)
                 * another repository.
                 */
        } else if (inline_data) {
+               if (S_ISDIR(mode))
+                       die("Directories cannot be specified 'inline': %s",
+                               command_buf.buf);
                if (p != uq.buf) {
                        strbuf_addstr(&uq, p);
                        p = uq.buf;
                }
                read_next_command();
                parse_and_store_blob(&last_blob, sha1, 0);
-       } else if (oe) {
-               if (oe->type != OBJ_BLOB)
-                       die("Not a blob (actually a %s): %s",
-                               typename(oe->type), command_buf.buf);
        } else {
-               enum object_type type = sha1_object_info(sha1, NULL);
+               enum object_type expected = S_ISDIR(mode) ?
+                                               OBJ_TREE: OBJ_BLOB;
+               enum object_type type = oe ? oe->type :
+                                       sha1_object_info(sha1, NULL);
                if (type < 0)
-                       die("Blob not found: %s", command_buf.buf);
-               if (type != OBJ_BLOB)
-                       die("Not a blob (actually a %s): %s",
-                           typename(type), command_buf.buf);
+                       die("%s not found: %s",
+                                       S_ISDIR(mode) ?  "Tree" : "Blob",
+                                       command_buf.buf);
+               if (type != expected)
+                       die("Not a %s (actually a %s): %s",
+                               typename(expected), typename(type),
+                               command_buf.buf);
        }
 
        tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
index 02a73eeb667e798fca29de25fed6b0b2900f6912..877096ecb09e524174ff22db3722d6428f43bcf4 100644 (file)
@@ -312,6 +312,11 @@ extern size_t gitstrlcpy(char *, const char *, size_t);
 extern uintmax_t gitstrtoumax(const char *, char **, int);
 #endif
 
+#ifdef NO_STRTOK_R
+#define strtok_r gitstrtok_r
+extern char *gitstrtok_r(char *s, const char *delim, char **save_ptr);
+#endif
+
 #ifdef NO_HSTRERROR
 #define hstrerror githstrerror
 extern const char *githstrerror(int herror);
@@ -388,6 +393,8 @@ extern int odb_pack_keep(char *name, size_t namesz, unsigned char *sha1);
 
 static inline size_t xsize_t(off_t len)
 {
+       if (len > (size_t) len)
+               die("Cannot handle files this big");
        return (size_t)len;
 }
 
index 7d5451198cb109e1d3e30e3e33f1557bbd8b12c0..bb104895a94450a2ade7446c8fda52910bae0868 100755 (executable)
@@ -38,7 +38,7 @@ if {[catch {package require Tcl 8.4} err]
        tk_messageBox \
                -icon error \
                -type ok \
-               -title [mc "git-gui: fatal error"] \
+               -title "git-gui: fatal error" \
                -message $err
        exit 1
 }
@@ -269,6 +269,17 @@ proc is_config_true {name} {
        }
 }
 
+proc is_config_false {name} {
+       global repo_config
+       if {[catch {set v $repo_config($name)}]} {
+               return 0
+       } elseif {$v eq {false} || $v eq {0} || $v eq {no}} {
+               return 1
+       } else {
+               return 0
+       }
+}
+
 proc get_config {name} {
        global repo_config
        if {[catch {set v $repo_config($name)}]} {
@@ -323,6 +334,8 @@ proc _trace_exec {cmd} {
        puts stderr $d
 }
 
+#'"  fix poor old emacs font-lock mode
+
 proc _git_cmd {name} {
        global _git_cmd_path
 
@@ -416,6 +429,9 @@ proc _lappend_nice {cmd_var} {
 
        if {![info exists _nice]} {
                set _nice [_which nice]
+               if {[catch {exec $_nice git version}]} {
+                       set _nice {}
+               }
        }
        if {$_nice ne {}} {
                lappend cmd $_nice
@@ -634,6 +650,7 @@ proc rmsel_tag {text} {
        return $text
 }
 
+wm withdraw .
 set root_exists 0
 bind . <Visibility> {
        bind . <Visibility> {}
@@ -782,6 +799,7 @@ set default_config(user.email) {}
 
 set default_config(gui.encoding) [encoding system]
 set default_config(gui.matchtrackingbranch) false
+set default_config(gui.textconv) true
 set default_config(gui.pruneduringfetch) false
 set default_config(gui.trustmtime) false
 set default_config(gui.fastcopyblame) false
@@ -1155,6 +1173,9 @@ apply_config
 # try to set work tree from environment, falling back to core.worktree
 if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
        set _gitworktree [get_config core.worktree]
+       if {$_gitworktree eq ""} {
+               set _gitworktree [file dirname [file normalize $_gitdir]]
+       }
 }
 if {$_prefix ne {}} {
        if {$_gitworktree eq {}} {
@@ -2098,7 +2119,7 @@ proc do_explore {} {
                # freedesktop.org-conforming system is our best shot
                set explorer "xdg-open"
        }
-       eval exec $explorer $_gitworktree &
+       eval exec $explorer [list [file nativename $_gitworktree]] &
 }
 
 set is_quitting 0
@@ -2901,6 +2922,7 @@ blame {
                set current_branch $head
        }
 
+       wm deiconify .
        switch -- $subcommand {
        browser {
                if {$jump_spec ne {}} usage
@@ -3405,6 +3427,19 @@ lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
 $ctxmsm add separator
 create_common_diff_popup $ctxmsm
 
+proc has_textconv {path} {
+       if {[is_config_false gui.textconv]} {
+               return 0
+       }
+       set filter [gitattr $path diff set]
+       set textconv [get_config [join [list diff $filter textconv] .]]
+       if {$filter ne {set} && $textconv ne {}} {
+               return 1
+       } else {
+               return 0
+       }
+}
+
 proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
        global current_diff_path file_states
        set ::cursorX $x
@@ -3440,7 +3475,8 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
                        || {__} eq $state
                        || {_O} eq $state
                        || {_T} eq $state
-                       || {T_} eq $state} {
+                       || {T_} eq $state
+                       || [has_textconv $current_diff_path]} {
                        set s disabled
                } else {
                        set s normal
@@ -3460,29 +3496,44 @@ $main_status show [mc "Initializing..."]
 
 # -- Load geometry
 #
-catch {
-set gm $repo_config(gui.geometry)
-wm geometry . [lindex $gm 0]
-if {$use_ttk} {
-       .vpane sashpos 0 [lindex $gm 1]
-       .vpane.files sashpos 0 [lindex $gm 2]
-} else {
-       .vpane sash place 0 \
-               [lindex $gm 1] \
-               [lindex [.vpane sash coord 0] 1]
-       .vpane.files sash place 0 \
-               [lindex [.vpane.files sash coord 0] 0] \
-               [lindex $gm 2]
+proc on_ttk_pane_mapped {w pane pos} {
+       bind $w <Map> {}
+       after 0 [list after idle [list $w sashpos $pane $pos]]
+}
+proc on_tk_pane_mapped {w pane x y} {
+       bind $w <Map> {}
+       after 0 [list after idle [list $w sash place $pane $x $y]]
+}
+proc on_application_mapped {} {
+       global repo_config use_ttk
+       bind . <Map> {}
+       set gm $repo_config(gui.geometry)
+       if {$use_ttk} {
+               bind .vpane <Map> \
+                   [list on_ttk_pane_mapped %W 0 [lindex $gm 1]]
+               bind .vpane.files <Map> \
+                   [list on_ttk_pane_mapped %W 0 [lindex $gm 2]]
+       } else {
+               bind .vpane <Map> \
+                   [list on_tk_pane_mapped %W 0 \
+                        [lindex $gm 1] \
+                        [lindex [.vpane sash coord 0] 1]]
+               bind .vpane.files <Map> \
+                   [list on_tk_pane_mapped %W 0 \
+                        [lindex [.vpane.files sash coord 0] 0] \
+                        [lindex $gm 2]]
+       }
+       wm geometry . [lindex $gm 0]
 }
-unset gm
+if {[info exists repo_config(gui.geometry)]} {
+       bind . <Map> [list on_application_mapped]
+       wm geometry . [lindex $repo_config(gui.geometry) 0]
 }
 
 # -- Load window state
 #
-catch {
-set gws $repo_config(gui.wmstate)
-wm state . $gws
-unset gws
+if {[info exists repo_config(gui.wmstate)]} {
+       catch {wm state . $repo_config(gui.wmstate)}
 }
 
 # -- Key Bindings
index 786b50b8c2b6f877d1d1a3a86d27368281ec2ae8..2137ec9684d0acdb386e6b50e1b41aa7705c9746 100644 (file)
@@ -449,11 +449,28 @@ method _load {jump} {
 
        $status show [mc "Reading %s..." "$commit:[escape_path $path]"]
        $w_path conf -text [escape_path $path]
+
+       set do_textconv 0
+       if {![is_config_false gui.textconv] && [git-version >= 1.7.2]} {
+               set filter [gitattr $path diff set]
+               set textconv [get_config [join [list diff $filter textconv] .]]
+               if {$filter ne {set} && $textconv ne {}} {
+                       set do_textconv 1
+               }
+       }
        if {$commit eq {}} {
-               set fd [open $path r]
+               if {$do_textconv ne 0} {
+                       set fd [open |[list $textconv $path] r]
+               } else {
+                       set fd [open $path r]
+               }
                fconfigure $fd -eofchar {}
        } else {
-               set fd [git_read cat-file blob "$commit:$path"]
+               if {$do_textconv ne 0} {
+                       set fd [git_read cat-file --textconv "$commit:$path"]
+               } else {
+                       set fd [git_read cat-file blob "$commit:$path"]
+               }
        }
        fconfigure $fd \
                -blocking 0 \
index 64f06748b612721143e851824e30987575010be2..fae119286d3d4511d1b362fb05a8fab775e80a40 100644 (file)
@@ -100,12 +100,17 @@ constructor pick {} {
        $opts insert end [mc "Clone Existing Repository"] link_clone
        $opts insert end "\n"
        if {$m_repo ne {}} {
+               if {[tk windowingsystem] eq "win32"} {
+                       set key L
+               } else {
+                       set key C
+               }
                $m_repo add command \
                        -command [cb _next clone] \
-                       -accelerator $M1T-C \
+                       -accelerator $M1T-$key \
                        -label [mc "Clone..."]
-               bind $top <$M1B-c> [cb _next clone]
-               bind $top <$M1B-C> [cb _next clone]
+               bind $top <$M1B-[string tolower $key]> [cb _next clone]
+               bind $top <$M1B-[string toupper $key]> [cb _next clone]
        }
 
        $opts tag conf link_open -foreground blue -underline 1
index ec8c11eeb7912e2e49e48f467ffc27fd9fc5f919..c628750276303fca620c9292463c4c59690c764f 100644 (file)
@@ -55,7 +55,7 @@ proc handle_empty_diff {} {
 
        set path $current_diff_path
        set s $file_states($path)
-       if {[lindex $s 0] ne {_M}} return
+       if {[lindex $s 0] ne {_M} || [has_textconv $path]} return
 
        # Prevent infinite rescan loops
        incr diff_empty_count
@@ -280,6 +280,9 @@ proc start_show_diff {cont_info {add_opts {}}} {
                        lappend cmd diff-files
                }
        }
+       if {![is_config_false gui.textconv] && [git-version >= 1.6.1]} {
+               lappend cmd --textconv
+       }
 
        if {[string match {160000 *} [lindex $s 2]]
         || [string match {160000 *} [lindex $s 3]]} {
index d4c5e45c8a7ade900753cb7eb5caf977e6e4ae2a..3807c8d28324a277204db9191e99ddb856041c22 100644 (file)
@@ -148,6 +148,7 @@ proc do_options {} {
                {b gui.trustmtime  {mc "Trust File Modification Timestamps"}}
                {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}}
                {b gui.matchtrackingbranch {mc "Match Tracking Branches"}}
+               {b gui.textconv {mc "Use Textconv For Diffs and Blames"}}
                {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}}
                {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}}
                {i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}}
index 79c1888e11d210eed962eebdf67793a7367dff5a..78878ef89d11210d614fc8b3d2957705611bdaa3 100644 (file)
@@ -16,7 +16,7 @@ proc do_windows_shortcut {} {
                                        [info nameofexecutable] \
                                        [file normalize $::argv0] \
                                        ] \
-                                       [file normalize [$_gitworktree]]
+                                       [file normalize $_gitworktree]
                        } err]} {
                        error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
                }
@@ -57,7 +57,7 @@ proc do_cygwin_shortcut {} {
                                        $sh -c \
                                        "CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \
                                        ] \
-                                       [file normalize [$_gitworktree]]
+                                       [file normalize $_gitworktree]
                        } err]} {
                        error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"]
                }
index 5fe3aad382f43a30a099a106f7d51f136eda0652..95cb44991fc5b018805d6091c4f98ce7ae0ccf52 100644 (file)
@@ -39,6 +39,7 @@ method _oneline_pack {} {
 }
 
 constructor two_line {path} {
+       global NS
        set w $path
        set w_l $w.l
        set w_c $w.c
index d7f93d045d1a2b3a14d2fdb4907697622b5973a8..db91ab84a56d79be6a5497f885a9181f368b9cf2 100644 (file)
@@ -18,9 +18,9 @@ proc win32_create_lnk {lnk_path lnk_exec lnk_dir} {
        eval [list exec wscript.exe \
                /E:jscript \
                /nologo \
-               [file join $oguilib win32_shortcut.js] \
+               [file nativename [file join $oguilib win32_shortcut.js]] \
                $lnk_path \
-               [file join $oguilib git-gui.ico] \
+               [file nativename [file join $oguilib git-gui.ico]] \
                $lnk_dir \
                $lnk_exec] $lnk_args
 }
index 66bbb2f8faaf83bc87819a9e288a0592f400e147..b1845c505500a0f079b2299de07d481c6b2550c4 100644 (file)
@@ -13,10 +13,11 @@ if { $argc >=2 && [lindex $argv 0] == "--working-dir" } {
        incr argc -2
 }
 
-set bindir [file dirname \
+set basedir [file dirname \
             [file dirname \
              [file dirname [info script]]]]
-set bindir [file join $bindir bin]
+set bindir [file join $basedir bin]
+set bindir "$bindir;[file join $basedir mingw bin]"
 regsub -all ";" $bindir "\\;" bindir
 set env(PATH) "$bindir;$env(PATH)"
 unset bindir
index 6635fbefdf2a06e556716f91a70181ecf4f3252a..e6f6ecda177c66a79b18359f7bf8cad9c4744c8c 100755 (executable)
@@ -43,7 +43,8 @@ test -z "$port" && port=1234
 
 resolve_full_httpd () {
        case "$httpd" in
-       *apache2*|*lighttpd*)
+       *apache2*|*lighttpd*|*httpd*)
+               # yes, *httpd* covers *lighttpd* above, but it is there for clarity
                # ensure that the apache2/lighttpd command ends with "-f"
                if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
                then
@@ -56,6 +57,13 @@ resolve_full_httpd () {
                httpd_only="${httpd%% *}" # cut on first space
                return
                ;;
+       *webrick*)
+               # server is started by running via generated webrick.rb in
+               # $fqgitdir/gitweb
+               full_httpd="$fqgitdir/gitweb/webrick.rb"
+               httpd_only="${httpd%% *}" # cut on first space
+               return
+               ;;
        esac
 
        httpd_only="$(echo $httpd | cut -f1 -d' ')"
@@ -187,40 +195,53 @@ GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
 export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 
 webrick_conf () {
+       # webrick seems to have no way of passing arbitrary environment
+       # variables to the underlying CGI executable, so we wrap the
+       # actual gitweb.cgi using a shell script to force it
+  wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
+       cat > "$wrapper" <<EOF
+#!/bin/sh
+# we use this shell script wrapper around the real gitweb.cgi since
+# there appears to be no other way to pass arbitrary environment variables
+# into the CGI process
+GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
+exec $root/gitweb.cgi
+EOF
+       chmod +x "$wrapper"
+
+       # This assumes _ruby_ is in the user's $PATH. that's _one_
+       # portable way to run ruby, which could be installed anywhere, really.
        # generate a standalone server script in $fqgitdir/gitweb.
        cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
+#!/usr/bin/env ruby
 require 'webrick'
-require 'yaml'
-options = YAML::load_file(ARGV[0])
-options[:StartCallback] = proc do
-  File.open(options[:PidFile],"w") do |f|
-    f.puts Process.pid
-  end
-end
-options[:ServerType] = WEBrick::Daemon
+require 'logger'
+options = {
+  :Port => $port,
+  :DocumentRoot => "$root",
+  :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
+  :AccessLog => [
+    [ Logger.new('$fqgitdir/gitweb/access.log'),
+      WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
+  ],
+  :DirectoryIndex => ["gitweb.cgi"],
+  :CGIInterpreter => "$wrapper",
+  :StartCallback => lambda do
+    File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
+  end,
+  :ServerType => WEBrick::Daemon,
+}
+options[:BindAddress] = '127.0.0.1' if "$local" == "true"
 server = WEBrick::HTTPServer.new(options)
 ['INT', 'TERM'].each do |signal|
   trap(signal) {server.shutdown}
 end
 server.start
 EOF
-       # generate a shell script to invoke the above ruby script,
-       # which assumes _ruby_ is in the user's $PATH. that's _one_
-       # portable way to run ruby, which could be installed anywhere,
-       # really.
-       cat >"$fqgitdir/gitweb/$httpd" <<EOF
-#!/bin/sh
-exec ruby "$fqgitdir/gitweb/$httpd.rb" \$*
-EOF
-       chmod +x "$fqgitdir/gitweb/$httpd"
-
-       cat >"$conf" <<EOF
-:Port: $port
-:DocumentRoot: "$root"
-:DirectoryIndex: ["gitweb.cgi"]
-:PidFile: "$fqgitdir/pid"
-EOF
-       test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
+       chmod +x "$fqgitdir/gitweb/$httpd.rb"
+       # configuration is embedded in server script file, webrick.rb
+       rm -f "$conf"
 }
 
 lighttpd_conf () {
@@ -300,7 +321,13 @@ EOF
 }
 
 apache2_conf () {
-       test -z "$module_path" && module_path=/usr/lib/apache2/modules
+       if test -z "$module_path"
+       then
+               test -d "/usr/lib/httpd/modules" &&
+                       module_path="/usr/lib/httpd/modules"
+               test -d "/usr/lib/apache2/modules" &&
+                       module_path="/usr/lib/apache2/modules"
+       fi
        bind=
        test x"$local" = xtrue && bind='127.0.0.1:'
        echo 'text/css css' > "$fqgitdir/mime.types"
@@ -314,8 +341,10 @@ PidFile "$fqgitdir/pid"
 Listen $bind$port
 EOF
 
-       for mod in mime dir; do
-               if test -e $module_path/mod_${mod}.so; then
+       for mod in mime dir env log_config
+       do
+               if test -e $module_path/mod_${mod}.so
+               then
                        echo "LoadModule ${mod}_module " \
                             "$module_path/mod_${mod}.so" >> "$conf"
                fi
@@ -334,7 +363,7 @@ EOF
                cat >> "$conf" <<EOF
 LoadModule perl_module $module_path/mod_perl.so
 PerlPassEnv GIT_DIR
-PerlPassEnv GIT_EXEC_DIR
+PerlPassEnv GIT_EXEC_PATH
 PerlPassEnv GITWEB_CONFIG
 <Location /gitweb.cgi>
        SetHandler perl-script
@@ -364,6 +393,9 @@ EOF
                        echo "ScriptSock logs/gitweb.sock" >> "$conf"
                fi
                cat >> "$conf" <<EOF
+PassEnv GIT_DIR
+PassEnv GIT_EXEC_PATH
+PassEnv GITWEB_CONFIG
 AddHandler cgi-script .cgi
 <Location /gitweb.cgi>
        Options +ExecCGI
@@ -560,7 +592,7 @@ case "$httpd" in
 *lighttpd*)
        lighttpd_conf
        ;;
-*apache2*)
+*apache2*|*httpd*)
        apache2_conf
        ;;
 webrick)
index b52a7410bcb7b37dce0f4d6213dddedd2c1e42e7..bd7ab02f1161d2d97ea57b2a2ceacc307f6a74e8 100755 (executable)
@@ -264,17 +264,35 @@ merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo fa
 
 last_status=0
 rollup_status=0
+rerere=false
+
+files_to_merge() {
+    if test "$rerere" = true
+    then
+       git rerere status
+    else
+       git ls-files -u | sed -e 's/^[^ ]*      //' | sort -u
+    fi
+}
+
 
 if test $# -eq 0 ; then
-    files=$(git ls-files -u | sed -e 's/^[^    ]*      //' | sort -u)
+    cd_to_toplevel
+
+    if test -e "$GIT_DIR/MERGE_RR"
+    then
+       rerere=true
+    fi
+
+    files=$(files_to_merge)
     if test -z "$files" ; then
        echo "No files need merging"
        exit 0
     fi
-    echo Merging the files: "$files"
-    git ls-files -u |
-    sed -e 's/^[^      ]*      //' |
-    sort -u |
+    printf "Merging:\n"
+    printf "$files\n"
+
+    files_to_merge |
     while IFS= read i
     do
        if test $last_status -ne 0; then
index a09a44ec4ce3d82cebc5f972f09e94c7c35dc948..8eb74d45debe7ab2bd2cc9b5ed57b1ba49d94bdb 100755 (executable)
@@ -273,6 +273,15 @@ then
        exit
 fi
 
+if test true = "$rebase"
+then
+       o=$(git show-branch --merge-base $curr_branch $merge_head $oldremoteref)
+       if test "$oldremoteref" = "$o"
+       then
+               unset oldremoteref
+       fi
+fi
+
 merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
 case "$rebase" in
 true)
index 31e68603f4e8124fd67f42c8aae6033a792892cb..eb2dff55f81b6726340ce26db93bb161087a4857 100755 (executable)
@@ -111,15 +111,16 @@ VERBOSE=
 OK_TO_SKIP_PRE_REBASE=
 REBASE_ROOT=
 AUTOSQUASH=
+test "$(git config --bool rebase.autosquash)" = "true" && AUTOSQUASH=t
 NEVER_FF=
 
-GIT_CHERRY_PICK_HELP="  After resolving the conflicts,
-mark the corrected paths with 'git add <paths>', and
-run 'git rebase --continue'"
+GIT_CHERRY_PICK_HELP="\
+hint: after resolving the conflicts, mark the corrected paths
+hint: with 'git add <paths>' and run 'git rebase --continue'"
 export GIT_CHERRY_PICK_HELP
 
 warn () {
-       echo "$*" >&2
+       printf '%s\n' "$*" >&2
 }
 
 output () {
@@ -537,6 +538,34 @@ do_next () {
                esac
                record_in_rewritten $sha1
                ;;
+       x|"exec")
+               read -r command rest < "$TODO"
+               mark_action_done
+               printf 'Executing: %s\n' "$rest"
+               # "exec" command doesn't take a sha1 in the todo-list.
+               # => can't just use $sha1 here.
+               git rev-parse --verify HEAD > "$DOTEST"/stopped-sha
+               ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
+               status=$?
+               if test "$status" -ne 0
+               then
+                       warn "Execution failed: $rest"
+                       warn "You can fix the problem, and then run"
+                       warn
+                       warn "  git rebase --continue"
+                       warn
+                       exit "$status"
+               fi
+               # Run in subshell because require_clean_work_tree can die.
+               if ! (require_clean_work_tree)
+               then
+                       warn "Commit or stash your changes, and then run"
+                       warn
+                       warn "  git rebase --continue"
+                       warn
+                       exit 1
+               fi
+               ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
                if git rev-parse --verify -q "$sha1" >/dev/null
@@ -591,22 +620,30 @@ do_rest () {
 # skip picking commits whose parents are unchanged
 skip_unnecessary_picks () {
        fd=3
-       while read -r command sha1 rest
+       while read -r command rest
        do
                # fd=3 means we skip the command
-               case "$fd,$command,$(git rev-parse --verify --quiet $sha1^)" in
-               3,pick,"$ONTO"*|3,p,"$ONTO"*)
+               case "$fd,$command" in
+               3,pick|3,p)
                        # pick a commit whose parent is current $ONTO -> skip
-                       ONTO=$sha1
+                       sha1=$(printf '%s' "$rest" | cut -d ' ' -f 1)
+                       case "$(git rev-parse --verify --quiet "$sha1"^)" in
+                       "$ONTO"*)
+                               ONTO=$sha1
+                               ;;
+                       *)
+                               fd=1
+                               ;;
+                       esac
                        ;;
-               3,#*|3,,*)
+               3,#*|3,)
                        # copy comments
                        ;;
                *)
                        fd=1
                        ;;
                esac
-               echo "$command${sha1:+ }$sha1${rest:+ }$rest" >&$fd
+               printf '%s\n' "$command${rest:+ }$rest" >&$fd
        done <"$TODO" >"$TODO.new" 3>>"$DONE" &&
        mv -f "$TODO".new "$TODO" &&
        case "$(peek_next_command)" in
@@ -649,12 +686,12 @@ rearrange_squash () {
                case " $used" in
                *" $sha1 "*) continue ;;
                esac
-               echo "$pick $sha1 $message"
+               printf '%s\n' "$pick $sha1 $message"
                while read -r squash action msg
                do
                        case "$message" in
                        "$msg"*)
-                               echo "$action $squash $action! $msg"
+                               printf '%s\n' "$action $squash $action! $msg"
                                used="$used$squash "
                                ;;
                        esac
@@ -795,6 +832,9 @@ first and then run 'git rebase --continue' again."
        --autosquash)
                AUTOSQUASH=t
                ;;
+       --no-autosquash)
+               AUTOSQUASH=
+               ;;
        --onto)
                shift
                ONTO=$(parse_onto "$1") ||
@@ -895,7 +935,7 @@ first and then run 'git rebase --continue' again."
                do
                        if test t != "$PRESERVE_MERGES"
                        then
-                               echo "pick $shortsha1 $rest" >> "$TODO"
+                               printf '%s\n' "pick $shortsha1 $rest" >> "$TODO"
                        else
                                sha1=$(git rev-parse $shortsha1)
                                if test -z "$REBASE_ROOT"
@@ -914,7 +954,7 @@ first and then run 'git rebase --continue' again."
                                if test f = "$preserve"
                                then
                                        touch "$REWRITTEN"/$sha1
-                                       echo "pick $shortsha1 $rest" >> "$TODO"
+                                       printf '%s\n' "pick $shortsha1 $rest" >> "$TODO"
                                fi
                        fi
                done
@@ -957,6 +997,7 @@ first and then run 'git rebase --continue' again."
 #  e, edit = use commit, but stop for amending
 #  s, squash = use commit, but meld into previous commit
 #  f, fixup = like "squash", but discard this commit's log message
+#  x <cmd>, exec <cmd> = Run a shell command <cmd>, and stop if it fails
 #
 # If you remove a line here THAT COMMIT WILL BE LOST.
 # However, if you remove everything, the rebase will be aborted.
index ab4afa7dee377707484e7f76c7d44ac955f09397..7508463b30e7d1659ac9423a19d195e494600bd3 100755 (executable)
@@ -44,6 +44,7 @@ To restore the original branch and stop rebasing run \"git rebase --abort\".
 "
 unset newbase
 strategy=recursive
+strategy_opts=
 do_merge=
 dotest="$GIT_DIR"/rebase-merge
 prec=4
@@ -112,7 +113,7 @@ call_merge () {
        then
                export GIT_MERGE_VERBOSITY=1
        fi
-       git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
+       eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
        rv=$?
        case "$rv" in
        0)
@@ -208,6 +209,7 @@ do
                test -d "$dotest" -o -d "$GIT_DIR"/rebase-apply ||
                        die "No rebase in progress?"
 
+               git update-index --ignore-submodules --refresh &&
                git diff-files --quiet --ignore-submodules || {
                        echo "You must edit all merge conflicts and then"
                        echo "mark them as resolved using git add"
@@ -293,6 +295,27 @@ do
        -M|-m|--m|--me|--mer|--merg|--merge)
                do_merge=t
                ;;
+       -X*|--strategy-option*)
+               case "$#,$1" in
+               1,-X|1,--strategy-option)
+                       usage ;;
+               *,-X|*,--strategy-option)
+                       newopt="$2"
+                       shift ;;
+               *,--strategy-option=*)
+                       newopt="$(expr " $1" : ' --strategy-option=\(.*\)')" ;;
+               *,-X*)
+                       newopt="$(expr " $1" : ' -X\(.*\)')" ;;
+               1,*)
+                       usage ;;
+               esac
+               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")"
+               do_merge=t
+               if test -n "$strategy"
+               then
+                       strategy=recursive
+               fi
+               ;;
        -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
                --strateg=*|--strategy=*|\
        -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
@@ -345,7 +368,7 @@ do
        --root)
                rebase_root=t
                ;;
-       -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)
+       -f|--f|--fo|--for|--forc|--force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)
                force_rebase=t
                ;;
        --rerere-autoupdate|--no-rerere-autoupdate)
@@ -543,7 +566,7 @@ fi
 if test -z "$do_merge"
 then
        git format-patch -k --stdout --full-index --ignore-if-in-upstream \
-               $root_flag "$revisions" |
+               --no-renames $root_flag "$revisions" |
        git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
        move_to_original_branch
        ret=$?
index 170186f4946859e04d6a0b4ce248dd17bc877b31..9ebbab798d21147fc019b6183cad0c462c9dafca 100755 (executable)
@@ -839,10 +839,11 @@ cmd_sync()
                if test -e "$path"/.git
                then
                (
+                       say "Synchronizing submodule url for '$name'"
+                       git config submodule."$name".url "$url"
                        clear_local_git_env
                        cd "$path"
                        remote=$(get_default_remote)
-                       say "Synchronizing submodule url for '$name'"
                        git config remote."$remote".url "$url"
                )
                fi
index c4163584a93ba594141aa8af99948fc8929605d7..9b046b693fe82c992b3844bf6e8f79214dfa7a59 100755 (executable)
@@ -494,6 +494,7 @@ sub cmd_set_tree {
 
 sub cmd_dcommit {
        my $head = shift;
+       command_noisy(qw/update-index --refresh/);
        git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
                'Cannot dcommit with a dirty index.  Commit your changes first, '
                . "or stash them with `git stash'.\n";
@@ -1819,6 +1820,7 @@ sub read_all_remotes {
                        die("svn-remote.$remote: remote ref '$remote_ref' "
                            . "must start with 'refs/'\n")
                                unless $remote_ref =~ m{^refs/};
+                       $local_ref = uri_decode($local_ref);
                        $r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
                        $r->{$remote}->{svm} = {} if $use_svm_props;
                } elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
@@ -1831,6 +1833,7 @@ sub read_all_remotes {
                        die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
                            . "must start with 'refs/'\n")
                                unless $remote_ref =~ m{^refs/};
+                       $local_ref = uri_decode($local_ref);
                        my $rs = {
                            t => $t,
                            remote => $remote,
@@ -2956,18 +2959,29 @@ sub other_gs {
        my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
        unless ($gs) {
                my $ref_id = $old_ref_id;
-               $ref_id =~ s/\@\d+$//;
+               $ref_id =~ s/\@\d+-*$//;
                $ref_id .= "\@$r";
                # just grow a tail if we're not unique enough :x
                $ref_id .= '-' while find_ref($ref_id);
-               print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
                my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
                if ($u =~ s#^\Q$url\E(/|$)##) {
                        $p = $u;
                        $u = $url;
                        $repo_id = $self->{repo_id};
                }
-               $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+               while (1) {
+                       # It is possible to tag two different subdirectories at
+                       # the same revision.  If the url for an existing ref
+                       # does not match, we must either find a ref with a
+                       # matching url or create a new ref by growing a tail.
+                       $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+                       my (undef, $max_commit) = $gs->rev_map_max(1);
+                       last if (!$max_commit);
+                       my ($url) = ::cmt_metadata($max_commit);
+                       last if ($url eq $gs->full_url);
+                       $ref_id .= '-';
+               }
+               print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
        }
        $gs
 }
@@ -4050,6 +4064,7 @@ sub new {
        $self->{absent_dir} = {};
        $self->{absent_file} = {};
        $self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
+       $self->{pathnameencoding} = Git::config('svn.pathnameencoding');
        $self;
 }
 
@@ -4133,6 +4148,10 @@ sub open_directory {
 
 sub git_path {
        my ($self, $path) = @_;
+       if (my $enc = $self->{pathnameencoding}) {
+               require Encode;
+               Encode::from_to($path, 'UTF-8', $enc);
+       }
        if ($self->{path_strip}) {
                $path =~ s!$self->{path_strip}!! or
                  die "Failed to strip path '$path' ($self->{path_strip})\n";
@@ -4521,6 +4540,10 @@ sub split_path {
 
 sub repo_path {
        my ($self, $path) = @_;
+       if (my $enc = $self->{pathnameencoding}) {
+               require Encode;
+               Encode::from_to($path, $enc, 'UTF-8');
+       }
        $self->{path_prefix}.(defined $path ? $path : '');
 }
 
index dbded76aaf5499dd116e5ad22f95d4518b79c0d8..3fc4166b25714911b6b1294c7439da1e237e4918 100755 (executable)
@@ -31,7 +31,7 @@ valid_custom_tool()
 
 valid_tool() {
        case "$1" in
-               firefox | iceweasel | chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
+               firefox | iceweasel | chrome | google-chrome | chromium | konqueror | w3m | links | lynx | dillo | open | start)
                        ;; # happy
                *)
                        valid_custom_tool "$1" || return 1
@@ -103,7 +103,7 @@ fi
 
 if test -z "$browser" ; then
     if test -n "$DISPLAY"; then
-       browser_candidates="firefox iceweasel chrome chromium konqueror w3m links lynx dillo"
+       browser_candidates="firefox iceweasel google-chrome chrome chromium konqueror w3m links lynx dillo"
        if test "$KDE_FULL_SESSION" = "true"; then
            browser_candidates="konqueror $browser_candidates"
        fi
@@ -146,7 +146,7 @@ case "$browser" in
        test "$vers" -lt 2 && NEWTAB=''
        "$browser_path" $NEWTAB "$@" &
        ;;
-    chrome|chromium)
+    google-chrome|chrome|chromium)
        # Actual command for chromium is chromium-browser.
        # No need to specify newTab. It's default in chromium
        eval "$browser_path" "$@" &
diff --git a/git.c b/git.c
index 17538117a5ccfd48129a873697d8674e86f512f5..8de48107e0dced316704c1ad78569ccb9eeebb08 100644 (file)
--- a/git.c
+++ b/git.c
@@ -8,12 +8,13 @@ const char git_usage_string[] =
        "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
        "           [-p|--paginate|--no-pager] [--no-replace-objects]\n"
        "           [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
-       "           [-c name=value\n"
-       "           [--help] COMMAND [ARGS]";
+       "           [-c name=value] [--help]\n"
+       "           COMMAND [ARGS]";
 
 const char git_more_info_string[] =
        "See 'git help COMMAND' for more information on a specific command.";
 
+static struct startup_info git_startup_info;
 static int use_pager = -1;
 struct pager_config {
        const char *cmd;
@@ -188,7 +189,8 @@ static int handle_alias(int *argcp, const char ***argv)
                }
                count = split_cmdline(alias_string, &new_argv);
                if (count < 0)
-                       die("Bad alias.%s string", alias_command);
+                       die("Bad alias.%s string: %s", alias_command,
+                           split_cmdline_strerror(count));
                option_count = handle_options(&new_argv, &count, &envchanged);
                if (envchanged)
                        die("alias '%s' changes environment variables\n"
@@ -229,13 +231,14 @@ static int handle_alias(int *argcp, const char ***argv)
 
 const char git_version_string[] = GIT_VERSION;
 
-#define RUN_SETUP      (1<<0)
-#define USE_PAGER      (1<<1)
+#define RUN_SETUP              (1<<0)
+#define RUN_SETUP_GENTLY       (1<<1)
+#define USE_PAGER              (1<<2)
 /*
  * require working tree to be present -- anything uses this needs
  * RUN_SETUP for reading from the configuration file.
  */
-#define NEED_WORK_TREE (1<<2)
+#define NEED_WORK_TREE         (1<<3)
 
 struct cmd_struct {
        const char *cmd;
@@ -254,8 +257,12 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        if (!help) {
                if (p->option & RUN_SETUP)
                        prefix = setup_git_directory();
+               if (p->option & RUN_SETUP_GENTLY) {
+                       int nongit_ok;
+                       prefix = setup_git_directory_gently(&nongit_ok);
+               }
 
-               if (use_pager == -1 && p->option & RUN_SETUP)
+               if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY))
                        use_pager = check_pager_config(p->cmd);
                if (use_pager == -1 && p->option & USE_PAGER)
                        use_pager = 1;
@@ -295,12 +302,12 @@ static void handle_internal_command(int argc, const char **argv)
                { "add", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
                { "annotate", cmd_annotate, RUN_SETUP },
-               { "apply", cmd_apply },
+               { "apply", cmd_apply, RUN_SETUP_GENTLY },
                { "archive", cmd_archive },
                { "bisect--helper", cmd_bisect__helper, RUN_SETUP | NEED_WORK_TREE },
                { "blame", cmd_blame, RUN_SETUP },
                { "branch", cmd_branch, RUN_SETUP },
-               { "bundle", cmd_bundle },
+               { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
                { "cat-file", cmd_cat_file, RUN_SETUP },
                { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
                { "checkout-index", cmd_checkout_index,
@@ -313,7 +320,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "clean", cmd_clean, RUN_SETUP | NEED_WORK_TREE },
                { "commit", cmd_commit, RUN_SETUP | NEED_WORK_TREE },
                { "commit-tree", cmd_commit_tree, RUN_SETUP },
-               { "config", cmd_config },
+               { "config", cmd_config, RUN_SETUP_GENTLY },
                { "count-objects", cmd_count_objects, RUN_SETUP },
                { "describe", cmd_describe, RUN_SETUP },
                { "diff", cmd_diff },
@@ -330,21 +337,21 @@ static void handle_internal_command(int argc, const char **argv)
                { "fsck-objects", cmd_fsck, RUN_SETUP },
                { "gc", cmd_gc, RUN_SETUP },
                { "get-tar-commit-id", cmd_get_tar_commit_id },
-               { "grep", cmd_grep },
+               { "grep", cmd_grep, RUN_SETUP_GENTLY },
                { "hash-object", cmd_hash_object },
                { "help", cmd_help },
-               { "index-pack", cmd_index_pack },
+               { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
                { "init", cmd_init_db },
                { "init-db", cmd_init_db },
-               { "log", cmd_log, RUN_SETUP | USE_PAGER },
+               { "log", cmd_log, RUN_SETUP },
                { "ls-files", cmd_ls_files, RUN_SETUP },
                { "ls-tree", cmd_ls_tree, RUN_SETUP },
-               { "ls-remote", cmd_ls_remote },
+               { "ls-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
                { "mailinfo", cmd_mailinfo },
                { "mailsplit", cmd_mailsplit },
                { "merge", cmd_merge, RUN_SETUP | NEED_WORK_TREE },
                { "merge-base", cmd_merge_base, RUN_SETUP },
-               { "merge-file", cmd_merge_file },
+               { "merge-file", cmd_merge_file, RUN_SETUP_GENTLY },
                { "merge-index", cmd_merge_index, RUN_SETUP },
                { "merge-ours", cmd_merge_ours, RUN_SETUP },
                { "merge-recursive", cmd_merge_recursive, RUN_SETUP | NEED_WORK_TREE },
@@ -360,7 +367,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "pack-objects", cmd_pack_objects, RUN_SETUP },
                { "pack-redundant", cmd_pack_redundant, RUN_SETUP },
                { "patch-id", cmd_patch_id },
-               { "peek-remote", cmd_ls_remote },
+               { "peek-remote", cmd_ls_remote, RUN_SETUP_GENTLY },
                { "pickaxe", cmd_blame, RUN_SETUP },
                { "prune", cmd_prune, RUN_SETUP },
                { "prune-packed", cmd_prune_packed, RUN_SETUP },
@@ -370,7 +377,7 @@ static void handle_internal_command(int argc, const char **argv)
                { "reflog", cmd_reflog, RUN_SETUP },
                { "remote", cmd_remote, RUN_SETUP },
                { "replace", cmd_replace, RUN_SETUP },
-               { "repo-config", cmd_config },
+               { "repo-config", cmd_config, RUN_SETUP_GENTLY },
                { "rerere", cmd_rerere, RUN_SETUP },
                { "reset", cmd_reset, RUN_SETUP },
                { "rev-list", cmd_rev_list, RUN_SETUP },
@@ -378,9 +385,9 @@ static void handle_internal_command(int argc, const char **argv)
                { "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
                { "rm", cmd_rm, RUN_SETUP },
                { "send-pack", cmd_send_pack, RUN_SETUP },
-               { "shortlog", cmd_shortlog, USE_PAGER },
+               { "shortlog", cmd_shortlog, RUN_SETUP_GENTLY | USE_PAGER },
                { "show-branch", cmd_show_branch, RUN_SETUP },
-               { "show", cmd_show, RUN_SETUP | USE_PAGER },
+               { "show", cmd_show, RUN_SETUP },
                { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
                { "stripspace", cmd_stripspace },
                { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
@@ -392,10 +399,10 @@ static void handle_internal_command(int argc, const char **argv)
                { "update-ref", cmd_update_ref, RUN_SETUP },
                { "update-server-info", cmd_update_server_info, RUN_SETUP },
                { "upload-archive", cmd_upload_archive },
-               { "var", cmd_var },
+               { "var", cmd_var, RUN_SETUP_GENTLY },
                { "verify-tag", cmd_verify_tag, RUN_SETUP },
                { "version", cmd_version },
-               { "whatchanged", cmd_whatchanged, RUN_SETUP | USE_PAGER },
+               { "whatchanged", cmd_whatchanged, RUN_SETUP },
                { "write-tree", cmd_write_tree, RUN_SETUP },
                { "verify-pack", cmd_verify_pack },
                { "show-ref", cmd_show_ref, RUN_SETUP },
@@ -489,6 +496,8 @@ int main(int argc, const char **argv)
 {
        const char *cmd;
 
+       startup_info = &git_startup_info;
+
        cmd = git_extract_argv0_path(argv[0]);
        if (!cmd)
                cmd = "git-help";
index 0e19be8d216aa813221c8663d6424f96f80bfb14..d4811987965e5a5036b7ed71e32d93578579073a 100644 (file)
@@ -95,7 +95,7 @@ You can specify the following configuration variables when building GIT:
    in the browser's URL bar and next to site name in bookmarks).  Relative
    to base URI of gitweb.  [Default: static/git-favicon.png]
  * GITWEB_JS
-   Points to the localtion where you put gitweb.js on your web server
+   Points to the location where you put gitweb.js on your web server
    (or to be more generic URI of JavaScript code used by gitweb).
    Relative to base URI of gitweb.  [Default: static/gitweb.js (or
    static/gitweb.min.js if JSMIN build variable is defined / JavaScript
@@ -233,7 +233,7 @@ not include variables usually directly set during build):
    is false.
  * $maxload
    Used to set the maximum load that we will still respond to gitweb queries.
-   If server load exceed this value then return "503 Service Unavaliable" error.
+   If server load exceed this value then return "503 Service Unavailable" error.
    Server load is taken to be 0 if gitweb cannot determine its value.  Set it to
    undefined value to turn it off.  The default is 300.
 
index cedc3573136922d424971ef2a16feeb82496c37d..a85e2f63197649bff4842ea2970448205618c104 100755 (executable)
@@ -232,6 +232,29 @@ our %avatar_size = (
 # Leave it undefined (or set to 'undef') to turn off load checking.
 our $maxload = 300;
 
+# configuration for 'highlight' (http://www.andre-simon.de/)
+# match by basename
+our %highlight_basename = (
+       #'Program' => 'py',
+       #'Library' => 'py',
+       'SConstruct' => 'py', # SCons equivalent of Makefile
+       'Makefile' => 'make',
+);
+# match by extension
+our %highlight_ext = (
+       # main extensions, defining name of syntax;
+       # see files in /usr/share/highlight/langDefs/ directory
+       map { $_ => $_ }
+               qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+       # alternate extensions, see /etc/highlight/filetypes.conf
+       'h' => 'c',
+       map { $_ => 'cpp' } qw(cxx c++ cc),
+       map { $_ => 'php' } qw(php3 php4),
+       map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
+       'mak' => 'make',
+       map { $_ => 'xml' } qw(xhtml html htm),
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -245,7 +268,7 @@ our %feature = (
        # return value of feature-sub indicates if to enable specified feature
        #
        # if there is no 'sub' key (no feature-sub), then feature cannot be
-       # overriden
+       # overridden
        #
        # use gitweb_get_feature(<feature>) to retrieve the <feature> value
        # (an array) or gitweb_check_feature(<feature>) to check if <feature>
@@ -1037,8 +1060,12 @@ sub run_request {
        reset_timer();
 
        evaluate_uri();
+       evaluate_gitweb_config();
        check_loadavg();
 
+       # $projectroot and $projects_list might be set in gitweb config file
+       $projects_list ||= $projectroot;
+
        evaluate_query_params();
        evaluate_path_info();
        evaluate_and_validate_params();
@@ -1086,12 +1113,8 @@ sub evaluate_argv {
 
 sub run {
        evaluate_argv();
-       evaluate_gitweb_config();
        evaluate_git_version();
 
-       # $projectroot and $projects_list might be set in gitweb config file
-       $projects_list ||= $projectroot;
-
        $pre_listen_hook->()
                if $pre_listen_hook;
 
@@ -1102,7 +1125,7 @@ sub run {
 
                run_request();
 
-               $pre_dispatch_hook->()
+               $post_dispatch_hook->()
                        if $post_dispatch_hook;
 
                last REQUEST if ($is_last_request->());
@@ -1323,7 +1346,7 @@ sub esc_param {
        return $str;
 }
 
-# quote unsafe chars in whole URL, so some charactrs cannot be quoted
+# quote unsafe chars in whole URL, so some characters cannot be quoted
 sub esc_url {
        my $str = shift;
        return undef unless defined $str;
@@ -3316,30 +3339,6 @@ sub blob_contenttype {
 sub guess_file_syntax {
        my ($highlight, $mimetype, $file_name) = @_;
        return undef unless ($highlight && defined $file_name);
-
-       # configuration for 'highlight' (http://www.andre-simon.de/)
-       # match by basename
-       my %highlight_basename = (
-               #'Program' => 'py',
-               #'Library' => 'py',
-               'SConstruct' => 'py', # SCons equivalent of Makefile
-               'Makefile' => 'make',
-       );
-       # match by extension
-       my %highlight_ext = (
-               # main extensions, defining name of syntax;
-               # see files in /usr/share/highlight/langDefs/ directory
-               map { $_ => $_ }
-                       qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
-               # alternate extensions, see /etc/highlight/filetypes.conf
-               'h' => 'c',
-               map { $_ => 'cpp' } qw(cxx c++ cc),
-               map { $_ => 'php' } qw(php3 php4),
-               map { $_ => 'pl'  } qw(perl pm), # perhaps also 'cgi'
-               'mak' => 'make',
-               map { $_ => 'xml' } qw(xhtml html htm),
-       );
-
        my $basename = basename($file_name, '.in');
        return $highlight_basename{$basename}
                if exists $highlight_basename{$basename};
@@ -3782,9 +3781,9 @@ sub git_print_authorship {
 }
 
 # Outputs table rows containing the full author or committer information,
-# in the format expected for 'commit' view (& similia).
+# in the format expected for 'commit' view (& similar).
 # Parameters are a commit hash reference, followed by the list of people
-# to output information for. If the list is empty it defalts to both
+# to output information for. If the list is empty it defaults to both
 # author and committer.
 sub git_print_authorship_rows {
        my $co = shift;
@@ -4513,8 +4512,8 @@ sub git_patchset_body {
                print "</div>\n"; # class="patch"
        }
 
-       # for compact combined (--cc) format, with chunk and patch simpliciaction
-       # patchset might be empty, but there might be unprocessed raw lines
+       # for compact combined (--cc) format, with chunk and patch simplification
+       # the patchset might be empty, but there might be unprocessed raw lines
        for (++$patch_idx if $patch_number > 0;
             $patch_idx < @$difftree;
             ++$patch_idx) {
@@ -5192,15 +5191,15 @@ sub git_summary {
 }
 
 sub git_tag {
-       my $head = git_get_head_hash($project);
-       git_header_html();
-       git_print_page_nav('','', $head,undef,$head);
        my %tag = parse_tag($hash);
 
        if (! %tag) {
                die_error(404, "Unknown tag object");
        }
 
+       my $head = git_get_head_hash($project);
+       git_header_html();
+       git_print_page_nav('','', $head,undef,$head);
        git_print_header_div('commit', esc_html($tag{'name'}), $hash);
        print "<div class=\"title_text\">\n" .
              "<table class=\"object_header\">\n" .
@@ -6522,12 +6521,13 @@ sub git_search {
                        $paging_nav .= " &sdot; next";
                }
 
-               if ($#commitlist >= 100) {
-               }
-
                git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
                git_print_header_div('commit', esc_html($co{'title'}), $hash);
-               git_search_grep_body(\@commitlist, 0, 99, $next_link);
+               if ($page == 0 && !@commitlist) {
+                       print "<p>No match.</p>\n";
+               } else {
+                       git_search_grep_body(\@commitlist, 0, 99, $next_link);
+               }
        }
 
        if ($searchtype eq 'pickaxe') {
diff --git a/graph.c b/graph.c
index ac7c60540645761d3c99fb405a69cb2d06aaddc2..f1a63c2241a30cce0141b87b5e8005b1a899a8ef 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -7,17 +7,6 @@
 
 /* Internal API */
 
-/*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf.  It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
 /*
  * Output a padding line in the graph.
  * This is similar to graph_next_line().  However, it is guaranteed to
@@ -73,7 +62,7 @@ enum graph_state {
 /*
  * The list of available column colors.
  */
-static char column_colors[][COLOR_MAXLEN] = {
+static const char *column_colors_ansi[] = {
        GIT_COLOR_RED,
        GIT_COLOR_GREEN,
        GIT_COLOR_YELLOW,
@@ -86,23 +75,33 @@ static char column_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_BOLD_BLUE,
        GIT_COLOR_BOLD_MAGENTA,
        GIT_COLOR_BOLD_CYAN,
+       GIT_COLOR_RESET,
 };
 
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
+#define COLUMN_COLORS_ANSI_MAX (ARRAY_SIZE(column_colors_ansi) - 1)
+
+static const char **column_colors;
+static unsigned short column_colors_max;
 
-static const char *column_get_color_code(const struct column *c)
+void graph_set_column_colors(const char **colors, unsigned short colors_max)
 {
-       return column_colors[c->color];
+       column_colors = colors;
+       column_colors_max = colors_max;
+}
+
+static const char *column_get_color_code(unsigned short color)
+{
+       return column_colors[color];
 }
 
 static void strbuf_write_column(struct strbuf *sb, const struct column *c,
                                char col_char)
 {
-       if (c->color < COLUMN_COLORS_MAX)
-               strbuf_addstr(sb, column_get_color_code(c));
+       if (c->color < column_colors_max)
+               strbuf_addstr(sb, column_get_color_code(c->color));
        strbuf_addch(sb, col_char);
-       if (c->color < COLUMN_COLORS_MAX)
-               strbuf_addstr(sb, GIT_COLOR_RESET);
+       if (c->color < column_colors_max)
+               strbuf_addstr(sb, column_get_color_code(column_colors_max));
 }
 
 struct git_graph {
@@ -226,6 +225,11 @@ static struct strbuf *diff_output_prefix_callback(struct diff_options *opt, void
 struct git_graph *graph_init(struct rev_info *opt)
 {
        struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+
+       if (!column_colors)
+               graph_set_column_colors(column_colors_ansi,
+                                       COLUMN_COLORS_ANSI_MAX);
+
        graph->commit = NULL;
        graph->revs = opt;
        graph->num_parents = 0;
@@ -242,7 +246,7 @@ struct git_graph *graph_init(struct rev_info *opt)
         * always increment it for the first commit we output.
         * This way we start at 0 for the first commit.
         */
-       graph->default_column_color = COLUMN_COLORS_MAX - 1;
+       graph->default_column_color = column_colors_max - 1;
 
        /*
         * Allocate a reasonably large default number of columns
@@ -365,7 +369,7 @@ static struct commit_list *first_interesting_parent(struct git_graph *graph)
 static unsigned short graph_get_current_column_color(const struct git_graph *graph)
 {
        if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
-               return COLUMN_COLORS_MAX;
+               return column_colors_max;
        return graph->default_column_color;
 }
 
@@ -375,7 +379,7 @@ static unsigned short graph_get_current_column_color(const struct git_graph *gra
 static void graph_increment_column_color(struct git_graph *graph)
 {
        graph->default_column_color = (graph->default_column_color + 1) %
-               COLUMN_COLORS_MAX;
+               column_colors_max;
 }
 
 static unsigned short graph_find_commit_color(const struct git_graph *graph,
@@ -439,7 +443,7 @@ static void graph_update_width(struct git_graph *graph,
                max_cols++;
 
        /*
-        * We added a column for the the current commit as part of
+        * We added a column for the current commit as part of
         * graph->num_parents.  If the current commit was already in
         * graph->columns, then we have double counted it.
         */
@@ -1143,7 +1147,7 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct strbuf
                graph_update_state(graph, GRAPH_PADDING);
 }
 
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
 {
        switch (graph->state) {
        case GRAPH_PADDING:
diff --git a/graph.h b/graph.h
index b82ae87a491454aa119476ac9609ad176e8c9406..aff960c7e8f63f49e3ec16afb0b891cf257ce999 100644 (file)
--- a/graph.h
+++ b/graph.h
@@ -4,6 +4,23 @@
 /* A graph is a pointer to this opaque structure */
 struct git_graph;
 
+/*
+ * Set up a custom scheme for column colors.
+ *
+ * The default column color scheme inserts ANSI color escapes to colorize
+ * the graph. The various color escapes are stored in an array of strings
+ * where each entry corresponds to a color, except for the last entry,
+ * which denotes the escape for resetting the color back to the default.
+ * When generating the graph, strings from this array are inserted before
+ * and after the various column characters.
+ *
+ * This function allows you to enable a custom array of color escapes.
+ * The 'colors_max' argument is the index of the last "reset" entry.
+ *
+ * This functions must be called BEFORE graph_init() is called.
+ */
+void graph_set_column_colors(const char **colors, unsigned short colors_max);
+
 /*
  * Create a new struct git_graph.
  */
@@ -32,6 +49,17 @@ void graph_update(struct git_graph *graph, struct commit *commit);
  */
 int graph_is_commit_finished(struct git_graph const *graph);
 
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf.  It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
 
 /*
  * graph_show_*: helper functions for printing to stdout
diff --git a/http.c b/http.c
index 1320c50e32eb7b8715b263bc2af089c3dbce39fa..0a5011f615bdfade0b69682b177dc97471ac20a3 100644 (file)
--- a/http.c
+++ b/http.c
@@ -41,6 +41,7 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static char *user_name, *user_pass;
+static const char *user_agent;
 
 #if LIBCURL_VERSION_NUM >= 0x071700
 /* Use CURLOPT_KEYPASSWD as is */
@@ -196,6 +197,9 @@ static int http_options(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp("http.useragent", var))
+               return git_config_string(&user_agent, var, value);
+
        /* Fall back on the default ones */
        return git_default_config(var, value, cb);
 }
@@ -279,7 +283,8 @@ static CURL *get_curl_handle(void)
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
 
-       curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
+       curl_easy_setopt(result, CURLOPT_USERAGENT,
+               user_agent ? user_agent : GIT_HTTP_USER_AGENT);
 
        if (curl_ftp_no_epsv)
                curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
@@ -380,6 +385,8 @@ void http_init(struct remote *remote)
 #endif
        set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
 
+       set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
+
        low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
        if (low_speed_limit != NULL)
                curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
diff --git a/http.h b/http.h
index a0b59015948c3b9736dd6c09fd9137f7ecd4f59a..173f74c8298c3fa3989f88b06e2085127fe96db8 100644 (file)
--- a/http.h
+++ b/http.h
 #endif
 
 #if LIBCURL_VERSION_NUM < 0x070704
-#define curl_global_cleanup() do { /* nothing */ } while(0)
+#define curl_global_cleanup() do { /* nothing */ } while (0)
 #endif
 #if LIBCURL_VERSION_NUM < 0x070800
-#define curl_global_init(a) do { /* nothing */ } while(0)
+#define curl_global_init(a) do { /* nothing */ } while (0)
 #endif
 
 #if (LIBCURL_VERSION_NUM < 0x070c04) || (LIBCURL_VERSION_NUM == 0x071000)
index 1a577a0a094342315bd3f68e8d13ef7f57a22f50..71506a8dd3ed07fe44c487a644ce9a42b94a7578 100644 (file)
@@ -543,9 +543,13 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
        while (imap->literal_pending)
                get_cmd_result(ctx, NULL);
 
-       bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
-                          "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-                          cmd->tag, cmd->cmd, cmd->cb.dlen);
+       if (!cmd->cb.data)
+               bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
+       else
+               bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
+                                 cmd->tag, cmd->cmd, cmd->cb.dlen,
+                                 CAP(LITERALPLUS) ? "+" : "");
+
        if (Verbose) {
                if (imap->num_in_progress)
                        printf("(%d in progress) ", imap->num_in_progress);
@@ -1086,7 +1090,7 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
                int gai;
                char portstr[6];
 
-               snprintf(portstr, sizeof(portstr), "%hu", srvc->port);
+               snprintf(portstr, sizeof(portstr), "%d", srvc->port);
 
                memset(&hints, 0, sizeof(hints));
                hints.ai_socktype = SOCK_STREAM;
index 3764a1ab72354a3ce643c899c1e8cf5443d4e7cb..6bb3095c3a85d771bb60e8f2f025287d16e792df 100644 (file)
@@ -46,7 +46,7 @@ static int ll_binary_merge(const struct ll_merge_driver *drv_unused,
         * or common ancestor for an internal merge.  Still return
         * "conflicted merge" status.
         */
-       mmfile_t *stolen = (flag & 01) ? orig : src1;
+       mmfile_t *stolen = (flag & LL_OPT_VIRTUAL_ANCESTOR) ? orig : src1;
 
        result->ptr = stolen->ptr;
        result->size = stolen->size;
@@ -79,7 +79,7 @@ static int ll_xdl_merge(const struct ll_merge_driver *drv_unused,
 
        memset(&xmp, 0, sizeof(xmp));
        xmp.level = XDL_MERGE_ZEALOUS;
-       xmp.favor= (flag >> 1) & 03;
+       xmp.favor = ll_opt_favor(flag);
        if (git_xmerge_style >= 0)
                xmp.style = git_xmerge_style;
        if (marker_size > 0)
@@ -99,7 +99,8 @@ static int ll_union_merge(const struct ll_merge_driver *drv_unused,
                          int flag, int marker_size)
 {
        /* Use union favor */
-       flag = (flag & 1) | (XDL_MERGE_FAVOR_UNION << 1);
+       flag &= ~LL_OPT_FAVOR_MASK;
+       flag |= create_ll_flag(XDL_MERGE_FAVOR_UNION);
        return ll_xdl_merge(drv_unused, result, path_unused,
                            orig, NULL, src1, NULL, src2, NULL,
                            flag, marker_size);
@@ -321,6 +322,16 @@ static int git_path_check_merge(const char *path, struct git_attr_check check[2]
        return git_checkattr(path, 2, check);
 }
 
+static void normalize_file(mmfile_t *mm, const char *path)
+{
+       struct strbuf strbuf = STRBUF_INIT;
+       if (renormalize_buffer(path, mm->ptr, mm->size, &strbuf)) {
+               free(mm->ptr);
+               mm->size = strbuf.len;
+               mm->ptr = strbuf_detach(&strbuf, NULL);
+       }
+}
+
 int ll_merge(mmbuffer_t *result_buf,
             const char *path,
             mmfile_t *ancestor, const char *ancestor_label,
@@ -332,8 +343,13 @@ int ll_merge(mmbuffer_t *result_buf,
        const char *ll_driver_name = NULL;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        const struct ll_merge_driver *driver;
-       int virtual_ancestor = flag & 01;
+       int virtual_ancestor = flag & LL_OPT_VIRTUAL_ANCESTOR;
 
+       if (flag & LL_OPT_RENORMALIZE) {
+               normalize_file(ancestor, path);
+               normalize_file(ours, path);
+               normalize_file(theirs, path);
+       }
        if (!git_path_check_merge(path, check)) {
                ll_driver_name = check[0].value;
                if (check[1].value) {
index 57754cc8ca7b378a86b168a4fd6299fa3dfba045..ff7ca87bfa01d0e9705f4bfd83b9e4d2c155d588 100644 (file)
@@ -5,6 +5,21 @@
 #ifndef LL_MERGE_H
 #define LL_MERGE_H
 
+#define LL_OPT_VIRTUAL_ANCESTOR        (1 << 0)
+#define LL_OPT_FAVOR_MASK      ((1 << 1) | (1 << 2))
+#define LL_OPT_FAVOR_SHIFT 1
+#define LL_OPT_RENORMALIZE     (1 << 3)
+
+static inline int ll_opt_favor(int flag)
+{
+       return (flag & LL_OPT_FAVOR_MASK) >> LL_OPT_FAVOR_SHIFT;
+}
+
+static inline int create_ll_flag(int favor)
+{
+       return ((favor << LL_OPT_FAVOR_SHIFT) & LL_OPT_FAVOR_MASK);
+}
+
 int ll_merge(mmbuffer_t *result_buf,
             const char *path,
             mmfile_t *ancestor, const char *ancestor_label,
index fb6aa4a551802de07be76bd838b6f22a236457ff..aadd48c4fc73d693979956735e601d969594107e 100644 (file)
@@ -20,6 +20,7 @@
 #include "attr.h"
 #include "merge-recursive.h"
 #include "dir.h"
+#include "submodule.h"
 
 static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
@@ -136,16 +137,10 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
                if (parse_commit(commit) != 0)
                        printf("(bad commit)\n");
                else {
-                       const char *s;
-                       int len;
-                       for (s = commit->buffer; *s; s++)
-                               if (*s == '\n' && s[1] == '\n') {
-                                       s += 2;
-                                       break;
-                               }
-                       for (len = 0; s[len] && '\n' != s[len]; len++)
-                               ; /* do nothing */
-                       printf("%.*s\n", len, s);
+                       const char *title;
+                       int len = find_commit_subject(commit->buffer, &title);
+                       if (len)
+                               printf("%.*s\n", len, title);
                }
        }
 }
@@ -185,7 +180,7 @@ static int git_merge_trees(int index_only,
        opts.fn = threeway_merge;
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
-       opts.msgs = get_porcelain_error_msgs();
+       set_porcelain_error_msgs(opts.msgs, "merge");
 
        init_tree_desc_from_tree(t+0, common);
        init_tree_desc_from_tree(t+1, head);
@@ -525,13 +520,15 @@ static void update_file_flags(struct merge_options *o,
                void *buf;
                unsigned long size;
 
-               if (S_ISGITLINK(mode))
+               if (S_ISGITLINK(mode)) {
                        /*
                         * We may later decide to recursively descend into
                         * the submodule directory and update its index
                         * and/or work tree, but we do not do that now.
                         */
+                       update_wd = 0;
                        goto update_index;
+               }
 
                buf = read_sha1_file(sha, &type, &size);
                if (!buf)
@@ -647,7 +644,9 @@ static int merge_3way(struct merge_options *o,
 
        merge_status = ll_merge(result_buf, a->path, &orig, base_name,
                                &src1, name1, &src2, name2,
-                               (!!o->call_depth) | (favor << 1));
+                               ((o->call_depth ? LL_OPT_VIRTUAL_ANCESTOR : 0) |
+                                (o->renormalize ? LL_OPT_RENORMALIZE : 0) |
+                                create_ll_flag(favor)));
 
        free(name1);
        free(name2);
@@ -716,8 +715,8 @@ static struct merge_file_info merge_file(struct merge_options *o,
                        free(result_buf.ptr);
                        result.clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
-                       result.clean = 0;
-                       hashcpy(result.sha, a->sha1);
+                       result.clean = merge_submodule(result.sha, one->path, one->sha1,
+                                                      a->sha1, b->sha1);
                } else if (S_ISLNK(a->mode)) {
                        hashcpy(result.sha, a->sha1);
 
@@ -806,7 +805,8 @@ static int process_renames(struct merge_options *o,
                           struct string_list *b_renames)
 {
        int clean_merge = 1, i, j;
-       struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+       struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
+       struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
        const struct rename *sre;
 
        for (i = 0; i < a_renames->nr; i++) {
@@ -1019,14 +1019,22 @@ static int process_renames(struct merge_options *o,
 
                                if (mfi.clean &&
                                    sha_eq(mfi.sha, ren1->pair->two->sha1) &&
-                                   mfi.mode == ren1->pair->two->mode)
+                                   mfi.mode == ren1->pair->two->mode) {
                                        /*
-                                        * This messaged is part of
+                                        * This message is part of
                                         * t6022 test. If you change
                                         * it update the test too.
                                         */
                                        output(o, 3, "Skipped %s (merged same as existing)", ren1_dst);
-                               else {
+
+                                       /* There may be higher stage entries left
+                                        * in the index (e.g. due to a D/F
+                                        * conflict) that need to be resolved.
+                                        */
+                                       if (!ren1->dst_entry->stages[2].mode !=
+                                           !ren1->dst_entry->stages[3].mode)
+                                               ren1->dst_entry->processed = 0;
+                               } else {
                                        if (mfi.merge || !mfi.clean)
                                                output(o, 1, "Renaming %s => %s", ren1_src, ren1_dst);
                                        if (mfi.merge)
@@ -1056,6 +1064,53 @@ static unsigned char *stage_sha(const unsigned char *sha, unsigned mode)
        return (is_null_sha1(sha) || mode == 0) ? NULL: (unsigned char *)sha;
 }
 
+static int read_sha1_strbuf(const unsigned char *sha1, struct strbuf *dst)
+{
+       void *buf;
+       enum object_type type;
+       unsigned long size;
+       buf = read_sha1_file(sha1, &type, &size);
+       if (!buf)
+               return error("cannot read object %s", sha1_to_hex(sha1));
+       if (type != OBJ_BLOB) {
+               free(buf);
+               return error("object %s is not a blob", sha1_to_hex(sha1));
+       }
+       strbuf_attach(dst, buf, size, size + 1);
+       return 0;
+}
+
+static int blob_unchanged(const unsigned char *o_sha,
+                         const unsigned char *a_sha,
+                         int renormalize, const char *path)
+{
+       struct strbuf o = STRBUF_INIT;
+       struct strbuf a = STRBUF_INIT;
+       int ret = 0; /* assume changed for safety */
+
+       if (sha_eq(o_sha, a_sha))
+               return 1;
+       if (!renormalize)
+               return 0;
+
+       assert(o_sha && a_sha);
+       if (read_sha1_strbuf(o_sha, &o) || read_sha1_strbuf(a_sha, &a))
+               goto error_return;
+       /*
+        * Note: binary | is used so that both renormalizations are
+        * performed.  Comparison can be skipped if both files are
+        * unchanged since their sha1s have already been compared.
+        */
+       if (renormalize_buffer(path, o.buf, o.len, &o) |
+           renormalize_buffer(path, a.buf, o.len, &a))
+               ret = (o.len == a.len && !memcmp(o.buf, a.buf, o.len));
+
+error_return:
+       strbuf_release(&o);
+       strbuf_release(&a);
+       return ret;
+}
+
 /* Per entry merge function */
 static int process_entry(struct merge_options *o,
                         const char *path, struct stage_data *entry)
@@ -1065,6 +1120,7 @@ static int process_entry(struct merge_options *o,
        print_index_entry("\tpath: ", entry);
        */
        int clean_merge = 1;
+       int normalize = o->renormalize;
        unsigned o_mode = entry->stages[1].mode;
        unsigned a_mode = entry->stages[2].mode;
        unsigned b_mode = entry->stages[3].mode;
@@ -1072,11 +1128,12 @@ static int process_entry(struct merge_options *o,
        unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
        unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
 
+       entry->processed = 1;
        if (o_sha && (!a_sha || !b_sha)) {
                /* Case A: Deleted in one */
                if ((!a_sha && !b_sha) ||
-                   (sha_eq(a_sha, o_sha) && !b_sha) ||
-                   (!a_sha && sha_eq(b_sha, o_sha))) {
+                   (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
+                   (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_sha)
@@ -1104,33 +1161,28 @@ static int process_entry(struct merge_options *o,
        } else if ((!o_sha && a_sha && !b_sha) ||
                   (!o_sha && !a_sha && b_sha)) {
                /* Case B: Added in one. */
-               const char *add_branch;
-               const char *other_branch;
                unsigned mode;
                const unsigned char *sha;
-               const char *conf;
 
                if (a_sha) {
-                       add_branch = o->branch1;
-                       other_branch = o->branch2;
                        mode = a_mode;
                        sha = a_sha;
-                       conf = "file/directory";
                } else {
-                       add_branch = o->branch2;
-                       other_branch = o->branch1;
                        mode = b_mode;
                        sha = b_sha;
-                       conf = "directory/file";
                }
                if (string_list_has_string(&o->current_directory_set, path)) {
-                       const char *new_path = unique_path(o, path, add_branch);
-                       clean_merge = 0;
-                       output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
-                              "Adding %s as %s",
-                              conf, path, other_branch, path, new_path);
-                       remove_file(o, 0, path, 0);
-                       update_file(o, 0, sha, mode, new_path);
+                       /* Handle D->F conflicts after all subfiles */
+                       entry->processed = 0;
+                       /* But get any file out of the way now, so conflicted
+                        * entries below the directory of the same name can
+                        * be put in the working directory.
+                        */
+                       if (a_sha)
+                               output(o, 2, "Removing %s", path);
+                       /* do not touch working file if it did not exist */
+                       remove_file(o, 0, path, !a_sha);
+                       return 1; /* Assume clean till processed */
                } else {
                        output(o, 2, "Adding %s", path);
                        update_file(o, 1, sha, mode, path);
@@ -1178,26 +1230,106 @@ static int process_entry(struct merge_options *o,
        return clean_merge;
 }
 
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
+/*
+ * Per entry merge function for D/F conflicts, to be called only after
+ * all files below dir have been processed.  We do this because in the
+ * cases we can cleanly resolve D/F conflicts, process_entry() can clean
+ * out all the files below the directory for us.
+ */
+static int process_df_entry(struct merge_options *o,
+                        const char *path, struct stage_data *entry)
 {
-       struct unpack_trees_error_msgs msgs = {
-               /* would_overwrite */
-               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
-               /* not_uptodate_file */
-               "Your local changes to '%s' would be overwritten by merge.  Aborting.",
-               /* not_uptodate_dir */
-               "Updating '%s' would lose untracked files in it.  Aborting.",
-               /* would_lose_untracked */
-               "Untracked working tree file '%s' would be %s by merge.  Aborting",
-               /* bind_overlap -- will not happen here */
-               NULL,
-       };
-       if (advice_commit_before_merge) {
-               msgs.would_overwrite = msgs.not_uptodate_file =
-                       "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
-                       "Please, commit your changes or stash them before you can merge.";
+       int clean_merge = 1;
+       unsigned o_mode = entry->stages[1].mode;
+       unsigned a_mode = entry->stages[2].mode;
+       unsigned b_mode = entry->stages[3].mode;
+       unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
+       unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
+       unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
+       const char *add_branch;
+       const char *other_branch;
+       unsigned mode;
+       const unsigned char *sha;
+       const char *conf;
+       struct stat st;
+
+       /* We currently only handle D->F cases */
+       assert((!o_sha && a_sha && !b_sha) ||
+              (!o_sha && !a_sha && b_sha));
+
+       entry->processed = 1;
+
+       if (a_sha) {
+               add_branch = o->branch1;
+               other_branch = o->branch2;
+               mode = a_mode;
+               sha = a_sha;
+               conf = "file/directory";
+       } else {
+               add_branch = o->branch2;
+               other_branch = o->branch1;
+               mode = b_mode;
+               sha = b_sha;
+               conf = "directory/file";
        }
-       return msgs;
+       if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+               const char *new_path = unique_path(o, path, add_branch);
+               clean_merge = 0;
+               output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
+                      "Adding %s as %s",
+                      conf, path, other_branch, path, new_path);
+               remove_file(o, 0, path, 0);
+               update_file(o, 0, sha, mode, new_path);
+       } else {
+               output(o, 2, "Adding %s", path);
+               update_file(o, 1, sha, mode, path);
+       }
+
+       return clean_merge;
+}
+
+void set_porcelain_error_msgs(const char **msgs, const char *cmd)
+{
+       const char *msg;
+       char *tmp;
+       const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
+       if (advice_commit_before_merge)
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
+                       "Please, commit your changes or stash them before you can %s.";
+       else
+               msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
+       sprintf(tmp, msg, cmd, cmd2);
+       msgs[ERROR_WOULD_OVERWRITE] = tmp;
+       msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
+
+       msgs[ERROR_NOT_UPTODATE_DIR] =
+               "Updating the following directories would lose untracked files in it:\n%s";
+
+       if (advice_commit_before_merge)
+               msg = "The following untracked working tree files would be %s by %s:\n%%s"
+                       "Please move or remove them before you can %s.";
+       else
+               msg = "The following untracked working tree files would be %s by %s:\n%%s";
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "removed", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
+       tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
+       sprintf(tmp, msg, "overwritten", cmd, cmd2);
+       msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
+
+       /*
+        * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
+        * cannot easily display it as a list.
+        */
+       msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
+
+       msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
+               "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
+               "The following Working tree files would be overwritten by sparse checkout update:\n%s";
+       msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
+               "The following Working tree files would be removed by sparse checkout update:\n%s";
 }
 
 int merge_trees(struct merge_options *o,
@@ -1249,6 +1381,13 @@ int merge_trees(struct merge_options *o,
                                && !process_entry(o, path, e))
                                clean = 0;
                }
+               for (i = 0; i < entries->nr; i++) {
+                       const char *path = entries->items[i].string;
+                       struct stage_data *e = entries->items[i].util;
+                       if (!e->processed
+                               && !process_df_entry(o, path, e))
+                               clean = 0;
+               }
 
                string_list_clear(re_merge, 0);
                string_list_clear(re_head, 0);
@@ -1436,6 +1575,7 @@ void init_merge_options(struct merge_options *o)
        o->buffer_output = 1;
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
+       o->renormalize = 0;
        git_config(merge_recursive_config, o);
        if (getenv("GIT_MERGE_VERBOSITY"))
                o->verbosity =
index b831293b3865ae52584565039e4a893c3c6f45ff..196f0531062a095d57afc92c16c57a67ec93f31c 100644 (file)
@@ -14,6 +14,7 @@ struct merge_options {
        } recursive_variant;
        const char *subtree_shift;
        unsigned buffer_output : 1;
+       unsigned renormalize : 1;
        int verbosity;
        int diff_rename_limit;
        int merge_rename_limit;
@@ -23,8 +24,11 @@ struct merge_options {
        struct string_list current_directory_set;
 };
 
-/* Return a list of user-friendly error messages to be used by merge */
-struct unpack_trees_error_msgs get_porcelain_error_msgs(void);
+/*
+ * Sets the list of user-friendly error messages to be used by the
+ * command "cmd" (either merge or checkout)
+ */
+void set_porcelain_error_msgs(const char **msgs, const char *cmd);
 
 /* merge_trees() but with recursive ancestor consolidation */
 int merge_recursive(struct merge_options *o,
diff --git a/notes.c b/notes.c
index 197824439826881fc0de3275353ec1b74d86cfa3..7fd203560aaded5ebbd80d67c805df5a707aa60d 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -877,14 +877,6 @@ void string_list_add_refs_from_colon_sep(struct string_list *list,
        strbuf_release(&globbuf);
 }
 
-static int string_list_add_refs_from_list(struct string_list_item *item,
-                                         void *cb)
-{
-       struct string_list *list = cb;
-       string_list_add_refs_by_glob(list, item->string);
-       return 0;
-}
-
 static int notes_display_config(const char *k, const char *v, void *cb)
 {
        int *load_refs = cb;
@@ -947,30 +939,18 @@ void init_notes(struct notes_tree *t, const char *notes_ref,
        load_subtree(t, &root_tree, t->root, 0);
 }
 
-struct load_notes_cb_data {
-       int counter;
-       struct notes_tree **trees;
-};
-
-static int load_one_display_note_ref(struct string_list_item *item,
-                                    void *cb_data)
-{
-       struct load_notes_cb_data *c = cb_data;
-       struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
-       init_notes(t, item->string, combine_notes_ignore, 0);
-       c->trees[c->counter++] = t;
-       return 0;
-}
-
 struct notes_tree **load_notes_trees(struct string_list *refs)
 {
+       struct string_list_item *item;
+       int counter = 0;
        struct notes_tree **trees;
-       struct load_notes_cb_data cb_data;
        trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
-       cb_data.counter = 0;
-       cb_data.trees = trees;
-       for_each_string_list(refs, load_one_display_note_ref, &cb_data);
-       trees[cb_data.counter] = NULL;
+       for_each_string_list_item(item, refs) {
+               struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
+               init_notes(t, item->string, combine_notes_ignore, 0);
+               trees[counter++] = t;
+       }
+       trees[counter] = NULL;
        return trees;
 }
 
@@ -995,10 +975,12 @@ void init_display_notes(struct display_notes_opt *opt)
 
        git_config(notes_display_config, &load_config_refs);
 
-       if (opt && opt->extra_notes_refs)
-               for_each_string_list(opt->extra_notes_refs,
-                                    string_list_add_refs_from_list,
-                                    &display_notes_refs);
+       if (opt && opt->extra_notes_refs) {
+               struct string_list_item *item;
+               for_each_string_list_item(item, opt->extra_notes_refs)
+                       string_list_add_refs_by_glob(&display_notes_refs,
+                                                    item->string);
+       }
 
        display_notes_trees = load_notes_trees(&display_notes_refs);
        string_list_clear(&display_notes_refs, 0);
index 82877c831ca7fe39abbb98805f5801bdc6fcac9c..4d1d61546f0441bd198cda3ad153b35a91444b72 100644 (file)
--- a/object.h
+++ b/object.h
@@ -21,6 +21,8 @@ struct object_array {
        } *objects;
 };
 
+#define OBJECT_ARRAY_INIT { 0, 0, NULL }
+
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
index 395fb9527a3bc6dd8ca648233911a1c35604440d..9d0cb9a114cb3e5b0a4a4a132b9289ef18d8ae21 100644 (file)
@@ -77,7 +77,7 @@ static int verify_packfile(struct packed_git *p,
                err = error("%s SHA1 checksum mismatch",
                            p->pack_name);
        if (hashcmp(index_base + index_size - 40, pack_sig))
-               err = error("%s SHA1 does not match its inddex",
+               err = error("%s SHA1 does not match its index",
                            p->pack_name);
        unuse_pack(w_curs);
 
index 7f43f8ac3398fb2dbe393f08811989b0a8d4e2dd..1290570260d184b9c94fdab62650d6e474b365b4 100644 (file)
@@ -60,6 +60,37 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
        return 0;
 }
 
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+       char *p, *q;
+       int i;
+       p = name;
+       for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+               while (*p && *p != '/')
+                       p++;
+               /* tolerate duplicate slashes; see check_ref_format() */
+               while (*p == '/')
+                       p++;
+       }
+       for (q = p; *q; q++)
+               ;
+       while (1) {
+               while (q > p && *q != '/')
+                       q--;
+               while (q > p && *(q-1) == '/')
+                       q--;
+               if (q == p)
+                       break;
+               *q = '\0';
+               if (rmdir(git_path("%s", name)))
+                       break;
+       }
+}
+
 /* make sure nobody touched the ref, and unlink */
 static void prune_ref(struct ref_to_prune *r)
 {
@@ -68,6 +99,7 @@ static void prune_ref(struct ref_to_prune *r)
        if (lock) {
                unlink_or_warn(git_path("%s", r->name));
                unlock_ref(lock);
+               try_remove_empty_parents(r->name);
        }
 }
 
index 7435cdbf1d94378cf1c3408ae17cb610165a00ba..d982f0f1bf181f00f54861eb768504007490e95d 100644 (file)
@@ -69,7 +69,7 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
  * `flags`::
  *   mask of parse_opt_option_flags.
  *   PARSE_OPT_OPTARG: says that the argument is optional (not for BOOLEANs)
- *   PARSE_OPT_NOARG: says that this option takes no argument
+ *   PARSE_OPT_NOARG: says that this option does not take an argument
  *   PARSE_OPT_NONEG: says that this option cannot be negated
  *   PARSE_OPT_HIDDEN: this option is skipped in the default usage, and
  *                     shown only in the full usage.
diff --git a/path.c b/path.c
index b4c8d917229a4187f36a76f43603fc036e65632e..a2c9d1e24afb9d1250c846791607c49d82e9565e 100644 (file)
--- a/path.c
+++ b/path.c
@@ -122,6 +122,44 @@ char *git_path(const char *fmt, ...)
        return cleanup_path(pathname);
 }
 
+char *git_path_submodule(const char *path, const char *fmt, ...)
+{
+       char *pathname = get_pathname();
+       struct strbuf buf = STRBUF_INIT;
+       const char *git_dir;
+       va_list args;
+       unsigned len;
+
+       len = strlen(path);
+       if (len > PATH_MAX-100)
+               return bad_path;
+
+       strbuf_addstr(&buf, path);
+       if (len && path[len-1] != '/')
+               strbuf_addch(&buf, '/');
+       strbuf_addstr(&buf, ".git");
+
+       git_dir = read_gitfile_gently(buf.buf);
+       if (git_dir) {
+               strbuf_reset(&buf);
+               strbuf_addstr(&buf, git_dir);
+       }
+       strbuf_addch(&buf, '/');
+
+       if (buf.len >= PATH_MAX)
+               return bad_path;
+       memcpy(pathname, buf.buf, buf.len + 1);
+
+       strbuf_release(&buf);
+       len = strlen(pathname);
+
+       va_start(args, fmt);
+       len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
+       va_end(args);
+       if (len >= PATH_MAX)
+               return bad_path;
+       return cleanup_path(pathname);
+}
 
 /* git_mkstemp() - create tmp file honoring TMPDIR variable */
 int git_mkstemp(char *path, size_t len, const char *template)
@@ -316,6 +354,8 @@ char *expand_user_path(const char *path)
                size_t username_len = first_slash - username;
                if (username_len == 0) {
                        const char *home = getenv("HOME");
+                       if (!home)
+                               goto return_null;
                        strbuf_add(&user_path, home, strlen(home));
                } else {
                        struct passwd *pw = getpw_str(username, username_len);
index 4ab21d61b808d2e396419fb41419bcea7ab3cde4..a2ffb6402d45420dff4dcd545dfa08b57305d8cd 100644 (file)
@@ -38,7 +38,7 @@ $(makfile): ../GIT-CFLAGS Makefile
        echo '  echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-       $(PERL_PATH) $< PREFIX='$(prefix_SQ)'
+       $(PERL_PATH) $< PREFIX='$(prefix_SQ)' INSTALL_BASE=''
 endif
 
 # this is just added comfort for calling make directly in perl dir
index b515fa2de332cc570a8a32861bd8d6491b61133e..a03fabf060081e9fa5e0d753cd0d9cf4bca5062e 100644 (file)
@@ -90,7 +90,7 @@ static void walk_commit_list(struct rev_info *revs)
 {
        int i;
        struct commit *commit;
-       struct object_array objects = { 0, 0, NULL };
+       struct object_array objects = OBJECT_ARRAY_INIT;
 
        /* Walk all commits, process their trees */
        while ((commit = get_revision(revs)) != NULL)
index f1f789b7b87643245f1772d15b3f1cb321af324c..1f42473e8070a05ada8c56b0d60537227a5223ec 100644 (file)
@@ -1516,6 +1516,7 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
        int size = ondisk_ce_size(ce);
        struct ondisk_cache_entry *ondisk = xcalloc(1, size);
        char *name;
+       int result;
 
        ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
        ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
@@ -1539,7 +1540,9 @@ static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce)
                name = ondisk->name;
        memcpy(name, ce->name, ce_namelen(ce));
 
-       return ce_write(c, fd, ondisk, size);
+       result = ce_write(c, fd, ondisk, size);
+       free(ondisk);
+       return result;
 }
 
 int write_index(struct index_state *istate, int newfd)
diff --git a/refs.c b/refs.c
index b5400674d7a1fcf4cc16dd630b8671b2ad7b9f7b..e3c05110e58ca5684f9c43b1e1a5eb2587c96828 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -157,7 +157,7 @@ static struct cached_refs {
        char did_packed;
        struct ref_list *loose;
        struct ref_list *packed;
-} cached_refs;
+} cached_refs, submodule_refs;
 static struct ref_list *current_ref;
 
 static struct ref_list *extra_refs;
@@ -229,23 +229,45 @@ void clear_extra_refs(void)
        extra_refs = NULL;
 }
 
-static struct ref_list *get_packed_refs(void)
+static struct ref_list *get_packed_refs(const char *submodule)
 {
-       if (!cached_refs.did_packed) {
-               FILE *f = fopen(git_path("packed-refs"), "r");
-               cached_refs.packed = NULL;
+       const char *packed_refs_file;
+       struct cached_refs *refs;
+
+       if (submodule) {
+               packed_refs_file = git_path_submodule(submodule, "packed-refs");
+               refs = &submodule_refs;
+               free_ref_list(refs->packed);
+       } else {
+               packed_refs_file = git_path("packed-refs");
+               refs = &cached_refs;
+       }
+
+       if (!refs->did_packed || submodule) {
+               FILE *f = fopen(packed_refs_file, "r");
+               refs->packed = NULL;
                if (f) {
-                       read_packed_refs(f, &cached_refs);
+                       read_packed_refs(f, refs);
                        fclose(f);
                }
-               cached_refs.did_packed = 1;
+               refs->did_packed = 1;
        }
-       return cached_refs.packed;
+       return refs->packed;
 }
 
-static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
+static struct ref_list *get_ref_dir(const char *submodule, const char *base,
+                                   struct ref_list *list)
 {
-       DIR *dir = opendir(git_path("%s", base));
+       DIR *dir;
+       const char *path;
+
+       if (submodule)
+               path = git_path_submodule(submodule, "%s", base);
+       else
+               path = git_path("%s", base);
+
+
+       dir = opendir(path);
 
        if (dir) {
                struct dirent *de;
@@ -261,6 +283,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                        struct stat st;
                        int flag;
                        int namelen;
+                       const char *refdir;
 
                        if (de->d_name[0] == '.')
                                continue;
@@ -270,16 +293,27 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
                        if (has_extension(de->d_name, ".lock"))
                                continue;
                        memcpy(ref + baselen, de->d_name, namelen+1);
-                       if (stat(git_path("%s", ref), &st) < 0)
+                       refdir = submodule
+                               ? git_path_submodule(submodule, "%s", ref)
+                               : git_path("%s", ref);
+                       if (stat(refdir, &st) < 0)
                                continue;
                        if (S_ISDIR(st.st_mode)) {
-                               list = get_ref_dir(ref, list);
+                               list = get_ref_dir(submodule, ref, list);
                                continue;
                        }
-                       if (!resolve_ref(ref, sha1, 1, &flag)) {
+                       if (submodule) {
                                hashclr(sha1);
-                               flag |= REF_BROKEN;
-                       }
+                               flag = 0;
+                               if (resolve_gitlink_ref(submodule, ref, sha1) < 0) {
+                                       hashclr(sha1);
+                                       flag |= REF_BROKEN;
+                               }
+                       } else
+                               if (!resolve_ref(ref, sha1, 1, &flag)) {
+                                       hashclr(sha1);
+                                       flag |= REF_BROKEN;
+                               }
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
                free(ref);
@@ -322,10 +356,16 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-static struct ref_list *get_loose_refs(void)
+static struct ref_list *get_loose_refs(const char *submodule)
 {
+       if (submodule) {
+               free_ref_list(submodule_refs.loose);
+               submodule_refs.loose = get_ref_dir(submodule, "refs", NULL);
+               return submodule_refs.loose;
+       }
+
        if (!cached_refs.did_loose) {
-               cached_refs.loose = get_ref_dir("refs", NULL);
+               cached_refs.loose = get_ref_dir(NULL, "refs", NULL);
                cached_refs.did_loose = 1;
        }
        return cached_refs.loose;
@@ -459,7 +499,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                git_snpath(path, sizeof(path), "%s", ref);
                /* Special case: non-existing file. */
                if (lstat(path, &st) < 0) {
-                       struct ref_list *list = get_packed_refs();
+                       struct ref_list *list = get_packed_refs(NULL);
                        while (list) {
                                if (!strcmp(ref, list->name)) {
                                        hashcpy(sha1, list->sha1);
@@ -588,7 +628,7 @@ int peel_ref(const char *ref, unsigned char *sha1)
                return -1;
 
        if ((flag & REF_ISPACKED)) {
-               struct ref_list *list = get_packed_refs();
+               struct ref_list *list = get_packed_refs(NULL);
 
                while (list) {
                        if (!strcmp(list->name, ref)) {
@@ -615,12 +655,12 @@ fallback:
        return -1;
 }
 
-static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
-                          int flags, void *cb_data)
+static int do_for_each_ref(const char *submodule, const char *base, each_ref_fn fn,
+                          int trim, int flags, void *cb_data)
 {
        int retval = 0;
-       struct ref_list *packed = get_packed_refs();
-       struct ref_list *loose = get_loose_refs();
+       struct ref_list *packed = get_packed_refs(submodule);
+       struct ref_list *loose = get_loose_refs(submodule);
 
        struct ref_list *extra;
 
@@ -657,24 +697,54 @@ end_each:
        return retval;
 }
 
-int head_ref(each_ref_fn fn, void *cb_data)
+
+static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
        unsigned char sha1[20];
        int flag;
 
+       if (submodule) {
+               if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
+                       return fn("HEAD", sha1, 0, cb_data);
+
+               return 0;
+       }
+
        if (resolve_ref("HEAD", sha1, 1, &flag))
                return fn("HEAD", sha1, flag, cb_data);
+
        return 0;
 }
 
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(NULL, fn, cb_data);
+}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(submodule, fn, cb_data);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0, 0, cb_data);
+       return do_for_each_ref(NULL, "refs/", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(submodule, "refs/", fn, 0, 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(prefix, fn, strlen(prefix), 0, cb_data);
+       return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+               each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
@@ -682,19 +752,34 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
        return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
+int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+}
+
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
        return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
+int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+}
+
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
        return for_each_ref_in("refs/remotes/", fn, cb_data);
 }
 
+int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
+}
+
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
+       return do_for_each_ref(NULL, "refs/replace/", fn, 13, 0, cb_data);
 }
 
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
@@ -734,7 +819,7 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 int for_each_rawref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref("refs/", fn, 0,
+       return do_for_each_ref(NULL, "refs/", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
@@ -958,7 +1043,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(ref, NULL, get_packed_refs(), 0)) {
+            !is_refname_available(ref, NULL, get_packed_refs(NULL), 0)) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -1021,7 +1106,7 @@ static int repack_without_ref(const char *refname)
        int fd;
        int found = 0;
 
-       packed_ref_list = get_packed_refs();
+       packed_ref_list = get_packed_refs(NULL);
        for (list = packed_ref_list; list; list = list->next) {
                if (!strcmp(refname, list->name)) {
                        found = 1;
@@ -1119,10 +1204,10 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
        if (!symref)
                return error("refname %s not found", oldref);
 
-       if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
+       if (!is_refname_available(newref, oldref, get_packed_refs(NULL), 0))
                return 1;
 
-       if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
+       if (!is_refname_available(newref, oldref, get_loose_refs(NULL), 0))
                return 1;
 
        lock = lock_ref_sha1_basic(renamed_ref, NULL, 0, NULL);
diff --git a/refs.h b/refs.h
index 762ce504b5eba3aacff204321a7c41db2b1e6053..5e7a9a59f5f6b687d15eb37a11696433bdc8f15c 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -28,6 +28,14 @@ extern int for_each_replace_ref(each_ref_fn, void *);
 extern int for_each_glob_ref(each_ref_fn, const char *pattern, void *);
 extern int for_each_glob_ref_in(each_ref_fn, const char *pattern, const char* prefix, void *);
 
+extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_ref_in_submodule(const char *submodule, const char *prefix,
+               each_ref_fn fn, void *cb_data);
+extern int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+extern int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
+
 static inline const char *has_glob_specials(const char *pattern)
 {
        return strpbrk(pattern, "?*[");
index 24fbb9a9b972c1078b3688b2d0683c9704e09ee6..04d4813e4183c675b54aba942cd078d8ff632053 100644 (file)
@@ -528,11 +528,12 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
                rpc->len = n;
                err |= post_rpc(rpc);
        }
-       strbuf_read(&rpc->result, client.out, 0);
 
        close(client.in);
-       close(client.out);
        client.in = -1;
+       strbuf_read(&rpc->result, client.out, 0);
+
+       close(client.out);
        client.out = -1;
 
        err |= finish_command(&client);
index afbba47460c0204721d61800033ed0a9f93f92bc..9143ec7a1772c45b6c3d4c84f6fc000040400dee 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -754,7 +754,7 @@ int for_each_remote(each_remote_fn fn, void *priv)
 
 void ref_remove_duplicates(struct ref *ref_map)
 {
-       struct string_list refs = { NULL, 0, 0, 0 };
+       struct string_list refs = STRING_LIST_INIT_NODUP;
        struct string_list_item *item = NULL;
        struct ref *prev = NULL, *next = NULL;
        for (; ref_map; prev = ref_map, ref_map = next) {
@@ -1704,7 +1704,7 @@ static int get_stale_heads_cb(const char *refname,
 struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
 {
        struct ref *ref, *stale_refs = NULL;
-       struct string_list ref_names = { NULL, 0, 0, 0 };
+       struct string_list ref_names = STRING_LIST_INIT_NODUP;
        struct stale_heads_info info;
        info.remote = remote;
        info.ref_names = &ref_names;
index d03a69634b81e1146c20182670a400b0c29b275f..861ca7c815b4857f1fde399617860e5a179e2ca3 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -319,6 +319,10 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
                if (!mmfile[i].ptr && !mmfile[i].size)
                        mmfile[i].ptr = xstrdup("");
        }
+       /*
+        * NEEDSWORK: handle conflicts from merges with
+        * merge.renormalize set, too
+        */
        ll_merge(&result, path, &mmfile[0], NULL,
                 &mmfile[1], "ours",
                 &mmfile[2], "theirs", 0);
@@ -378,7 +382,13 @@ static int merge(const char *name, const char *path)
        }
        ret = ll_merge(&result, path, &base, NULL, &cur, "", &other, "", 0);
        if (!ret) {
-               FILE *f = fopen(path, "w");
+               FILE *f;
+
+               if (utime(rerere_path(name, "postimage"), NULL) < 0)
+                       warning("failed utime() on %s: %s",
+                                       rerere_path(name, "postimage"),
+                                       strerror(errno));
+               f = fopen(path, "w");
                if (!f)
                        return error("Could not open %s: %s", path,
                                     strerror(errno));
@@ -426,8 +436,8 @@ static int update_paths(struct string_list *update)
 
 static int do_plain_rerere(struct string_list *rr, int fd)
 {
-       struct string_list conflict = { NULL, 0, 0, 1 };
-       struct string_list update = { NULL, 0, 0, 1 };
+       struct string_list conflict = STRING_LIST_INIT_DUP;
+       struct string_list update = STRING_LIST_INIT_DUP;
        int i;
 
        find_conflict(&conflict);
@@ -547,7 +557,7 @@ int setup_rerere(struct string_list *merge_rr, int flags)
 
 int rerere(int flags)
 {
-       struct string_list merge_rr = { NULL, 0, 0, 1 };
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
        int fd;
 
        fd = setup_rerere(&merge_rr, flags);
@@ -585,8 +595,8 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
 int rerere_forget(const char **pathspec)
 {
        int i, fd;
-       struct string_list conflict = { NULL, 0, 0, 1 };
-       struct string_list merge_rr = { NULL, 0, 0, 1 };
+       struct string_list conflict = STRING_LIST_INIT_DUP;
+       struct string_list merge_rr = STRING_LIST_INIT_DUP;
 
        if (read_cache() < 0)
                return error("Could not read index");
index 174ebec9e573b1942e379942037f38500bd19b97..72b46125b719861641a55ee8fd534e0d1f47a94f 100644 (file)
@@ -28,29 +28,25 @@ void record_resolve_undo(struct index_state *istate, struct cache_entry *ce)
        ui->mode[stage - 1] = ce->ce_mode;
 }
 
-static int write_one(struct string_list_item *item, void *cbdata)
+void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
 {
-       struct strbuf *sb = cbdata;
-       struct resolve_undo_info *ui = item->util;
-       int i;
+       struct string_list_item *item;
+       for_each_string_list_item(item, resolve_undo) {
+               struct resolve_undo_info *ui = item->util;
+               int i;
 
-       if (!ui)
-               return 0;
-       strbuf_addstr(sb, item->string);
-       strbuf_addch(sb, 0);
-       for (i = 0; i < 3; i++)
-               strbuf_addf(sb, "%o%c", ui->mode[i], 0);
-       for (i = 0; i < 3; i++) {
-               if (!ui->mode[i])
+               if (!ui)
                        continue;
-               strbuf_add(sb, ui->sha1[i], 20);
+               strbuf_addstr(sb, item->string);
+               strbuf_addch(sb, 0);
+               for (i = 0; i < 3; i++)
+                       strbuf_addf(sb, "%o%c", ui->mode[i], 0);
+               for (i = 0; i < 3; i++) {
+                       if (!ui->mode[i])
+                               continue;
+                       strbuf_add(sb, ui->sha1[i], 20);
+               }
        }
-       return 0;
-}
-
-void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
-{
-   for_each_string_list(resolve_undo, write_one, sb);
 }
 
 struct string_list *resolve_undo_read(const char *data, unsigned long size)
index 7e82efd9324e84582732485c53c6c25a24c29997..b1c18906badc5fc4ce3af48daa7c226365e3c08f 100644 (file)
@@ -820,12 +820,12 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_flags = flags;
 }
 
-static void handle_refs(struct rev_info *revs, unsigned flags,
-               int (*for_each)(each_ref_fn, void *))
+static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
+               int (*for_each)(const char *, each_ref_fn, void *))
 {
        struct all_refs_cb cb;
        init_all_refs_cb(&cb, revs, flags);
-       for_each(handle_one_ref, &cb);
+       for_each(submodule, handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -1148,6 +1148,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                               int *unkc, const char **unkv)
 {
        const char *arg = argv[0];
+       const char *optarg;
+       int argcount;
 
        /* pseudo revision arguments */
        if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") ||
@@ -1160,11 +1162,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                return 1;
        }
 
-       if (!prefixcmp(arg, "--max-count=")) {
-               revs->max_count = atoi(arg + 12);
+       if ((argcount = parse_long_opt("max-count", argv, &optarg))) {
+               revs->max_count = atoi(optarg);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--skip=")) {
-               revs->skip_count = atoi(arg + 7);
+               return argcount;
+       } else if ((argcount = parse_long_opt("skip", argv, &optarg))) {
+               revs->skip_count = atoi(optarg);
+               return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
        /* accept -<digit>, like traditional "head" */
                revs->max_count = atoi(arg + 1);
@@ -1178,18 +1182,24 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!prefixcmp(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
-       } else if (!prefixcmp(arg, "--max-age=")) {
-               revs->max_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--since=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--after=")) {
-               revs->max_age = approxidate(arg + 8);
-       } else if (!prefixcmp(arg, "--min-age=")) {
-               revs->min_age = atoi(arg + 10);
-       } else if (!prefixcmp(arg, "--before=")) {
-               revs->min_age = approxidate(arg + 9);
-       } else if (!prefixcmp(arg, "--until=")) {
-               revs->min_age = approxidate(arg + 8);
+       } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
+               revs->max_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("since", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("after", argv, &optarg))) {
+               revs->max_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("min-age", argv, &optarg))) {
+               revs->min_age = atoi(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("before", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("until", argv, &optarg))) {
+               revs->min_age = approxidate(optarg);
+               return argcount;
        } else if (!strcmp(arg, "--first-parent")) {
                revs->first_parent_only = 1;
        } else if (!strcmp(arg, "--ancestry-path")) {
@@ -1295,6 +1305,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
        } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+               /*
+                * Detached form ("--pretty X" as opposed to "--pretty=X")
+                * not allowed, since the argument is optional.
+                */
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+9, revs);
@@ -1359,21 +1373,25 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--relative-date")) {
                revs->date_mode = DATE_RELATIVE;
                revs->date_mode_explicit = 1;
-       } else if (!strncmp(arg, "--date=", 7)) {
-               revs->date_mode = parse_date_format(arg + 7);
+       } else if ((argcount = parse_long_opt("date", argv, &optarg))) {
+               revs->date_mode = parse_date_format(optarg);
                revs->date_mode_explicit = 1;
+               return argcount;
        } else if (!strcmp(arg, "--log-size")) {
                revs->show_log_size = 1;
        }
        /*
         * Grepping the commit log
         */
-       else if (!prefixcmp(arg, "--author=")) {
-               add_header_grep(revs, GREP_HEADER_AUTHOR, arg+9);
-       } else if (!prefixcmp(arg, "--committer=")) {
-               add_header_grep(revs, GREP_HEADER_COMMITTER, arg+12);
-       } else if (!prefixcmp(arg, "--grep=")) {
-               add_message_grep(revs, arg+7);
+       else if ((argcount = parse_long_opt("author", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_AUTHOR, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("committer", argv, &optarg))) {
+               add_header_grep(revs, GREP_HEADER_COMMITTER, optarg);
+               return argcount;
+       } else if ((argcount = parse_long_opt("grep", argv, &optarg))) {
+               add_message_grep(revs, optarg);
+               return argcount;
        } else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
                revs->grep_filter.regflags |= REG_EXTENDED;
        } else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
@@ -1382,12 +1400,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->grep_filter.fixed = 1;
        } else if (!strcmp(arg, "--all-match")) {
                revs->grep_filter.all_match = 1;
-       } else if (!prefixcmp(arg, "--encoding=")) {
-               arg += 11;
-               if (strcmp(arg, "none"))
-                       git_log_output_encoding = xstrdup(arg);
+       } else if ((argcount = parse_long_opt("encoding", argv, &optarg))) {
+               if (strcmp(optarg, "none"))
+                       git_log_output_encoding = xstrdup(optarg);
                else
                        git_log_output_encoding = "";
+               return argcount;
        } else if (!strcmp(arg, "--reverse")) {
                revs->reverse ^= 1;
        } else if (!strcmp(arg, "--children")) {
@@ -1417,14 +1435,14 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
        ctx->argc -= n;
 }
 
-static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_bad_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/bad", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/bad", fn, cb_data);
 }
 
-static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data)
+static int for_each_good_bisect_ref(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/bisect/good", fn, cb_data);
+       return for_each_ref_in_submodule(submodule, "refs/bisect/good", fn, cb_data);
 }
 
 static void append_prune_data(const char ***prune_data, const char **av)
@@ -1466,6 +1484,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 {
        int i, flags, left, seen_dashdash, read_from_stdin, got_rev_arg = 0;
        const char **prune_data = NULL;
+       const char *submodule = NULL;
+       const char *optarg;
+       int argcount;
+
+       if (opt)
+               submodule = opt->submodule;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -1490,32 +1514,33 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                        int opts;
 
                        if (!strcmp(arg, "--all")) {
-                               handle_refs(revs, flags, for_each_ref);
-                               handle_refs(revs, flags, head_ref);
+                               handle_refs(submodule, revs, flags, for_each_ref_submodule);
+                               handle_refs(submodule, revs, flags, head_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--branches")) {
-                               handle_refs(revs, flags, for_each_branch_ref);
+                               handle_refs(submodule, revs, flags, for_each_branch_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
-                               handle_refs(revs, flags, for_each_bad_bisect_ref);
-                               handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
+                               handle_refs(submodule, revs, flags, for_each_bad_bisect_ref);
+                               handle_refs(submodule, revs, flags ^ UNINTERESTING, for_each_good_bisect_ref);
                                revs->bisect = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--tags")) {
-                               handle_refs(revs, flags, for_each_tag_ref);
+                               handle_refs(submodule, revs, flags, for_each_tag_ref_submodule);
                                continue;
                        }
                        if (!strcmp(arg, "--remotes")) {
-                               handle_refs(revs, flags, for_each_remote_ref);
+                               handle_refs(submodule, revs, flags, for_each_remote_ref_submodule);
                                continue;
                        }
-                       if (!prefixcmp(arg, "--glob=")) {
+                       if ((argcount = parse_long_opt("glob", argv + i, &optarg))) {
                                struct all_refs_cb cb;
+                               i += argcount - 1;
                                init_all_refs_cb(&cb, revs, flags);
-                               for_each_glob_ref(handle_one_ref, arg + 7, &cb);
+                               for_each_glob_ref(handle_one_ref, optarg, &cb);
                                continue;
                        }
                        if (!prefixcmp(arg, "--branches=")) {
index 36fdf22b299c4cc7e6f20d767d5c6a6c3f69d952..05659c64acd7fe8eb7be011cee6174e397e57baf 100644 (file)
@@ -151,6 +151,7 @@ extern volatile show_early_output_fn_t show_early_output;
 struct setup_revision_opt {
        const char *def;
        void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+       const char *submodule;
 };
 
 extern void init_revisions(struct rev_info *revs, const char *prefix);
diff --git a/setup.c b/setup.c
index 276916052795c5ffa872b57a7e6723edc71dfdc4..a3b76de2bb1c18e2f3b8cdf5799c3c650b16d027 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -313,21 +313,129 @@ const char *read_gitfile_gently(const char *path)
        return path;
 }
 
+static const char *setup_explicit_git_dir(const char *gitdirenv,
+                               const char *work_tree_env, int *nongit_ok)
+{
+       static char buffer[1024 + 1];
+       const char *retval;
+
+       if (PATH_MAX - 40 < strlen(gitdirenv))
+               die("'$%s' too big", GIT_DIR_ENVIRONMENT);
+       if (!is_git_directory(gitdirenv)) {
+               if (nongit_ok) {
+                       *nongit_ok = 1;
+                       return NULL;
+               }
+               die("Not a git repository: '%s'", gitdirenv);
+       }
+       if (!work_tree_env) {
+               retval = set_work_tree(gitdirenv);
+               /* config may override worktree */
+               if (check_repository_format_gently(nongit_ok))
+                       return NULL;
+               return retval;
+       }
+       if (check_repository_format_gently(nongit_ok))
+               return NULL;
+       retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
+                       get_git_work_tree());
+       if (!retval || !*retval)
+               return NULL;
+       set_git_dir(make_absolute_path(gitdirenv));
+       if (chdir(work_tree_env) < 0)
+               die_errno ("Could not chdir to '%s'", work_tree_env);
+       strcat(buffer, "/");
+       return retval;
+}
+
+static int cwd_contains_git_dir(const char **gitfile_dirp)
+{
+       const char *gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
+       *gitfile_dirp = gitfile_dir;
+       if (gitfile_dir) {
+               if (set_git_dir(gitfile_dir))
+                       die("Repository setup failed");
+               return 1;
+       }
+       return is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT);
+}
+
+static const char *setup_discovered_git_dir(const char *work_tree_env,
+               int offset, int len, char *cwd, int *nongit_ok)
+{
+       int root_len;
+
+       inside_git_dir = 0;
+       if (!work_tree_env)
+               inside_work_tree = 1;
+       root_len = offset_1st_component(cwd);
+       git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
+       if (check_repository_format_gently(nongit_ok))
+               return NULL;
+       if (offset == len)
+               return NULL;
+
+       /* Make "offset" point to past the '/', and add a '/' at the end */
+       offset++;
+       cwd[len++] = '/';
+       cwd[len] = 0;
+       return cwd + offset;
+}
+
+static const char *setup_bare_git_dir(const char *work_tree_env,
+               int offset, int len, char *cwd, int *nongit_ok)
+{
+       int root_len;
+
+       inside_git_dir = 1;
+       if (!work_tree_env)
+               inside_work_tree = 0;
+       if (offset != len) {
+               if (chdir(cwd))
+                       die_errno("Cannot come back to cwd");
+               root_len = offset_1st_component(cwd);
+               cwd[offset > root_len ? offset : root_len] = '\0';
+               set_git_dir(cwd);
+       } else
+               set_git_dir(".");
+       check_repository_format_gently(nongit_ok);
+       return NULL;
+}
+
+static const char *setup_nongit(const char *cwd, int *nongit_ok)
+{
+       if (!nongit_ok)
+               die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
+       if (chdir(cwd))
+               die_errno("Cannot come back to cwd");
+       *nongit_ok = 1;
+       return NULL;
+}
+
+static dev_t get_device_or_die(const char *path, const char *prefix)
+{
+       struct stat buf;
+       if (stat(path, &buf))
+               die_errno("failed to stat '%s%s%s'",
+                               prefix ? prefix : "",
+                               prefix ? "/" : "", path);
+       return buf.st_dev;
+}
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
  */
-const char *setup_git_directory_gently(int *nongit_ok)
+static const char *setup_git_directory_gently_1(int *nongit_ok)
 {
        const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
        const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        static char cwd[PATH_MAX+1];
        const char *gitdirenv;
        const char *gitfile_dir;
-       int len, offset, ceil_offset, root_len;
+       int len, offset, ceil_offset;
        dev_t current_device = 0;
        int one_filesystem = 1;
-       struct stat buf;
 
        /*
         * Let's assume that we are in a git repository.
@@ -343,38 +451,8 @@ const char *setup_git_directory_gently(int *nongit_ok)
         * validation.
         */
        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-       if (gitdirenv) {
-               if (PATH_MAX - 40 < strlen(gitdirenv))
-                       die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-               if (is_git_directory(gitdirenv)) {
-                       static char buffer[1024 + 1];
-                       const char *retval;
-
-                       if (!work_tree_env) {
-                               retval = set_work_tree(gitdirenv);
-                               /* config may override worktree */
-                               if (check_repository_format_gently(nongit_ok))
-                                       return NULL;
-                               return retval;
-                       }
-                       if (check_repository_format_gently(nongit_ok))
-                               return NULL;
-                       retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
-                                       get_git_work_tree());
-                       if (!retval || !*retval)
-                               return NULL;
-                       set_git_dir(make_absolute_path(gitdirenv));
-                       if (chdir(work_tree_env) < 0)
-                               die_errno ("Could not chdir to '%s'", work_tree_env);
-                       strcat(buffer, "/");
-                       return retval;
-               }
-               if (nongit_ok) {
-                       *nongit_ok = 1;
-                       return NULL;
-               }
-               die("Not a git repository: '%s'", gitdirenv);
-       }
+       if (gitdirenv)
+               return setup_explicit_git_dir(gitdirenv, work_tree_env, nongit_ok);
 
        if (!getcwd(cwd, sizeof(cwd)-1))
                die_errno("Unable to read current working directory");
@@ -396,49 +474,21 @@ const char *setup_git_directory_gently(int *nongit_ok)
         */
        offset = len = strlen(cwd);
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
-       if (one_filesystem) {
-               if (stat(".", &buf))
-                       die_errno("failed to stat '.'");
-               current_device = buf.st_dev;
-       }
+       if (one_filesystem)
+               current_device = get_device_or_die(".", NULL);
        for (;;) {
-               gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
-               if (gitfile_dir) {
-                       if (set_git_dir(gitfile_dir))
-                               die("Repository setup failed");
-                       break;
-               }
-               if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
-                       break;
-               if (is_git_directory(".")) {
-                       inside_git_dir = 1;
-                       if (!work_tree_env)
-                               inside_work_tree = 0;
-                       if (offset != len) {
-                               root_len = offset_1st_component(cwd);
-                               cwd[offset > root_len ? offset : root_len] = '\0';
-                               set_git_dir(cwd);
-                       } else
-                               set_git_dir(".");
-                       check_repository_format_gently(nongit_ok);
-                       return NULL;
-               }
+               if (cwd_contains_git_dir(&gitfile_dir))
+                       return setup_discovered_git_dir(work_tree_env, offset,
+                                                       len, cwd, nongit_ok);
+               if (is_git_directory("."))
+                       return setup_bare_git_dir(work_tree_env, offset,
+                                                       len, cwd, nongit_ok);
                while (--offset > ceil_offset && cwd[offset] != '/');
-               if (offset <= ceil_offset) {
-                       if (nongit_ok) {
-                               if (chdir(cwd))
-                                       die_errno("Cannot come back to cwd");
-                               *nongit_ok = 1;
-                               return NULL;
-                       }
-                       die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
-               }
+               if (offset <= ceil_offset)
+                       return setup_nongit(cwd, nongit_ok);
                if (one_filesystem) {
-                       if (stat("..", &buf)) {
-                               cwd[offset] = '\0';
-                               die_errno("failed to stat '%s/..'", cwd);
-                       }
-                       if (buf.st_dev != current_device) {
+                       dev_t parent_device = get_device_or_die("..", cwd);
+                       if (parent_device != current_device) {
                                if (nongit_ok) {
                                        if (chdir(cwd))
                                                die_errno("Cannot come back to cwd");
@@ -455,22 +505,16 @@ const char *setup_git_directory_gently(int *nongit_ok)
                        die_errno("Cannot change to '%s/..'", cwd);
                }
        }
+}
 
-       inside_git_dir = 0;
-       if (!work_tree_env)
-               inside_work_tree = 1;
-       root_len = offset_1st_component(cwd);
-       git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
-       if (check_repository_format_gently(nongit_ok))
-               return NULL;
-       if (offset == len)
-               return NULL;
+const char *setup_git_directory_gently(int *nongit_ok)
+{
+       const char *prefix;
 
-       /* Make "offset" point to past the '/', and add a '/' at the end */
-       offset++;
-       cwd[len++] = '/';
-       cwd[len] = 0;
-       return cwd + offset;
+       prefix = setup_git_directory_gently_1(nongit_ok);
+       if (startup_info)
+               startup_info->have_repository = !nongit_ok || !*nongit_ok;
+       return prefix;
 }
 
 int git_config_perm(const char *var, const char *value)
index e42ef96d457f6aa12ab7d0057acf936db0335d40..0cd9435619f1e0637584289b45d52c1cdd8a9460 100644 (file)
@@ -2086,6 +2086,7 @@ void *read_sha1_file_repl(const unsigned char *sha1,
 {
        const unsigned char *repl = lookup_replace_object(sha1);
        void *data = read_object(repl, type, size);
+       char *path;
 
        /* die if we replaced an object with one that does not exist */
        if (!data && repl != sha1)
@@ -2093,8 +2094,16 @@ void *read_sha1_file_repl(const unsigned char *sha1,
                    sha1_to_hex(repl), sha1_to_hex(sha1));
 
        /* legacy behavior is to die on corrupted objects */
-       if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
-               die("object %s is corrupted", sha1_to_hex(repl));
+       if (!data) {
+               if (has_loose_object(repl)) {
+                       path = sha1_file_name(sha1);
+                       die("loose object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+               }
+               if (has_packed_and_bad(repl)) {
+                       path = sha1_pack_name(sha1);
+                       die("packed object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+               }
+       }
 
        if (replacement)
                *replacement = repl;
index 4f2af8da934b125f2c09ceb4d8185dabc58f7831..4af94fa59806c570c177a68139b54d46772d68a5 100644 (file)
@@ -659,6 +659,16 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
        return get_short_sha1(name, len, sha1, 0);
 }
 
+/*
+ * This interprets names like ':/Initial revision of "git"' by searching
+ * through history and returning the first commit whose message starts
+ * the given regular expression.
+ *
+ * For future extension, ':/!' is reserved. If you want to match a message
+ * beginning with a '!', you have to repeat the exclamation mark.
+ */
+#define ONELINE_SEEN (1u<<20)
+
 static int handle_one_ref(const char *path,
                const unsigned char *sha1, int flag, void *cb_data)
 {
@@ -674,19 +684,10 @@ static int handle_one_ref(const char *path,
        if (object->type != OBJ_COMMIT)
                return 0;
        insert_by_date((struct commit *)object, list);
+       object->flags |= ONELINE_SEEN;
        return 0;
 }
 
-/*
- * This interprets names like ':/Initial revision of "git"' by searching
- * through history and returning the first commit whose message matches
- * the given regular expression.
- *
- * For future extension, ':/!' is reserved. If you want to match a message
- * beginning with a '!', you have to repeat the exclamation mark.
- */
-
-#define ONELINE_SEEN (1u<<20)
 static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
 {
        struct commit_list *list = NULL, *backup = NULL, *l;
index 4d90eda19efe0a80c1cb39e8897ab3ed5e6fcf56..a0363dea203d2a06e985bb4e140ffc675428cbf8 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -47,7 +47,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
 {
        int i = 0, cur_depth = 0;
        struct commit_list *result = NULL;
-       struct object_array stack = {0, 0, NULL};
+       struct object_array stack = OBJECT_ARRAY_INIT;
        struct commit *commit = NULL;
 
        while (commit || i < heads->nr || stack.nr) {
index 680d600d16c095eb167be2eca8c1a7cb94f962af..494693898b89fe2f8145b483ea7e366e9b1192c1 100644 (file)
@@ -12,6 +12,9 @@ struct string_list
        unsigned int strdup_strings:1;
 };
 
+#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
+#define STRING_LIST_INIT_DUP   { NULL, 0, 0, 1 }
+
 void print_string_list(const struct string_list *p, const char *text);
 void string_list_clear(struct string_list *list, int free_util);
 
@@ -20,10 +23,12 @@ void string_list_clear(struct string_list *list, int free_util);
 typedef void (*string_list_clear_func_t)(void *p, const char *str);
 void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
 
-/* Use this function to iterate over each item */
+/* Use this function or the macro below to iterate over each item */
 typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
 int for_each_string_list(struct string_list *list,
                         string_list_each_func_t, void *cb_data);
+#define for_each_string_list_item(item,list) \
+       for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
 
 /* Use these functions only on sorted lists: */
 int string_list_has_string(const struct string_list *list, const char *string);
index 61cb6e21ddfc789ce59597c96204e7904cd9359e..91a47587478ae0550be8f41c00cb1749c85834f4 100644 (file)
@@ -6,6 +6,11 @@
 #include "revision.h"
 #include "run-command.h"
 #include "diffcore.h"
+#include "refs.h"
+#include "string-list.h"
+
+struct string_list config_name_for_path;
+struct string_list config_ignore_for_name;
 
 static int add_submodule_odb(const char *path)
 {
@@ -46,16 +51,90 @@ done:
        return ret;
 }
 
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+                                            const char *path)
+{
+       struct string_list_item *path_option, *ignore_option;
+       path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+       if (path_option) {
+               ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
+               if (ignore_option)
+                       handle_ignore_submodules_arg(diffopt, ignore_option->util);
+       }
+}
+
+static int submodule_config(const char *var, const char *value, void *cb)
+{
+       if (!prefixcmp(var, "submodule."))
+               return parse_submodule_config_option(var, value);
+       return 0;
+}
+
+void gitmodules_config(void)
+{
+       const char *work_tree = get_git_work_tree();
+       if (work_tree) {
+               struct strbuf gitmodules_path = STRBUF_INIT;
+               strbuf_addstr(&gitmodules_path, work_tree);
+               strbuf_addstr(&gitmodules_path, "/.gitmodules");
+               git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
+               strbuf_release(&gitmodules_path);
+       }
+}
+
+int parse_submodule_config_option(const char *var, const char *value)
+{
+       int len;
+       struct string_list_item *config;
+       struct strbuf submodname = STRBUF_INIT;
+
+       var += 10;              /* Skip "submodule." */
+
+       len = strlen(var);
+       if ((len > 5) && !strcmp(var + len - 5, ".path")) {
+               strbuf_add(&submodname, var, len - 5);
+               config = unsorted_string_list_lookup(&config_name_for_path, value);
+               if (config)
+                       free(config->util);
+               else
+                       config = string_list_append(&config_name_for_path, xstrdup(value));
+               config->util = strbuf_detach(&submodname, NULL);
+               strbuf_release(&submodname);
+       } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
+               if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
+                   strcmp(value, "all") && strcmp(value, "none")) {
+                       warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
+                       return 0;
+               }
+
+               strbuf_add(&submodname, var, len - 7);
+               config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
+               if (config)
+                       free(config->util);
+               else
+                       config = string_list_append(&config_ignore_for_name,
+                                                   strbuf_detach(&submodname, NULL));
+               strbuf_release(&submodname);
+               config->util = xstrdup(value);
+               return 0;
+       }
+       return 0;
+}
+
 void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
 {
+       DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
+       DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+       DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+
        if (!strcmp(arg, "all"))
                DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
        else if (!strcmp(arg, "untracked"))
                DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
        else if (!strcmp(arg, "dirty"))
                DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
-       else
+       else if (strcmp(arg, "none"))
                die("bad --ignore-submodules argument: %s", arg);
 }
 
@@ -218,3 +297,163 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        strbuf_release(&buf);
        return dirty_submodule;
 }
+
+static int find_first_merges(struct object_array *result, const char *path,
+               struct commit *a, struct commit *b)
+{
+       int i, j;
+       struct object_array merges;
+       struct commit *commit;
+       int contains_another;
+
+       char merged_revision[42];
+       const char *rev_args[] = { "rev-list", "--merges", "--ancestry-path",
+                                  "--all", merged_revision, NULL };
+       struct rev_info revs;
+       struct setup_revision_opt rev_opts;
+
+       memset(&merges, 0, sizeof(merges));
+       memset(result, 0, sizeof(struct object_array));
+       memset(&rev_opts, 0, sizeof(rev_opts));
+
+       /* get all revisions that merge commit a */
+       snprintf(merged_revision, sizeof(merged_revision), "^%s",
+                       sha1_to_hex(a->object.sha1));
+       init_revisions(&revs, NULL);
+       rev_opts.submodule = path;
+       setup_revisions(sizeof(rev_args)/sizeof(char *)-1, rev_args, &revs, &rev_opts);
+
+       /* save all revisions from the above list that contain b */
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+       while ((commit = get_revision(&revs)) != NULL) {
+               struct object *o = &(commit->object);
+               if (in_merge_bases(b, &commit, 1))
+                       add_object_array(o, NULL, &merges);
+       }
+
+       /* Now we've got all merges that contain a and b. Prune all
+        * merges that contain another found merge and save them in
+        * result.
+        */
+       for (i = 0; i < merges.nr; i++) {
+               struct commit *m1 = (struct commit *) merges.objects[i].item;
+
+               contains_another = 0;
+               for (j = 0; j < merges.nr; j++) {
+                       struct commit *m2 = (struct commit *) merges.objects[j].item;
+                       if (i != j && in_merge_bases(m2, &m1, 1)) {
+                               contains_another = 1;
+                               break;
+                       }
+               }
+
+               if (!contains_another)
+                       add_object_array(merges.objects[i].item,
+                                        merges.objects[i].name, result);
+       }
+
+       free(merges.objects);
+       return result->nr;
+}
+
+static void print_commit(struct commit *commit)
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct pretty_print_context ctx = {0};
+       ctx.date_mode = DATE_NORMAL;
+       format_commit_message(commit, " %h: %m %s", &sb, &ctx);
+       fprintf(stderr, "%s\n", sb.buf);
+       strbuf_release(&sb);
+}
+
+#define MERGE_WARNING(path, msg) \
+       warning("Failed to merge submodule %s (%s)", path, msg);
+
+int merge_submodule(unsigned char result[20], const char *path,
+                   const unsigned char base[20], const unsigned char a[20],
+                   const unsigned char b[20])
+{
+       struct commit *commit_base, *commit_a, *commit_b;
+       int parent_count;
+       struct object_array merges;
+
+       int i;
+
+       /* store a in result in case we fail */
+       hashcpy(result, a);
+
+       /* we can not handle deletion conflicts */
+       if (is_null_sha1(base))
+               return 0;
+       if (is_null_sha1(a))
+               return 0;
+       if (is_null_sha1(b))
+               return 0;
+
+       if (add_submodule_odb(path)) {
+               MERGE_WARNING(path, "not checked out");
+               return 0;
+       }
+
+       if (!(commit_base = lookup_commit_reference(base)) ||
+           !(commit_a = lookup_commit_reference(a)) ||
+           !(commit_b = lookup_commit_reference(b))) {
+               MERGE_WARNING(path, "commits not present");
+               return 0;
+       }
+
+       /* check whether both changes are forward */
+       if (!in_merge_bases(commit_base, &commit_a, 1) ||
+           !in_merge_bases(commit_base, &commit_b, 1)) {
+               MERGE_WARNING(path, "commits don't follow merge-base");
+               return 0;
+       }
+
+       /* Case #1: a is contained in b or vice versa */
+       if (in_merge_bases(commit_a, &commit_b, 1)) {
+               hashcpy(result, b);
+               return 1;
+       }
+       if (in_merge_bases(commit_b, &commit_a, 1)) {
+               hashcpy(result, a);
+               return 1;
+       }
+
+       /*
+        * Case #2: There are one or more merges that contain a and b in
+        * the submodule. If there is only one, then present it as a
+        * suggestion to the user, but leave it marked unmerged so the
+        * user needs to confirm the resolution.
+        */
+
+       /* find commit which merges them */
+       parent_count = find_first_merges(&merges, path, commit_a, commit_b);
+       switch (parent_count) {
+       case 0:
+               MERGE_WARNING(path, "merge following commits not found");
+               break;
+
+       case 1:
+               MERGE_WARNING(path, "not fast-forward");
+               fprintf(stderr, "Found a possible merge resolution "
+                               "for the submodule:\n");
+               print_commit((struct commit *) merges.objects[0].item);
+               fprintf(stderr,
+                       "If this is correct simply add it to the index "
+                       "for example\n"
+                       "by using:\n\n"
+                       "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+                       "which will accept this suggestion.\n",
+                       sha1_to_hex(merges.objects[0].item->sha1), path);
+               break;
+
+       default:
+               MERGE_WARNING(path, "multiple merges found");
+               for (i = 0; i < merges.nr; i++)
+                       print_commit((struct commit *) merges.objects[i].item);
+       }
+
+       free(merges.objects);
+       return 0;
+}
index 6fd3bb40702e5fb905eb4ae519d487e3c9f40073..386f410a66d9c431a61c0e26c773f1b3452f0d09 100644 (file)
@@ -3,11 +3,17 @@
 
 struct diff_options;
 
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+               const char *path);
+void gitmodules_config();
+int parse_submodule_config_option(const char *var, const char *value);
 void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
 void show_submodule_summary(FILE *f, const char *path,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule,
                const char *del, const char *add, const char *reset);
 unsigned is_submodule_modified(const char *path, int ignore_untracked);
+int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
+                   const unsigned char a[20], const unsigned char b[20]);
 
 #endif
index 7dcbb232cd876cb7b976443cc586f60a94ab92bf..4e731dc1e3bef53903f030ee7c63fe7ef7324cb1 100644 (file)
@@ -1,2 +1,3 @@
 /trash directory*
 /test-results
+/.prove
index cf5f9e2e1eb17c442220464d06374e6c529f8629..819b936870300fc8325c0f24f8c68ea8bdaa2727 100644 (file)
@@ -30,6 +30,7 @@ clean:
        $(RM) -r 'trash directory'.* test-results
        $(RM) t????/cvsroot/CVSROOT/?*
        $(RM) -r valgrind/bin
+       $(RM) .prove
 
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
index 0d1183c3e69904e9e3543d757f14f10c629e199b..410499a09645634349d7685e728228f238021c34 100644 (file)
--- a/t/README
+++ b/t/README
@@ -467,6 +467,13 @@ library for your script to use.
    <expected> file.  This behaves like "cmp" but produces more
    helpful output when the test is run with "-v" option.
 
+ - test_path_is_file <file> [<diagnosis>]
+   test_path_is_dir <dir> [<diagnosis>]
+   test_path_is_missing <path> [<diagnosis>]
+
+   Check whether a file/directory exists or doesn't. <diagnosis> will
+   be displayed if the test fails.
+
  - test_when_finished <script>
 
    Prepend <script> to a list of commands to run to clean up
index c3f6676ca2c4a37cbe1cf2779e97a678c1345559..92d6d319428223de9205d4d162e3806aedee855b 100644 (file)
@@ -16,7 +16,6 @@ fi
 GIT_DIR=$PWD/.git
 GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
 SVN_TREE=$GIT_SVN_DIR/svn-tree
-PERL=${PERL:-perl}
 
 svn >/dev/null 2>&1
 if test $? -ne 1
@@ -30,7 +29,7 @@ export svnrepo
 svnconf=$PWD/svnconf
 export svnconf
 
-$PERL -w -e "
+"$PERL_PATH" -w -e "
 use SVN::Core;
 use SVN::Repos;
 \$SVN::Core::VERSION gt '1.1.0' or exit(42);
@@ -130,7 +129,7 @@ stop_httpd () {
 }
 
 convert_to_rev_db () {
-       $PERL -w -- - "$@" <<\EOF
+       "$PERL_PATH" -w -- - "$@" <<\EOF
 use strict;
 @ARGV == 2 or die "Usage: convert_to_rev_db <input> <output>";
 open my $wr, '+>', $ARGV[1] or die "$!: couldn't open: $ARGV[1]";
index 6aefe27593e89d57f075bc0d3dbc5a1104d874b7..6ccf7970916b58748aedcce7e583eed2dee782d3 100644 (file)
@@ -47,6 +47,8 @@ for line in $FAKE_LINES; do
        case $line in
        squash|fixup|edit|reword)
                action="$line";;
+       exec*)
+               echo "$line" | sed 's/_/ /g' >> "$1";;
        "#")
                echo '# comment' >> "$1";;
        ">")
index 53bd7fcc4abbf6cb7db73b43d5dde477f5115f90..de38c7f7aab16a0c2766ef020a3fc03fadece555 100755 (executable)
@@ -48,11 +48,11 @@ test_expect_success 'attribute test' '
        attr_check a/b/g a/b/g &&
        attr_check b/g unspecified &&
        attr_check a/b/h a/b/h &&
-       attr_check a/b/d/g "a/b/d/*"
-       attr_check onoff unset
-       attr_check offon set
-       attr_check no unspecified
-       attr_check a/b/d/no "a/b/d/*"
+       attr_check a/b/d/g "a/b/d/*" &&
+       attr_check onoff unset &&
+       attr_check offon set &&
+       attr_check no unspecified &&
+       attr_check a/b/d/no "a/b/d/*" &&
        attr_check a/b/d/yes unspecified
 
 '
diff --git a/t/t0080-vcs-svn.sh b/t/t0080-vcs-svn.sh
new file mode 100755 (executable)
index 0000000..d3225ad
--- /dev/null
@@ -0,0 +1,171 @@
+#!/bin/sh
+
+test_description='check infrastructure for svn importer'
+
+. ./test-lib.sh
+uint32_max=4294967295
+
+test_expect_success 'obj pool: store data' '
+       cat <<-\EOF >expected &&
+       0
+       1
+       EOF
+
+       test-obj-pool <<-\EOF >actual &&
+       alloc one 16
+       set one 13
+       test one 13
+       reset one
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'obj pool: NULL is offset ~0' '
+       echo "$uint32_max" >expected &&
+       echo null one | test-obj-pool >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'obj pool: out-of-bounds access' '
+       cat <<-EOF >expected &&
+       0
+       0
+       $uint32_max
+       $uint32_max
+       16
+       20
+       $uint32_max
+       EOF
+
+       test-obj-pool <<-\EOF >actual &&
+       alloc one 16
+       alloc two 16
+       offset one 20
+       offset two 20
+       alloc one 5
+       offset one 20
+       free one 1
+       offset one 20
+       reset one
+       reset two
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'obj pool: high-water mark' '
+       cat <<-\EOF >expected &&
+       0
+       0
+       10
+       20
+       20
+       20
+       EOF
+
+       test-obj-pool <<-\EOF >actual &&
+       alloc one 10
+       committed one
+       alloc one 10
+       commit one
+       committed one
+       alloc one 10
+       free one 20
+       committed one
+       reset one
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'line buffer' '
+       echo HELLO >expected1 &&
+       printf "%s\n" "" HELLO >expected2 &&
+       echo >expected3 &&
+       printf "%s\n" "" Q | q_to_nul >expected4 &&
+       printf "%s\n" foo "" >expected5 &&
+       printf "%s\n" "" foo >expected6 &&
+
+       test-line-buffer <<-\EOF >actual1 &&
+       5
+       HELLO
+       EOF
+
+       test-line-buffer <<-\EOF >actual2 &&
+       0
+
+       5
+       HELLO
+       EOF
+
+       q_to_nul <<-\EOF |
+       1
+       Q
+       EOF
+       test-line-buffer >actual3 &&
+
+       q_to_nul <<-\EOF |
+       0
+
+       1
+       Q
+       EOF
+       test-line-buffer >actual4 &&
+
+       test-line-buffer <<-\EOF >actual5 &&
+       5
+       foo
+       EOF
+
+       test-line-buffer <<-\EOF >actual6 &&
+       0
+
+       5
+       foo
+       EOF
+
+       test_cmp expected1 actual1 &&
+       test_cmp expected2 actual2 &&
+       test_cmp expected3 actual3 &&
+       test_cmp expected4 actual4 &&
+       test_cmp expected5 actual5 &&
+       test_cmp expected6 actual6
+'
+
+test_expect_success 'string pool' '
+       echo a does not equal b >expected.differ &&
+       echo a equals a >expected.match &&
+       echo equals equals equals >expected.matchmore &&
+
+       test-string-pool "a,--b" >actual.differ &&
+       test-string-pool "a,a" >actual.match &&
+       test-string-pool "equals-equals" >actual.matchmore &&
+       test_must_fail test-string-pool a,a,a &&
+       test_must_fail test-string-pool a &&
+
+       test_cmp expected.differ actual.differ &&
+       test_cmp expected.match actual.match &&
+       test_cmp expected.matchmore actual.matchmore
+'
+
+test_expect_success 'treap sort' '
+       cat <<-\EOF >unsorted &&
+       68
+       12
+       13
+       13
+       68
+       13
+       13
+       21
+       10
+       11
+       12
+       13
+       13
+       EOF
+       sort unsorted >expected &&
+
+       test-treap <unsorted >actual &&
+       test_cmp expected actual
+'
+
+test_done
index 0c562bb820cb747a8f0d0df1eabb39da0b9eea21..93ca84f9e6d3dfd859d8bd4941f4d2c5a354ab6c 100755 (executable)
@@ -359,7 +359,7 @@ test_expect_success \
 
 test_expect_success \
     'a/b (untracked) vs a, plus c/d case test.' \
-    '! git read-tree -u -m "$treeH" "$treeM" &&
+    'test_must_fail git read-tree -u -m "$treeH" "$treeM" &&
      git ls-files --stage &&
      test -f a/b'
 
index 62246dbf9546b9ee5ad4dbeb0b34de45a1671ac2..9a07de1a5b6d3a98c0db0b24cdd9c3a68cef79b5 100755 (executable)
@@ -1,16 +1,30 @@
 #!/bin/sh
 
-test_description='sparse checkout tests'
+test_description='sparse checkout tests
+
+* (tag: removed, master) removed
+| D    sub/added
+* (HEAD, tag: top) modified and added
+| M    init.t
+| A    sub/added
+* (tag: init) init
+  A    init.t
+'
 
 . ./test-lib.sh
 
-cat >expected <<EOF
-100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0      init.t
-100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      sub/added
-EOF
 test_expect_success 'setup' '
+       cat >expected <<-\EOF &&
+       100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0       init.t
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       sub/added
+       EOF
+       cat >expected.swt <<-\EOF &&
+       H init.t
+       H sub/added
+       EOF
+
        test_commit init &&
-       echo modified >> init.t &&
+       echo modified >>init.t &&
        mkdir sub &&
        touch sub/added &&
        git add init.t sub/added &&
@@ -20,26 +34,22 @@ test_expect_success 'setup' '
        git commit -m removed &&
        git tag removed &&
        git checkout top &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_success 'read-tree without .git/info/sparse-checkout' '
        git read-tree -m -u HEAD &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result
 '
 
 test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
-       echo > .git/info/sparse-checkout
+       echo >.git/info/sparse-checkout
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
@@ -47,9 +57,9 @@ test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
 
 test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
        git config core.sparsecheckout true &&
-       echo > .git/info/sparse-checkout &&
+       echo >.git/info/sparse-checkout &&
        git read-tree --no-sparse-checkout -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
@@ -57,94 +67,113 @@ test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-
 
 test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
        git config core.sparsecheckout true &&
-       echo > .git/info/sparse-checkout &&
+       echo >.git/info/sparse-checkout &&
        test_must_fail git read-tree -m -u HEAD &&
-       git ls-files --stage > result &&
+       git ls-files --stage >result &&
        test_cmp expected result &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-S init.t
-H sub/added
-EOF
 test_expect_success 'match directories with trailing slash' '
+       cat >expected.swt-noinit <<-\EOF &&
+       S init.t
+       H sub/added
+       EOF
+
        echo sub/ > .git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
        git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       test_cmp expected.swt-noinit result &&
        test ! -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-H sub/added
-EOF
 test_expect_failure 'match directories without trailing slash' '
-       echo init.t > .git/info/sparse-checkout &&
-       echo sub >> .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
+       echo sub >>.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
+       git ls-files -t >result &&
        test_cmp expected.swt result &&
        test ! -f init.t &&
        test -f sub/added
 '
 
-cat >expected.swt <<EOF
-H init.t
-S sub/added
-EOF
 test_expect_success 'checkout area changes' '
-       echo init.t > .git/info/sparse-checkout &&
+       cat >expected.swt-nosub <<-\EOF &&
+       H init.t
+       S sub/added
+       EOF
+
+       echo init.t >.git/info/sparse-checkout &&
        git read-tree -m -u HEAD &&
-       git ls-files -t > result &&
-       test_cmp expected.swt result &&
+       git ls-files -t >result &&
+       test_cmp expected.swt-nosub result &&
        test -f init.t &&
        test ! -f sub/added
 '
 
 test_expect_success 'read-tree updates worktree, absent case' '
-       echo sub/added > .git/info/sparse-checkout &&
+       echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
        git read-tree -m -u HEAD^ &&
        test ! -f init.t
 '
 
 test_expect_success 'read-tree updates worktree, dirty case' '
-       echo sub/added > .git/info/sparse-checkout &&
+       echo sub/added >.git/info/sparse-checkout &&
        git checkout -f top &&
-       echo dirty > init.t &&
+       echo dirty >init.t &&
        git read-tree -m -u HEAD^ &&
        grep -q dirty init.t &&
        rm init.t
 '
 
 test_expect_success 'read-tree removes worktree, dirty case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f top &&
-       echo dirty > added &&
+       echo dirty >added &&
        git read-tree -m -u HEAD^ &&
        grep -q dirty added
 '
 
 test_expect_success 'read-tree adds to worktree, absent case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
        git read-tree -u -m HEAD^ &&
        test ! -f sub/added
 '
 
 test_expect_success 'read-tree adds to worktree, dirty case' '
-       echo init.t > .git/info/sparse-checkout &&
+       echo init.t >.git/info/sparse-checkout &&
        git checkout -f removed &&
        mkdir sub &&
-       echo dirty > sub/added &&
+       echo dirty >sub/added &&
        git read-tree -u -m HEAD^ &&
        grep -q dirty sub/added
 '
 
+test_expect_success 'index removal and worktree narrowing at the same time' '
+       >empty &&
+       echo init.t >.git/info/sparse-checkout &&
+       echo sub/added >>.git/info/sparse-checkout &&
+       git checkout -f top &&
+       echo init.t >.git/info/sparse-checkout &&
+       git checkout removed &&
+       git ls-files sub/added >result &&
+       test ! -f sub/added &&
+       test_cmp empty result
+'
+
+test_expect_success 'read-tree --reset removes outside worktree' '
+       >empty &&
+       echo init.t >.git/info/sparse-checkout &&
+       git checkout -f top &&
+       git reset --hard removed &&
+       git ls-files sub/added >result &&
+       test_cmp empty result
+'
+
 test_done
index 64f05080b65c2b9506d1e34748b47ee721026aef..074f2f2e3e57f76b01f16502998e95b493676711 100755 (executable)
@@ -707,19 +707,41 @@ test_expect_success 'set --path' '
        git config --path path.trailingtilde "foo~" &&
        test_cmp expect .git/config'
 
+if test "${HOME+set}"
+then
+       test_set_prereq HOMEVAR
+fi
+
 cat >expect <<EOF
 $HOME/
 /dev/null
 foo~
 EOF
 
-test_expect_success 'get --path' '
+test_expect_success HOMEVAR 'get --path' '
        git config --get --path path.home > result &&
        git config --get --path path.normal >> result &&
        git config --get --path path.trailingtilde >> result &&
        test_cmp expect result
 '
 
+cat >expect <<\EOF
+/dev/null
+foo~
+EOF
+
+test_expect_success 'get --path copes with unset $HOME' '
+       (
+               unset HOME;
+               test_must_fail git config --get --path path.home \
+                       >result 2>msg &&
+               git config --get --path path.normal >>result &&
+               git config --get --path path.trailingtilde >>result
+       ) &&
+       grep "[Ff]ailed to expand.*~/" msg &&
+       test_cmp expect result
+'
+
 rm .git/config
 
 git config quote.leading " test"
index eb45afb018f6e3849204b44cce06ae1a4e2e29aa..782e75d00091ebf1fda93ca5dc8e532289a15638 100755 (executable)
@@ -41,6 +41,23 @@ test_expect_success "check-ref-format --branch @{-1}" '
        refname2=$(git check-ref-format --branch @{-2}) &&
        test "$refname2" = master'
 
+test_expect_success 'check-ref-format --branch from subdir' '
+       mkdir subdir &&
+
+       T=$(git write-tree) &&
+       sha1=$(echo A | git commit-tree $T) &&
+       git update-ref refs/heads/master $sha1 &&
+       git update-ref refs/remotes/origin/master $sha1
+       git checkout master &&
+       git checkout origin/master &&
+       git checkout master &&
+       refname=$(
+               cd subdir &&
+               git check-ref-format --branch @{-1}
+       ) &&
+       test "$refname" = "$sha1"
+'
+
 valid_ref_normalized() {
        test_expect_success "ref name '$1' simplifies to '$2'" "
                refname=\$(git check-ref-format --print '$1') &&
index bd8b60732b06365fa22585eb8e97a3b1e64fdd05..2c8f01f6684cbe492132575209f79371ad761d87 100755 (executable)
 test_description='test separate work tree'
 . ./test-lib.sh
 
-test_rev_parse() {
-       name=$1
-       shift
-
-       test_expect_success "$name: is-bare-repository" \
-       "test '$1' = \"\$(git rev-parse --is-bare-repository)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: is-inside-git-dir" \
-       "test '$1' = \"\$(git rev-parse --is-inside-git-dir)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: is-inside-work-tree" \
-       "test '$1' = \"\$(git rev-parse --is-inside-work-tree)\""
-       shift
-       [ $# -eq 0 ] && return
-
-       test_expect_success "$name: prefix" \
-       "test '$1' = \"\$(git rev-parse --show-prefix)\""
-       shift
-       [ $# -eq 0 ] && return
-}
-
-EMPTY_TREE=$(git write-tree)
-mkdir -p work/sub/dir || exit 1
-mkdir -p work2 || exit 1
-mv .git repo.git || exit 1
-
-say "core.worktree = relative path"
-GIT_DIR=repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-export GIT_DIR GIT_CONFIG
-unset GIT_WORK_TREE
-git config core.worktree ../work
-test_rev_parse 'outside'      false false false
-cd work || exit 1
-GIT_DIR=../repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-GIT_DIR=../../../repo.git
-GIT_CONFIG="$(pwd)"/$GIT_DIR/config
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-say "core.worktree = absolute path"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-git config core.worktree "$(pwd)/work"
-test_rev_parse 'outside'      false false false
-cd work2
-test_rev_parse 'outside2'     false false false
-cd ../work || exit 1
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-say "GIT_WORK_TREE=relative path (override core.worktree)"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-git config core.worktree non-existent
-GIT_WORK_TREE=work
-export GIT_WORK_TREE
-test_rev_parse 'outside'      false false false
-cd work2
-test_rev_parse 'outside'      false false false
-cd ../work || exit 1
-GIT_WORK_TREE=.
-test_rev_parse 'inside'       false false true ''
-cd sub/dir || exit 1
-GIT_WORK_TREE=../..
-test_rev_parse 'subdirectory' false false true sub/dir/
-cd ../../.. || exit 1
-
-mv work repo.git/work
-mv work2 repo.git/work2
-
-say "GIT_WORK_TREE=absolute path, work tree below git dir"
-GIT_DIR=$(pwd)/repo.git
-GIT_CONFIG=$GIT_DIR/config
-GIT_WORK_TREE=$(pwd)/repo.git/work
-test_rev_parse 'outside'              false false false
-cd repo.git || exit 1
-test_rev_parse 'in repo.git'              false true  false
-cd objects || exit 1
-test_rev_parse 'in repo.git/objects'      false true  false
-cd ../work2 || exit 1
-test_rev_parse 'in repo.git/work2'      false true  false
-cd ../work || exit 1
-test_rev_parse 'in repo.git/work'         false true true ''
-cd sub/dir || exit 1
-test_rev_parse 'in repo.git/sub/dir' false true true sub/dir/
-cd ../../../.. || exit 1
-
-test_expect_success 'repo finds its work tree' '
-       (cd repo.git &&
-        : > work/sub/dir/untracked &&
-        test sub/dir/untracked = "$(git ls-files --others)")
-'
-
-test_expect_success 'repo finds its work tree from work tree, too' '
-       (cd repo.git/work/sub/dir &&
-        : > tracked &&
-        git --git-dir=../../.. add tracked &&
-        cd ../../.. &&
-        test sub/dir/tracked = "$(git ls-files)")
+test_expect_success 'setup' '
+       EMPTY_TREE=$(git write-tree) &&
+       EMPTY_BLOB=$(git hash-object -t blob --stdin </dev/null) &&
+       CHANGED_BLOB=$(echo changed | git hash-object -t blob --stdin) &&
+       ZEROES=0000000000000000000000000000000000000000 &&
+       EMPTY_BLOB7=$(echo $EMPTY_BLOB | sed "s/\(.......\).*/\1/") &&
+       CHANGED_BLOB7=$(echo $CHANGED_BLOB | sed "s/\(.......\).*/\1/") &&
+
+       mkdir -p work/sub/dir &&
+       mkdir -p work2 &&
+       mv .git repo.git
+'
+
+test_expect_success 'setup: helper for testing rev-parse' '
+       test_rev_parse() {
+               echo $1 >expected.bare &&
+               echo $2 >expected.inside-git &&
+               echo $3 >expected.inside-worktree &&
+               if test $# -ge 4
+               then
+                       echo $4 >expected.prefix
+               fi &&
+
+               git rev-parse --is-bare-repository >actual.bare &&
+               git rev-parse --is-inside-git-dir >actual.inside-git &&
+               git rev-parse --is-inside-work-tree >actual.inside-worktree &&
+               if test $# -ge 4
+               then
+                       git rev-parse --show-prefix >actual.prefix
+               fi &&
+
+               test_cmp expected.bare actual.bare &&
+               test_cmp expected.inside-git actual.inside-git &&
+               test_cmp expected.inside-worktree actual.inside-worktree &&
+               if test $# -ge 4
+               then
+                       # rev-parse --show-prefix should output
+                       # a single newline when at the top of the work tree,
+                       # but we test for that separately.
+                       test -z "$4" && ! test -s actual.prefix ||
+                       test_cmp expected.prefix actual.prefix
+               fi
+       }
+'
+
+test_expect_success 'setup: core.worktree = relative path' '
+       unset GIT_WORK_TREE;
+       GIT_DIR=repo.git &&
+       GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+       export GIT_DIR GIT_CONFIG &&
+       git config core.worktree ../work
+'
+
+test_expect_success 'outside' '
+       test_rev_parse false false false
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd work &&
+               GIT_DIR=../repo.git &&
+               GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+               test_rev_parse false false true ""
+       )
+'
+
+test_expect_failure 'empty prefix is actually written out' '
+       echo >expected &&
+       (
+               cd work &&
+               GIT_DIR=../repo.git &&
+               GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+               git rev-parse --show-prefix >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd work/sub/dir &&
+               GIT_DIR=../../../repo.git &&
+               GIT_CONFIG="$(pwd)"/$GIT_DIR/config &&
+               test_rev_parse false false true sub/dir/
+       )
+'
+
+test_expect_success 'setup: core.worktree = absolute path' '
+       unset GIT_WORK_TREE;
+       GIT_DIR=$(pwd)/repo.git &&
+       GIT_CONFIG=$GIT_DIR/config &&
+       export GIT_DIR GIT_CONFIG &&
+       git config core.worktree "$(pwd)/work"
+'
+
+test_expect_success 'outside' '
+       test_rev_parse false false false &&
+       (
+               cd work2 &&
+               test_rev_parse false false false
+       )
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd work &&
+               test_rev_parse false false true ""
+       )
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd work/sub/dir &&
+               test_rev_parse false false true sub/dir/
+       )
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=relative (override core.worktree)' '
+       GIT_DIR=$(pwd)/repo.git &&
+       GIT_CONFIG=$GIT_DIR/config &&
+       git config core.worktree non-existent &&
+       GIT_WORK_TREE=work &&
+       export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+       test_rev_parse false false false &&
+       (
+               cd work2 &&
+               test_rev_parse false false false
+       )
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd work &&
+               GIT_WORK_TREE=. &&
+               test_rev_parse false false true ""
+       )
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd work/sub/dir &&
+               GIT_WORK_TREE=../.. &&
+               test_rev_parse false false true sub/dir/
+       )
+'
+
+test_expect_success 'setup: GIT_WORK_TREE=absolute, below git dir' '
+       mv work repo.git/work &&
+       mv work2 repo.git/work2 &&
+       GIT_DIR=$(pwd)/repo.git &&
+       GIT_CONFIG=$GIT_DIR/config &&
+       GIT_WORK_TREE=$(pwd)/repo.git/work &&
+       export GIT_DIR GIT_CONFIG GIT_WORK_TREE
+'
+
+test_expect_success 'outside' '
+       echo outside &&
+       test_rev_parse false false false
+'
+
+test_expect_success 'in repo.git' '
+       (
+               cd repo.git &&
+               test_rev_parse false true false
+       ) &&
+       (
+               cd repo.git/objects &&
+               test_rev_parse false true false
+       ) &&
+       (
+               cd repo.git/work2 &&
+               test_rev_parse false true false
+       )
+'
+
+test_expect_success 'inside work tree' '
+       (
+               cd repo.git/work &&
+               test_rev_parse false true true ""
+       )
+'
+
+test_expect_success 'subdir of work tree' '
+       (
+               cd repo.git/work/sub/dir &&
+               test_rev_parse false true true sub/dir/
+       )
+'
+
+test_expect_success 'find work tree from repo' '
+       echo sub/dir/untracked >expected &&
+       cat <<-\EOF >repo.git/work/.gitignore &&
+       expected.*
+       actual.*
+       .gitignore
+       EOF
+       >repo.git/work/sub/dir/untracked &&
+       (
+               cd repo.git &&
+               git ls-files --others --exclude-standard >../actual
+       ) &&
+       test_cmp expected actual
+'
+
+test_expect_success 'find work tree from work tree' '
+       echo sub/dir/tracked >expected &&
+       >repo.git/work/sub/dir/tracked &&
+       (
+               cd repo.git/work/sub/dir &&
+               git --git-dir=../../.. add tracked
+       ) &&
+       (
+               cd repo.git &&
+               git ls-files >../actual
+       ) &&
+       test_cmp expected actual
 '
 
 test_expect_success '_gently() groks relative GIT_DIR & GIT_WORK_TREE' '
-       (cd repo.git/work/sub/dir &&
-       GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
+       (
+               cd repo.git/work/sub/dir &&
+               GIT_DIR=../../.. &&
+               GIT_WORK_TREE=../.. &&
+               GIT_PAGER= &&
+               export GIT_DIR GIT_WORK_TREE GIT_PAGER &&
+
                git diff --exit-code tracked &&
-       echo changed > tracked &&
-       ! GIT_DIR=../../.. GIT_WORK_TREE=../.. GIT_PAGER= \
-               git diff --exit-code tracked)
-'
-cat > diff-index-cached.expected <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 A     sub/dir/tracked
-EOF
-cat > diff-index.expected <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 A     sub/dir/tracked
-EOF
-
-
-test_expect_success 'git diff-index' '
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-index $EMPTY_TREE > result &&
-       test_cmp diff-index.expected result &&
-       GIT_DIR=repo.git git diff-index --cached $EMPTY_TREE > result &&
-       test_cmp diff-index-cached.expected result
-'
-cat >diff-files.expected <<\EOF
-:100644 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0000000000000000000000000000000000000000 M     sub/dir/tracked
-EOF
-
-test_expect_success 'git diff-files' '
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff-files > result &&
-       test_cmp diff-files.expected result
-'
-
-cat >diff-TREE.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-new file mode 100644
-index 0000000..5ea2ed4
---- /dev/null
-+++ b/sub/dir/tracked
-@@ -0,0 +1 @@
-+changed
-EOF
-cat >diff-TREE-cached.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-new file mode 100644
-index 0000000..e69de29
-EOF
-cat >diff-FILES.expected <<\EOF
-diff --git a/sub/dir/tracked b/sub/dir/tracked
-index e69de29..5ea2ed4 100644
---- a/sub/dir/tracked
-+++ b/sub/dir/tracked
-@@ -0,0 +1 @@
-+changed
-EOF
-
-test_expect_success 'git diff' '
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff $EMPTY_TREE > result &&
-       test_cmp diff-TREE.expected result &&
-       GIT_DIR=repo.git git diff --cached $EMPTY_TREE > result &&
-       test_cmp diff-TREE-cached.expected result &&
-       GIT_DIR=repo.git GIT_WORK_TREE=repo.git/work git diff > result &&
-       test_cmp diff-FILES.expected result
+               echo changed >tracked &&
+               test_must_fail git diff --exit-code tracked
+       )
+'
+
+test_expect_success 'diff-index respects work tree under .git dir' '
+       cat >diff-index-cached.expected <<-EOF &&
+       :000000 100644 $ZEROES $EMPTY_BLOB A    sub/dir/tracked
+       EOF
+       cat >diff-index.expected <<-EOF &&
+       :000000 100644 $ZEROES $ZEROES A        sub/dir/tracked
+       EOF
+
+       (
+               GIT_DIR=repo.git &&
+               GIT_WORK_TREE=repo.git/work &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git diff-index $EMPTY_TREE >diff-index.actual &&
+               git diff-index --cached $EMPTY_TREE >diff-index-cached.actual
+       ) &&
+       test_cmp diff-index.expected diff-index.actual &&
+       test_cmp diff-index-cached.expected diff-index-cached.actual
+'
+
+test_expect_success 'diff-files respects work tree under .git dir' '
+       cat >diff-files.expected <<-EOF &&
+       :100644 100644 $EMPTY_BLOB $ZEROES M    sub/dir/tracked
+       EOF
+
+       (
+               GIT_DIR=repo.git &&
+               GIT_WORK_TREE=repo.git/work &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git diff-files >diff-files.actual
+       ) &&
+       test_cmp diff-files.expected diff-files.actual
+'
+
+test_expect_success 'git diff respects work tree under .git dir' '
+       cat >diff-TREE.expected <<-EOF &&
+       diff --git a/sub/dir/tracked b/sub/dir/tracked
+       new file mode 100644
+       index 0000000..$CHANGED_BLOB7
+       --- /dev/null
+       +++ b/sub/dir/tracked
+       @@ -0,0 +1 @@
+       +changed
+       EOF
+       cat >diff-TREE-cached.expected <<-EOF &&
+       diff --git a/sub/dir/tracked b/sub/dir/tracked
+       new file mode 100644
+       index 0000000..$EMPTY_BLOB7
+       EOF
+       cat >diff-FILES.expected <<-EOF &&
+       diff --git a/sub/dir/tracked b/sub/dir/tracked
+       index $EMPTY_BLOB7..$CHANGED_BLOB7 100644
+       --- a/sub/dir/tracked
+       +++ b/sub/dir/tracked
+       @@ -0,0 +1 @@
+       +changed
+       EOF
+
+       (
+               GIT_DIR=repo.git &&
+               GIT_WORK_TREE=repo.git/work &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git diff $EMPTY_TREE >diff-TREE.actual &&
+               git diff --cached $EMPTY_TREE >diff-TREE-cached.actual &&
+               git diff >diff-FILES.actual
+       ) &&
+       test_cmp diff-TREE.expected diff-TREE.actual &&
+       test_cmp diff-TREE-cached.expected diff-TREE-cached.actual &&
+       test_cmp diff-FILES.expected diff-FILES.actual
 '
 
 test_expect_success 'git grep' '
-       (cd repo.git/work/sub &&
-       GIT_DIR=../.. GIT_WORK_TREE=.. git grep -l changed | grep dir/tracked)
+       echo dir/tracked >expected.grep &&
+       (
+               cd repo.git/work/sub &&
+               GIT_DIR=../.. &&
+               GIT_WORK_TREE=.. &&
+               export GIT_DIR GIT_WORK_TREE &&
+               git grep -l changed >../../../actual.grep
+       ) &&
+       test_cmp expected.grep actual.grep
 '
 
 test_expect_success 'git commit' '
@@ -191,14 +328,14 @@ test_expect_success 'git commit' '
 
 test_expect_success 'absolute pathspec should fail gracefully' '
        (
-               cd repo.git || exit 1
-               git config --unset core.worktree
+               cd repo.git &&
+               test_might_fail git config --unset core.worktree &&
                test_must_fail git log HEAD -- /home
        )
 '
 
 test_expect_success 'make_relative_path handles double slashes in GIT_DIR' '
-       : > dummy_file
+       >dummy_file
        echo git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file &&
        git --git-dir="$(pwd)//repo.git" --work-tree="$(pwd)" add dummy_file
 '
diff --git a/t/t2018-checkout-branch.sh b/t/t2018-checkout-branch.sh
new file mode 100755 (executable)
index 0000000..fa69016
--- /dev/null
@@ -0,0 +1,172 @@
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+#   1) we are on the specified branch, <branch>;
+#   2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+       exp_branch=$1 &&
+       exp_ref="refs/heads/$exp_branch" &&
+
+       # if <sha> is not specified, use HEAD.
+       exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+       # default options for git checkout: -b
+       if [ -z "$3" ]; then
+               opts="-b"
+       else
+               opts="$3"
+       fi
+
+       git checkout $opts $exp_branch $exp_sha &&
+
+       test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+       test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+       ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+       echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+       ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+       echo >file2 file2 &&
+       git add file2
+}
+
+test_expect_success 'setup' '
+       test_commit initial file1 &&
+       HEAD1=$(git rev-parse --verify HEAD) &&
+
+       test_commit change1 file1 &&
+       HEAD2=$(git rev-parse --verify HEAD) &&
+
+       git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+       do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+       git checkout branch1 &&
+       git branch -D branch2 &&
+
+       do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
+       git checkout branch1 &&
+
+       # clean up from previous test
+       git branch -D branch2 &&
+
+       setup_dirty_unmergeable &&
+       test_must_fail do_checkout branch2 $HEAD1 &&
+       test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+       # still dirty and on branch1
+       do_checkout branch2 $HEAD1 "-f -b" &&
+       test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
+       git checkout branch1 &&
+
+       # clean up from previous test
+       git branch -D branch2 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 &&
+       test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
+       # clean up from previous test
+       git reset --hard &&
+
+       git checkout branch1 &&
+
+       # clean up from previous test
+       git branch -D branch2 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 "-f -b" &&
+       test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+       git reset --hard HEAD &&
+
+       test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
+       git checkout branch1 &&
+
+       do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
+       git checkout $(git rev-parse --verify HEAD) &&
+
+       do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '
+       git checkout branch1 &&
+
+       do_checkout branch2 $HEAD1 -B
+'
+
+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '
+       git checkout branch1 &&
+
+       setup_dirty_unmergeable &&
+       test_must_fail do_checkout branch2 $HEAD1 -B &&
+       test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
+       # still dirty and on branch1
+       do_checkout branch2 $HEAD1 "-f -B" &&
+       test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+       git checkout branch1 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 -B &&
+       test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
+       # clean up from previous test
+       git reset --hard &&
+
+       git checkout branch1 &&
+
+       setup_dirty_mergeable &&
+       do_checkout branch2 $HEAD1 "-f -B" &&
+       test_must_fail test_dirty_mergeable
+'
+
+test_done
index d541544537d47587abad8d27565cf9197387cfd6..efe2900a372601848248de33188bda292185ba40 100755 (executable)
@@ -294,7 +294,7 @@ test_expect_success 'fail if the index has unresolved entries' '
        grep "You have not concluded your merge" out &&
        rm -f .git/MERGE_HEAD &&
        test_must_fail git merge "$c5" 2> out &&
-       grep "Your local changes to .* would be overwritten by merge." out
+       grep "Your local changes to the following files would be overwritten by merge:" out
 '
 
 test_expect_success 'merge-recursive remove conflict' '
index 525174013c4c33eab5bdbde8831d43f1ddbaeaae..cd04361df811d329e269ac1d5eb0b0f03baa74d8 100755 (executable)
@@ -60,6 +60,12 @@ test_expect_success 'see if git pack-refs --prune remove ref files' '
      ! test -f .git/refs/heads/f
 '
 
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+     git branch r/s/t &&
+     git pack-refs --all --prune &&
+     ! test -e .git/refs/heads/r
+'
+
 test_expect_success \
     'git branch g should work when git branch g/h has been deleted' \
     'git branch g/h &&
index 1d82f79ee07e8f9317bc23c49cdf941af36ce3ff..96b75813d73b3fd34a3c96aa9e977594319adb1d 100755 (executable)
@@ -299,7 +299,7 @@ cat expect-F >> expect-rm-F
 test_expect_success 'verify note removal with -F /dev/null' '
        git log -4 > output &&
        test_cmp expect-rm-F output &&
-       ! git notes show
+       test_must_fail git notes show
 '
 
 test_expect_success 'do not create empty note with -m "" (setup)' '
@@ -309,7 +309,7 @@ test_expect_success 'do not create empty note with -m "" (setup)' '
 test_expect_success 'verify non-creation of note with -m ""' '
        git log -4 > output &&
        test_cmp expect-rm-F output &&
-       ! git notes show
+       test_must_fail git notes show
 '
 
 cat > expect-combine_m_and_F << EOF
@@ -357,7 +357,7 @@ cat expect-multiline >> expect-rm-remove
 test_expect_success 'verify note removal with "git notes remove"' '
        git log -4 > output &&
        test_cmp expect-rm-remove output &&
-       ! git notes show HEAD^
+       test_must_fail git notes show HEAD^
 '
 
 cat > expect << EOF
index 361a10aeb17108049bf0e47b71153800727edd87..8ab333dbd949fe781b5c13b3deab665544763a88 100755 (executable)
@@ -98,7 +98,7 @@ time_notes () {
        for mode in no-notes notes
        do
                echo $mode
-               /usr/bin/time sh ../time_notes $mode $1
+               /usr/bin/time "$SHELL_PATH" ../time_notes $mode $1
        done
 }
 
index b4554041b49d1ab7b56430f2e85b76a23d360fb4..c4282179b387c75e084d05075a7fd365c3220cdc 100755 (executable)
@@ -67,7 +67,7 @@ test_expect_success 'remove some commits' '
 
 test_expect_success 'verify that commits are gone' '
 
-       ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
        git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
@@ -106,7 +106,7 @@ test_expect_success 'prune notes' '
 
 test_expect_success 'verify that notes are gone' '
 
-       ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
        git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
@@ -130,8 +130,8 @@ test_expect_success 'prune -v notes' '
 
 test_expect_success 'verify that notes are gone' '
 
-       ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+       test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
        git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
 '
 
index d98c7b5571245ccfebf19547186c3cf116a13cfe..349eebd54268c927249322dc22fc478869860f7c 100755 (executable)
@@ -14,140 +14,165 @@ GIT_AUTHOR_NAME=author@name
 GIT_AUTHOR_EMAIL=bogus@email@address
 export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 
-test_expect_success \
-    'prepare repository with topic branches' \
-    'git config core.logAllRefUpdates true &&
-     echo First > A &&
-     git update-index --add A &&
-     git commit -m "Add A." &&
-     git checkout -b my-topic-branch &&
-     echo Second > B &&
-     git update-index --add B &&
-     git commit -m "Add B." &&
-     git checkout -f master &&
-     echo Third >> A &&
-     git update-index A &&
-     git commit -m "Modify A." &&
-     git checkout -b side my-topic-branch &&
-     echo Side >> C &&
-     git add C &&
-     git commit -m "Add C" &&
-     git checkout -b nonlinear my-topic-branch &&
-     echo Edit >> B &&
-     git add B &&
-     git commit -m "Modify B" &&
-     git merge side &&
-     git checkout -b upstream-merged-nonlinear &&
-     git merge master &&
-     git checkout -f my-topic-branch &&
-     git tag topic
+test_expect_success 'prepare repository with topic branches' '
+       git config core.logAllRefUpdates true &&
+       echo First >A &&
+       git update-index --add A &&
+       git commit -m "Add A." &&
+       git checkout -b force-3way &&
+       echo Dummy >Y &&
+       git update-index --add Y &&
+       git commit -m "Add Y." &&
+       git checkout -b filemove &&
+       git reset --soft master &&
+       mkdir D &&
+       git mv A D/A &&
+       git commit -m "Move A." &&
+       git checkout -b my-topic-branch master &&
+       echo Second >B &&
+       git update-index --add B &&
+       git commit -m "Add B." &&
+       git checkout -f master &&
+       echo Third >>A &&
+       git update-index A &&
+       git commit -m "Modify A." &&
+       git checkout -b side my-topic-branch &&
+       echo Side >>C &&
+       git add C &&
+       git commit -m "Add C" &&
+       git checkout -b nonlinear my-topic-branch &&
+       echo Edit >>B &&
+       git add B &&
+       git commit -m "Modify B" &&
+       git merge side &&
+       git checkout -b upstream-merged-nonlinear &&
+       git merge master &&
+       git checkout -f my-topic-branch &&
+       git tag topic
 '
 
 test_expect_success 'rebase on dirty worktree' '
-     echo dirty >> A &&
-     test_must_fail git rebase master'
+       echo dirty >>A &&
+       test_must_fail git rebase master
+'
 
 test_expect_success 'rebase on dirty cache' '
-     git add A &&
-     test_must_fail git rebase master'
+       git add A &&
+       test_must_fail git rebase master
+'
 
 test_expect_success 'rebase against master' '
-     git reset --hard HEAD &&
-     git rebase master'
+       git reset --hard HEAD &&
+       git rebase master
+'
 
 test_expect_success 'rebase against master twice' '
-     git rebase master >out &&
-     grep "Current branch my-topic-branch is up to date" out
+       git rebase master >out &&
+       grep "Current branch my-topic-branch is up to date" out
 '
 
 test_expect_success 'rebase against master twice with --force' '
-     git rebase --force-rebase master >out &&
-     grep "Current branch my-topic-branch is up to date, rebase forced" out
+       git rebase --force-rebase master >out &&
+       grep "Current branch my-topic-branch is up to date, rebase forced" out
 '
 
 test_expect_success 'rebase against master twice from another branch' '
-     git checkout my-topic-branch^ &&
-     git rebase master my-topic-branch >out &&
-     grep "Current branch my-topic-branch is up to date" out
+       git checkout my-topic-branch^ &&
+       git rebase master my-topic-branch >out &&
+       grep "Current branch my-topic-branch is up to date" out
 '
 
 test_expect_success 'rebase fast-forward to master' '
-     git checkout my-topic-branch^ &&
-     git rebase my-topic-branch >out &&
-     grep "Fast-forwarded HEAD to my-topic-branch" out
+       git checkout my-topic-branch^ &&
+       git rebase my-topic-branch >out &&
+       grep "Fast-forwarded HEAD to my-topic-branch" out
 '
 
-test_expect_success \
-    'the rebase operation should not have destroyed author information' \
-    '! (git log | grep "Author:" | grep "<>")'
+test_expect_success 'the rebase operation should not have destroyed author information' '
+       ! (git log | grep "Author:" | grep "<>")
+'
 
-test_expect_success \
-    'the rebase operation should not have destroyed author information (2)' \
-    "git log -1 | grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'"
+test_expect_success 'the rebase operation should not have destroyed author information (2)' "
+       git log -1 |
+       grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'
+"
 
 test_expect_success 'HEAD was detached during rebase' '
-     test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+       test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
 '
 
 test_expect_success 'rebase after merge master' '
-     git reset --hard topic &&
-     git merge master &&
-     git rebase master &&
-     ! (git show | grep "^Merge:")
+       git reset --hard topic &&
+       git merge master &&
+       git rebase master &&
+       ! (git show | grep "^Merge:")
 '
 
 test_expect_success 'rebase of history with merges is linearized' '
-     git checkout nonlinear &&
-     test 4 = $(git rev-list master.. | wc -l) &&
-     git rebase master &&
-     test 3 = $(git rev-list master.. | wc -l)
+       git checkout nonlinear &&
+       test 4 = $(git rev-list master.. | wc -l) &&
+       git rebase master &&
+       test 3 = $(git rev-list master.. | wc -l)
 '
 
-test_expect_success \
-    'rebase of history with merges after upstream merge is linearized' '
-     git checkout upstream-merged-nonlinear &&
-     test 5 = $(git rev-list master.. | wc -l) &&
-     git rebase master &&
-     test 3 = $(git rev-list master.. | wc -l)
+test_expect_success 'rebase of history with merges after upstream merge is linearized' '
+       git checkout upstream-merged-nonlinear &&
+       test 5 = $(git rev-list master.. | wc -l) &&
+       git rebase master &&
+       test 3 = $(git rev-list master.. | wc -l)
 '
 
 test_expect_success 'rebase a single mode change' '
-     git checkout master &&
-     echo 1 > X &&
-     git add X &&
-     test_tick &&
-     git commit -m prepare &&
-     git checkout -b modechange HEAD^ &&
-     echo 1 > X &&
-     git add X &&
-     test_chmod +x A &&
-     test_tick &&
-     git commit -m modechange &&
-     GIT_TRACE=1 git rebase master
+       git checkout master &&
+       echo 1 >X &&
+       git add X &&
+       test_tick &&
+       git commit -m prepare &&
+       git checkout -b modechange HEAD^ &&
+       echo 1 >X &&
+       git add X &&
+       test_chmod +x A &&
+       test_tick &&
+       git commit -m modechange &&
+       GIT_TRACE=1 git rebase master
+'
+
+test_expect_success 'rebase is not broken by diff.renames' '
+       git config diff.renames copies &&
+       test_when_finished "git config --unset diff.renames" &&
+       git checkout filemove &&
+       GIT_TRACE=1 git rebase force-3way
+'
+
+test_expect_success 'setup: recover' '
+       test_might_fail git rebase --abort &&
+       git reset --hard &&
+       git checkout modechange
 '
 
 test_expect_success 'Show verbose error when HEAD could not be detached' '
-     : > B &&
-     test_must_fail git rebase topic 2> output.err > output.out &&
-     grep "Untracked working tree file .B. would be overwritten" output.err
+       >B &&
+       test_must_fail git rebase topic 2>output.err >output.out &&
+       grep "The following untracked working tree files would be overwritten by checkout:" output.err &&
+       grep B output.err
 '
 rm -f B
 
 test_expect_success 'dump usage when upstream arg is missing' '
-     git checkout -b usage topic &&
-     test_must_fail git rebase 2>error1 &&
-     grep "[Uu]sage" error1 &&
-     test_must_fail git rebase --abort 2>error2 &&
-     grep "No rebase in progress" error2 &&
-     test_must_fail git rebase --onto master 2>error3 &&
-     grep "[Uu]sage" error3 &&
-     ! grep "can.t shift" error3
+       git checkout -b usage topic &&
+       test_must_fail git rebase 2>error1 &&
+       grep "[Uu]sage" error1 &&
+       test_must_fail git rebase --abort 2>error2 &&
+       grep "No rebase in progress" error2 &&
+       test_must_fail git rebase --onto master 2>error3 &&
+       grep "[Uu]sage" error3 &&
+       ! grep "can.t shift" error3
 '
 
 test_expect_success 'rebase -q is quiet' '
-     git checkout -b quiet topic &&
-     git rebase -q master > output.out 2>&1 &&
-     test ! -s output.out
+       git checkout -b quiet topic &&
+       git rebase -q master >output.out 2>&1 &&
+       test ! -s output.out
 '
 
 test_expect_success 'Rebase a commit that sprinkles CRs in' '
index 7b7d07269ae35f56dd02a223f350ae0da97bae85..2bea65634a1790ceecf824309c1fbb66250f58d5 100755 (executable)
@@ -74,6 +74,15 @@ test_expect_success 'rebase the other way' '
        git rebase --merge side
 '
 
+test_expect_success 'rebase -Xtheirs' '
+       git checkout -b conflicting master~2 &&
+       echo "AB $T" >> original &&
+       git commit -mconflicting original &&
+       git rebase -Xtheirs master &&
+       grep AB original &&
+       ! grep 11 original
+'
+
 test_expect_success 'merge and rebase should match' '
        git diff-tree -r test-rebase test-merge >difference &&
        if test -s difference
index 47ca88fc5231a5e0689b43a35eb660285a73190d..af3b663aeee8354c6f8f2dff05666f6d70c07055 100755 (executable)
@@ -64,6 +64,67 @@ test_expect_success 'setup' '
        done
 '
 
+# "exec" commands are ran with the user shell by default, but this may
+# be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work
+# to create a file. Unseting SHELL avoids such non-portable behavior
+# in tests.
+SHELL=
+
+test_expect_success 'rebase -i with the exec command' '
+       git checkout master &&
+       (
+       FAKE_LINES="1 exec_>touch-one
+               2 exec_>touch-two exec_false exec_>touch-three
+               3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" &&
+       export FAKE_LINES &&
+       test_must_fail git rebase -i A
+       ) &&
+       test_path_is_file touch-one &&
+       test_path_is_file touch-two &&
+       test_path_is_missing touch-three " (should have stopped before)" &&
+       test $(git rev-parse C) = $(git rev-parse HEAD) || {
+               echo "Stopped at wrong revision:"
+               echo "($(git describe --tags HEAD) instead of C)"
+               false
+       } &&
+       git rebase --continue &&
+       test_path_is_file touch-three &&
+       test_path_is_file "touch-file  name with spaces" &&
+       test_path_is_file touch-after-semicolon &&
+       test $(git rev-parse master) = $(git rev-parse HEAD) || {
+               echo "Stopped at wrong revision:"
+               echo "($(git describe --tags HEAD) instead of master)"
+               false
+       } &&
+       rm -f touch-*
+'
+
+test_expect_success 'rebase -i with the exec command runs from tree root' '
+       git checkout master &&
+       mkdir subdir && cd subdir &&
+       FAKE_LINES="1 exec_>touch-subdir" \
+               git rebase -i HEAD^ &&
+       cd .. &&
+       test_path_is_file touch-subdir &&
+       rm -fr subdir
+'
+
+test_expect_success 'rebase -i with the exec command checks tree cleanness' '
+       git checkout master &&
+       (
+       FAKE_LINES="exec_echo_foo_>file1 1" &&
+       export FAKE_LINES &&
+       test_must_fail git rebase -i HEAD^
+       ) &&
+       test $(git rev-parse master^) = $(git rev-parse HEAD) || {
+               echo "Stopped at wrong revision:"
+               echo "($(git describe --tags HEAD) instead of master^)"
+               false
+       } &&
+       git reset --hard &&
+       git rebase --continue
+'
+
 test_expect_success 'no changes are a nop' '
        git checkout branch2 &&
        git rebase -i F &&
@@ -143,16 +204,17 @@ test_expect_success 'abort' '
        git rebase --abort &&
        test $(git rev-parse new-branch1) = $(git rev-parse HEAD) &&
        test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" &&
-       ! test -d .git/rebase-merge
+       test_path_is_missing .git/rebase-merge
 '
 
 test_expect_success 'abort with error when new base cannot be checked out' '
        git rm --cached file1 &&
        git commit -m "remove file in base" &&
        test_must_fail git rebase -i master > output 2>&1 &&
-       grep "Untracked working tree file .file1. would be overwritten" \
+       grep "The following untracked working tree files would be overwritten by checkout:" \
                output &&
-       ! test -d .git/rebase-merge &&
+       grep "file1" output &&
+       test_path_is_missing .git/rebase-merge &&
        git reset --hard HEAD^
 '
 
@@ -637,13 +699,19 @@ test_expect_success 'set up commits with funny messages' '
        git commit -a -m "end with slash\\" &&
        echo >>file1 &&
        test_tick &&
+       git commit -a -m "something (\000) that looks like octal" &&
+       echo >>file1 &&
+       test_tick &&
+       git commit -a -m "something (\n) that looks like a newline" &&
+       echo >>file1 &&
+       test_tick &&
        git commit -a -m "another commit"
 '
 
 test_expect_success 'rebase-i history with funny messages' '
        git rev-list A..funny >expect &&
        test_tick &&
-       FAKE_LINES="1 2" git rebase -i A &&
+       FAKE_LINES="1 2 3 4" git rebase -i A &&
        git rev-list A.. >actual &&
        test_cmp expect actual
 '
index 2999e78937f31a45e9e2ea925f69ac00f157503f..fbb3f2e0dfcf1a0673dbd2022a4ed843990fce52 100755 (executable)
@@ -38,7 +38,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                git rebase --abort &&
                test $(git rev-parse to-rebase) = $(git rev-parse pre-rebase) &&
                test ! -d "$dotest"
@@ -49,7 +49,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                test_must_fail git rebase --skip &&
                test $(git rev-parse HEAD) = $(git rev-parse master) &&
                git rebase --abort &&
@@ -62,7 +62,7 @@ testrebase() {
                # Clean up the state from the previous one
                git reset --hard pre-rebase &&
                test_must_fail git rebase$type master &&
-               test -d "$dotest" &&
+               test_path_is_dir "$dotest" &&
                echo c > a &&
                echo d >> a &&
                git add a &&
index c49143a1a45d6949253e2daf0e57f60029041e60..6f73b95558c85264d073d42f67c407967c4d5c69 100755 (executable)
@@ -43,11 +43,11 @@ test_expect_success 'setup' '
 # G2 = same changes as G
 test_expect_success 'skip same-resolution merges with -p' '
        git checkout H &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit L file1 23 &&
        git checkout I &&
        test_commit G2 file1 3 &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit J file1 23 &&
        test_commit K file7 file7 &&
        git rebase -i -p L &&
@@ -65,11 +65,11 @@ test_expect_success 'skip same-resolution merges with -p' '
 # G2 = different changes as G
 test_expect_success 'keep different-resolution merges with -p' '
        git checkout H &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit L2 file1 23 &&
        git checkout I &&
        test_commit G3 file1 4 &&
-       ! git merge E &&
+       test_must_fail git merge E &&
        test_commit J2 file1 24 &&
        test_commit K2 file7 file7 &&
        test_must_fail git rebase -i -p L2 &&
index b63f4e2d677b6b4827d39a51a3c39719a8ab5b4f..37cb89ab53055099db3d5f774d00ade1c4da6c69 100755 (executable)
@@ -21,38 +21,62 @@ test_expect_success setup '
        git tag base
 '
 
-test_expect_success 'auto fixup' '
+test_auto_fixup() {
        git reset --hard base &&
        echo 1 >file1 &&
        git add -u &&
        test_tick &&
        git commit -m "fixup! first"
 
-       git tag final-fixup &&
+       git tag $1 &&
        test_tick &&
-       git rebase --autosquash -i HEAD^^^ &&
+       git rebase $2 -i HEAD^^^ &&
        git log --oneline >actual &&
        test 3 = $(wc -l <actual) &&
-       git diff --exit-code final-fixup &&
+       git diff --exit-code $1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto fixup (option)' '
+       test_auto_fixup final-fixup-option --autosquash
+'
+
+test_expect_success 'auto fixup (config)' '
+       git config rebase.autosquash true &&
+       test_auto_fixup final-fixup-config-true &&
+       test_must_fail test_auto_fixup fixup-config-true-no --no-autosquash &&
+       git config rebase.autosquash false &&
+       test_must_fail test_auto_fixup final-fixup-config-false
 '
 
-test_expect_success 'auto squash' '
+test_auto_squash() {
        git reset --hard base &&
        echo 1 >file1 &&
        git add -u &&
        test_tick &&
        git commit -m "squash! first"
 
-       git tag final-squash &&
+       git tag $1 &&
        test_tick &&
-       git rebase --autosquash -i HEAD^^^ &&
+       git rebase $2 -i HEAD^^^ &&
        git log --oneline >actual &&
        test 3 = $(wc -l <actual) &&
-       git diff --exit-code final-squash &&
+       git diff --exit-code $1 &&
        test 1 = "$(git cat-file blob HEAD^:file1)" &&
        test 2 = $(git cat-file commit HEAD^ | grep first | wc -l)
+}
+
+test_expect_success 'auto squash (option)' '
+       test_auto_squash final-squash --autosquash
+'
+
+test_expect_success 'auto squash (config)' '
+       git config rebase.autosquash true &&
+       test_auto_squash final-squash-config-true &&
+       test_must_fail test_auto_squash squash-config-true-no --no-autosquash &&
+       git config rebase.autosquash false &&
+       test_must_fail test_auto_squash final-squash-config-false
 '
 
 test_expect_success 'misspelled auto squash' '
diff --git a/t/t3418-rebase-continue.sh b/t/t3418-rebase-continue.sh
new file mode 100755 (executable)
index 0000000..3b0d273
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description='git rebase --continue tests'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+set_fake_editor
+
+test_expect_success 'setup' '
+       test_commit "commit-new-file-F1" F1 1 &&
+       test_commit "commit-new-file-F2" F2 2 &&
+
+       git checkout -b topic HEAD^ &&
+       test_commit "commit-new-file-F2-on-topic-branch" F2 22 &&
+
+       git checkout master
+'
+
+test_expect_success 'interactive rebase --continue works with touched file' '
+       rm -fr .git/rebase-* &&
+       git reset --hard &&
+       git checkout master &&
+
+       FAKE_LINES="edit 1" git rebase -i HEAD^ &&
+       test-chmtime =-60 F1 &&
+       git rebase --continue
+'
+
+test_expect_success 'non-interactive rebase --continue works with touched file' '
+       rm -fr .git/rebase-* &&
+       git reset --hard &&
+       git checkout master &&
+
+       test_must_fail git rebase --onto master master topic &&
+       echo "Resolved" >F2 &&
+       git add F2 &&
+       test-chmtime =-60 F1 &&
+       git rebase --continue
+'
+
+test_done
index e51e505a9fb902ec7d4cedfa32052f03a04e612e..c10b28cf5731705b437793a58f5acd6c605ad579 100755 (executable)
@@ -13,11 +13,29 @@ test_expect_success setup '
 
        git checkout -b empty-branch &&
        test_tick &&
-       git commit --allow-empty -m "empty"
+       git commit --allow-empty -m "empty" &&
+
+       echo third >> file1 &&
+       git add file1 &&
+       test_tick &&
+       git commit --allow-empty-message -m ""
 
 '
 
 test_expect_success 'cherry-pick an empty commit' '
+       git checkout master && {
+               git cherry-pick empty-branch^
+               test "$?" = 1
+       }
+'
+
+test_expect_success 'index lockfile was removed' '
+
+       test ! -f .git/index.lock
+
+'
+
+test_expect_success 'cherry-pick a commit with an empty message' '
        git checkout master && {
                git cherry-pick empty-branch
                test "$?" = 1
index e25cf8039a310e325c23e85ce5fc845a0f681200..607bf25d8ff7720407c2b15e4808c575b5f36093 100755 (executable)
@@ -38,6 +38,26 @@ test_expect_success 'failed cherry-pick does not advance HEAD' '
        test "$head" = "$newhead"
 '
 
+test_expect_success 'advice from failed cherry-pick' "
+       git checkout -f initial^0 &&
+       git read-tree -u --reset HEAD &&
+       git clean -d -f -f -q -x &&
+
+       git update-index --refresh &&
+       git diff-index --exit-code HEAD &&
+
+       picked=\$(git rev-parse --short picked) &&
+       cat <<-EOF >expected &&
+       error: could not apply \$picked... picked
+       hint: after resolving the conflicts, mark the corrected paths
+       hint: with 'git add <paths>' or 'git rm <paths>'
+       hint: and commit the result with 'git commit -c \$picked'
+       EOF
+       test_must_fail git cherry-pick picked 2>actual &&
+
+       test_cmp expected actual
+"
+
 test_expect_success 'failed cherry-pick produces dirty index' '
 
        git checkout -f initial^0 &&
index f90ed3da3e0cb93b63d32d8002dc6d9bd3596a65..8e09fd0319c95cbd4d30c461f00fee5f52e27cbd 100755 (executable)
@@ -4,6 +4,18 @@ test_description='test cherry-picking many commits'
 
 . ./test-lib.sh
 
+check_head_differs_from() {
+       head=$(git rev-parse --verify HEAD) &&
+       arg=$(git rev-parse --verify "$1") &&
+       test "$head" != "$arg"
+}
+
+check_head_equals() {
+       head=$(git rev-parse --verify HEAD) &&
+       arg=$(git rev-parse --verify "$1") &&
+       test "$head" = "$arg"
+}
+
 test_expect_success setup '
        echo first > file1 &&
        git add file1 &&
@@ -23,13 +35,55 @@ test_expect_success setup '
 '
 
 test_expect_success 'cherry-pick first..fourth works' '
+       cat <<-\EOF >expected &&
+       [master OBJID] second
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       [master OBJID] third
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       [master OBJID] fourth
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       EOF
+
+       git checkout -f master &&
+       git reset --hard first &&
+       test_tick &&
+       git cherry-pick first..fourth >actual &&
+       git diff --quiet other &&
+       git diff --quiet HEAD other &&
+
+       sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+       test_cmp expected actual.fuzzy &&
+       check_head_differs_from fourth
+'
+
+test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
+       cat <<-\EOF >expected &&
+       Trying simple merge.
+       [master OBJID] second
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       Trying simple merge.
+       [master OBJID] third
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       Trying simple merge.
+       [master OBJID] fourth
+        Author: A U Thor <author@example.com>
+        1 files changed, 1 insertions(+), 0 deletions(-)
+       EOF
+
        git checkout -f master &&
        git reset --hard first &&
        test_tick &&
-       git cherry-pick first..fourth &&
+       git cherry-pick --strategy resolve first..fourth >actual &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+       sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
+       test_cmp expected actual.fuzzy &&
+       check_head_differs_from fourth
 '
 
 test_expect_success 'cherry-pick --ff first..fourth works' '
@@ -39,7 +93,7 @@ test_expect_success 'cherry-pick --ff first..fourth works' '
        git cherry-pick --ff first..fourth &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" = "$(git rev-parse --verify fourth)"
+       check_head_equals fourth
 '
 
 test_expect_success 'cherry-pick -n first..fourth works' '
@@ -89,7 +143,7 @@ test_expect_success 'cherry-pick -3 fourth works' '
        git cherry-pick -3 fourth &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+       check_head_differs_from fourth
 '
 
 test_expect_success 'cherry-pick --stdin works' '
@@ -99,7 +153,7 @@ test_expect_success 'cherry-pick --stdin works' '
        git rev-list --reverse first..fourth | git cherry-pick --stdin &&
        git diff --quiet other &&
        git diff --quiet HEAD other &&
-       test "$(git rev-parse --verify HEAD)" != "$(git rev-parse --verify fourth)"
+       check_head_differs_from fourth
 '
 
 test_done
diff --git a/t/t3509-cherry-pick-merge-df.sh b/t/t3509-cherry-pick-merge-df.sh
new file mode 100755 (executable)
index 0000000..a5ccdbf
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='Test cherry-pick with directory/file conflicts'
+. ./test-lib.sh
+
+test_expect_success SYMLINKS 'Setup rename across paths each below D/F conflicts' '
+       mkdir a &&
+       >a/f &&
+       git add a &&
+       git commit -m a &&
+
+       mkdir b &&
+       ln -s ../a b/a &&
+       git add b &&
+       git commit -m b &&
+
+       git checkout -b branch &&
+       rm b/a &&
+       mv a b/a &&
+       ln -s b/a a &&
+       git add . &&
+       git commit -m swap &&
+
+       >f1 &&
+       git add f1 &&
+       git commit -m f1
+'
+
+test_expect_success SYMLINKS 'Cherry-pick succeeds with rename across D/F conflicts' '
+       git reset --hard &&
+       git checkout master^0 &&
+       git cherry-pick branch
+'
+
+test_done
index d03495dc7a9840ddb362b257187176b613de3056..7d7140db380225bfffd3b64c125e2599b6be77c2 100755 (executable)
@@ -272,17 +272,20 @@ test_expect_success 'git add --dry-run of non-existing file' "
        echo \"fatal: pathspec 'ignored-file' did not match any files\" | test_cmp - actual
 "
 
-cat >expect <<EOF
+cat >expect.err <<\EOF
 The following paths are ignored by one of your .gitignore files:
 ignored-file
 Use -f if you really want to add them.
 fatal: no files added
+EOF
+cat >expect.out <<\EOF
 add 'track-this'
 EOF
 
 test_expect_success 'git add --dry-run --ignore-missing of non-existing file' '
-       test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual 2>&1 &&
-       test_cmp expect actual
+       test_must_fail git add --dry-run --ignore-missing track-this ignored-file >actual.out 2>actual.err &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
 '
 
 test_done
index dae635851666a7f5ccf685b89d64f31d5dbdd2fd..19857f4326aa491a4c7d292900a0a766e7c45f80 100755 (executable)
@@ -208,6 +208,7 @@ log -p --first-parent master
 log -m -p --first-parent master
 log -m -p master
 log -SF master
+log -S F master
 log -SF -p master
 log --decorate --all
 log --decorate=full --all
@@ -282,4 +283,8 @@ diff master master^ side
 diff --dirstat master~1 master~2
 EOF
 
+test_expect_success 'log -S requires an argument' '
+       test_must_fail git log -S
+'
+
 test_done
diff --git a/t/t4013/diff.log_-S_F_master b/t/t4013/diff.log_-S_F_master
new file mode 100644 (file)
index 0000000..978d2b4
--- /dev/null
@@ -0,0 +1,7 @@
+$ git log -S F master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+$
index 5b10e976a3fd0c554ff2fa14004e69343a9ec634..61de8a2718df8a7b0b0661d7ba47ade133d66ab8 100755 (executable)
@@ -32,7 +32,7 @@ EOF
 
 sed 's/beer\\/beer,\\/' < Beer.java > Beer-correct.java
 
-builtin_patterns="bibtex cpp html java objc pascal php python ruby tex"
+builtin_patterns="bibtex cpp csharp html java objc pascal php python ruby tex"
 for p in $builtin_patterns
 do
        test_expect_success "builtin $p pattern compiles" '
index 1bd8e5ee3ac5ca4d101738048e6769ae798b23dc..d99814ac641c93383d7199f4419083bedcfc9ea8 100755 (executable)
@@ -114,6 +114,69 @@ test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match)'
        ! test -s actual4
 '
 
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
+       git config diff.ignoreSubmodules all &&
+       git diff HEAD >actual &&
+       ! test -s actual &&
+       git config submodule.subname.ignore none &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config submodule.subname.ignore all &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       sed -e "1,/^@@/d" actual3 >actual3.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual3.body &&
+       git config submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git diff HEAD --ignore-submodules=none >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname &&
+       git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
+       git config diff.ignoreSubmodules dirty &&
+       git diff HEAD >actual &&
+       ! test -s actual &&
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config -f .gitmodules submodule.subname.ignore all &&
+       git config -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config -f .gitmodules submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       sed -e "1,/^@@/d" actual3 >actual3.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual3.body &&
+       git config -f .gitmodules submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git config submodule.subname.ignore none &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname &&
+       git config --unset diff.ignoreSubmodules &&
+       rm .gitmodules
+'
+
 test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
        (
                cd sub &&
@@ -146,6 +209,103 @@ test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match)'
        ! test -s actual4
 '
 
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
+       git config submodule.subname.ignore all &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       ! test -s actual3 &&
+       git config submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git diff --ignore-submodules=none HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
+       git config --add -f .gitmodules submodule.subname.ignore all &&
+       git config --add -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD >actual2 &&
+       ! test -s actual2 &&
+       git config -f .gitmodules submodule.subname.ignore untracked &&
+       git diff HEAD >actual3 &&
+       ! test -s actual3 &&
+       git config -f .gitmodules submodule.subname.ignore dirty &&
+       git diff HEAD >actual4 &&
+       ! test -s actual4 &&
+       git config submodule.subname.ignore none &&
+       git config submodule.subname.path sub &&
+       git diff HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subprev $subprev-dirty &&
+       test_cmp expect.body actual.body &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname &&
+       rm .gitmodules
+'
+
+test_expect_success 'git diff between submodule commits' '
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git diff --ignore-submodules HEAD^..HEAD >actual &&
+       ! test -s actual
+'
+
+test_expect_success 'git diff between submodule commits [.git/config]' '
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config submodule.subname.ignore dirty &&
+       git config submodule.subname.path sub &&
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config submodule.subname.ignore all &&
+       git diff HEAD^..HEAD >actual &&
+       ! test -s actual &&
+       git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff between submodule commits [.gitmodules]' '
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sub &&
+       git diff HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       test_cmp expect.body actual.body &&
+       git config -f .gitmodules submodule.subname.ignore all &&
+       git diff HEAD^..HEAD >actual &&
+       ! test -s actual &&
+       git config submodule.subname.ignore dirty &&
+       git config submodule.subname.path sub &&
+       git diff  HEAD^..HEAD >actual &&
+       sed -e "1,/^@@/d" actual >actual.body &&
+       expect_from_to >expect.body $subtip $subprev &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname &&
+       rm .gitmodules
+'
+
 test_expect_success 'git diff (empty submodule dir)' '
        : >empty &&
        rm -rf sub/* sub/.git &&
diff --git a/t/t4045-diff-relative.sh b/t/t4045-diff-relative.sh
new file mode 100755 (executable)
index 0000000..8a3c63b
--- /dev/null
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+test_description='diff --relative tests'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       git commit --allow-empty -m empty &&
+       echo content >file1 &&
+       mkdir subdir &&
+       echo other content >subdir/file2 &&
+       git add . &&
+       git commit -m one
+'
+
+check_diff() {
+expect=$1; shift
+cat >expected <<EOF
+diff --git a/$expect b/$expect
+new file mode 100644
+index 0000000..25c05ef
+--- /dev/null
++++ b/$expect
+@@ -0,0 +1 @@
++other content
+EOF
+test_expect_success "-p $*" "
+       git diff -p $* HEAD^ >actual &&
+       test_cmp expected actual
+"
+}
+
+check_stat() {
+expect=$1; shift
+cat >expected <<EOF
+ $expect |    1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+EOF
+test_expect_success "--stat $*" "
+       git diff --stat $* HEAD^ >actual &&
+       test_cmp expected actual
+"
+}
+
+check_raw() {
+expect=$1; shift
+cat >expected <<EOF
+:000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A     $expect
+EOF
+test_expect_success "--raw $*" "
+       git diff --no-abbrev --raw $* HEAD^ >actual &&
+       test_cmp expected actual
+"
+}
+
+for type in diff stat raw; do
+       check_$type file2 --relative=subdir/
+       check_$type file2 --relative=subdir
+       check_$type dir/file2 --relative=sub
+done
+
+test_done
diff --git a/t/t4111-apply-subdir.sh b/t/t4111-apply-subdir.sh
new file mode 100755 (executable)
index 0000000..a52d94a
--- /dev/null
@@ -0,0 +1,142 @@
+#!/bin/sh
+
+test_description='patching from inconvenient places'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       cat >patch <<-\EOF &&
+       diff file.orig file
+       --- a/file.orig
+       +++ b/file
+       @@ -1 +1,2 @@
+        1
+       +2
+       EOF
+       patch="$(pwd)/patch" &&
+
+       echo 1 >preimage &&
+       printf "%s\n" 1 2 >postimage &&
+       echo 3 >other &&
+
+       test_tick &&
+       git commit --allow-empty -m basis
+'
+
+test_expect_success 'setup: subdir' '
+       reset_subdir() {
+               git reset &&
+               mkdir -p sub/dir/b &&
+               mkdir -p objects &&
+               cp "$1" file &&
+               cp "$1" objects/file &&
+               cp "$1" sub/dir/file &&
+               cp "$1" sub/dir/b/file &&
+               git add file sub/dir/file sub/dir/b/file objects/file &&
+               cp "$2" file &&
+               cp "$2" sub/dir/file &&
+               cp "$2" sub/dir/b/file &&
+               cp "$2" objects/file &&
+               test_might_fail git update-index --refresh -q
+       }
+'
+
+test_expect_success 'apply from subdir of toplevel' '
+       cp postimage expected &&
+       reset_subdir other preimage &&
+       (
+               cd sub/dir &&
+               git apply "$patch"
+       ) &&
+       test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply --cached from subdir of toplevel' '
+       cp postimage expected &&
+       cp other expected.working &&
+       reset_subdir preimage other &&
+       (
+               cd sub/dir &&
+               git apply --cached "$patch"
+       ) &&
+       git show :sub/dir/file >actual &&
+       test_cmp expected actual &&
+       test_cmp expected.working sub/dir/file
+'
+
+test_expect_success 'apply --index from subdir of toplevel' '
+       cp postimage expected &&
+       reset_subdir preimage other &&
+       (
+               cd sub/dir &&
+               test_must_fail git apply --index "$patch"
+       ) &&
+       reset_subdir other preimage &&
+       (
+               cd sub/dir &&
+               test_must_fail git apply --index "$patch"
+       ) &&
+       reset_subdir preimage preimage &&
+       (
+               cd sub/dir &&
+               git apply --index "$patch"
+       ) &&
+       git show :sub/dir/file >actual &&
+       test_cmp expected actual &&
+       test_cmp expected sub/dir/file
+'
+
+test_expect_success 'apply from .git dir' '
+       cp postimage expected &&
+       cp preimage .git/file &&
+       cp preimage .git/objects/file
+       (
+               cd .git &&
+               git apply "$patch"
+       ) &&
+       test_cmp expected .git/file
+'
+
+test_expect_success 'apply from subdir of .git dir' '
+       cp postimage expected &&
+       cp preimage .git/file &&
+       cp preimage .git/objects/file
+       (
+               cd .git/objects &&
+               git apply "$patch"
+       ) &&
+       test_cmp expected .git/objects/file
+'
+
+test_expect_success 'apply --cached from .git dir' '
+       cp postimage expected &&
+       cp other expected.working &&
+       cp other .git/file &&
+       reset_subdir preimage other &&
+       (
+               cd .git &&
+               git apply --cached "$patch"
+       ) &&
+       git show :file >actual &&
+       test_cmp expected actual &&
+       test_cmp expected.working file &&
+       test_cmp expected.working .git/file
+'
+
+test_expect_success 'apply --cached from subdir of .git dir' '
+       cp postimage expected &&
+       cp preimage expected.subdir &&
+       cp other .git/file &&
+       cp other .git/objects/file &&
+       reset_subdir preimage other &&
+       (
+               cd .git/objects &&
+               git apply --cached "$patch"
+       ) &&
+       git show :file >actual &&
+       git show :objects/file >actual.subdir &&
+       test_cmp expected actual &&
+       test_cmp expected.subdir actual.subdir
+'
+
+test_done
index 810b04b817c79d2b4c478f767843b4e7a42e0bed..1c3d8ed548e629689517661cd1fc6c21d98ccc80 100755 (executable)
@@ -4,66 +4,71 @@ test_description='git am running'
 
 . ./test-lib.sh
 
-cat >msg <<EOF
-second
-
-Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
-eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
-voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
-kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
-ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
-tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
-vero eos et accusam et justo duo dolores et ea rebum.
-
-       Duis autem vel eum iriure dolor in hendrerit in vulputate velit
-       esse molestie consequat, vel illum dolore eu feugiat nulla facilisis
-       at vero eros et accumsan et iusto odio dignissim qui blandit
-       praesent luptatum zzril delenit augue duis dolore te feugait nulla
-       facilisi.
-
-
-Lorem ipsum dolor sit amet,
-consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
-laoreet dolore magna aliquam erat volutpat.
-
-  git
-  ---
-  +++
-
-Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
-lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
-dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
-dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
-dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
-feugait nulla facilisi.
-EOF
-
-cat >failmail <<EOF
-From foo@example.com Fri May 23 10:43:49 2008
-From:  foo@example.com
-To:    bar@example.com
-Subject: Re: [RFC/PATCH] git-foo.sh
-Date:  Fri, 23 May 2008 05:23:42 +0200
-
-Sometimes we have to find out that there's nothing left.
-
-EOF
-
-cat >pine <<EOF
-From MAILER-DAEMON Fri May 23 10:43:49 2008
-Date: 23 May 2008 05:23:42 +0200
-From: Mail System Internal Data <MAILER-DAEMON@example.com>
-Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
-Message-ID: <foo-0001@example.com>
-
-This text is part of the internal format of your mail folder, and is not
-a real message.  It is created automatically by the mail system software.
-If deleted, important folder data will be lost, and it will be re-created
-with the data reset to initial values.
-
-EOF
-
-echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected
+test_expect_success 'setup: messages' '
+       cat >msg <<-\EOF &&
+       second
+
+       Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
+       eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+       voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
+       kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
+       ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+       tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+       vero eos et accusam et justo duo dolores et ea rebum.
+
+       EOF
+       q_to_tab <<-\EOF >>msg &&
+       QDuis autem vel eum iriure dolor in hendrerit in vulputate velit
+       Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis
+       Qat vero eros et accumsan et iusto odio dignissim qui blandit
+       Qpraesent luptatum zzril delenit augue duis dolore te feugait nulla
+       Qfacilisi.
+       EOF
+       cat >>msg <<-\EOF &&
+
+       Lorem ipsum dolor sit amet,
+       consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
+       laoreet dolore magna aliquam erat volutpat.
+
+         git
+         ---
+         +++
+
+       Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
+       lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
+       dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
+       dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
+       dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
+       feugait nulla facilisi.
+       EOF
+
+       cat >failmail <<-\EOF &&
+       From foo@example.com Fri May 23 10:43:49 2008
+       From:   foo@example.com
+       To:     bar@example.com
+       Subject: Re: [RFC/PATCH] git-foo.sh
+       Date:   Fri, 23 May 2008 05:23:42 +0200
+
+       Sometimes we have to find out that there'\''s nothing left.
+
+       EOF
+
+       cat >pine <<-\EOF &&
+       From MAILER-DAEMON Fri May 23 10:43:49 2008
+       Date: 23 May 2008 05:23:42 +0200
+       From: Mail System Internal Data <MAILER-DAEMON@example.com>
+       Subject: DON'\''T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
+       Message-ID: <foo-0001@example.com>
+
+       This text is part of the internal format of your mail folder, and is not
+       a real message.  It is created automatically by the mail system software.
+       If deleted, important folder data will be lost, and it will be re-created
+       with the data reset to initial values.
+
+       EOF
+
+       signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
 
 test_expect_success setup '
        echo hello >file &&
@@ -71,11 +76,13 @@ test_expect_success setup '
        test_tick &&
        git commit -m first &&
        git tag first &&
+
        echo world >>file &&
        git add file &&
        test_tick &&
        git commit -s -F msg &&
        git tag second &&
+
        git format-patch --stdout first >patch1 &&
        {
                echo "X-Fake-Field: Line One" &&
@@ -89,74 +96,101 @@ test_expect_success setup '
                echo "X-Fake-Field: Line Three" &&
                git format-patch --stdout first | sed -e "1d"
        } | append_cr >patch1-crlf.eml &&
+
        sed -n -e "3,\$p" msg >file &&
        git add file &&
        test_tick &&
        git commit -m third &&
+
        git format-patch --stdout first >patch2 &&
+
        git checkout -b lorem &&
        sed -n -e "11,\$p" msg >file &&
        head -n 9 msg >>file &&
        test_tick &&
        git commit -a -m "moved stuff" &&
+
        echo goodbye >another &&
        git add another &&
        test_tick &&
        git commit -m "added another file" &&
-       git format-patch --stdout master >lorem-move.patch
-'
 
-# reset time
-unset test_tick
-test_tick
+       git format-patch --stdout master >lorem-move.patch &&
+
+       git checkout -b rename &&
+       git mv file renamed &&
+       git commit -m "renamed a file" &&
+
+       git format-patch -M --stdout lorem >rename.patch &&
+
+       git reset --soft lorem^ &&
+       git commit -m "renamed a file and added another" &&
+
+       git format-patch -M --stdout lorem^ >rename-add.patch &&
+
+       # reset time
+       unset test_tick &&
+       test_tick
+'
 
 test_expect_success 'am applies patch correctly' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        test_tick &&
        git am <patch1 &&
        ! test -d .git/rebase-apply &&
-       test -z "$(git diff second)" &&
+       git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
 '
 
 test_expect_success 'am applies patch e-mail not in a mbox' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        git am patch1.eml &&
        ! test -d .git/rebase-apply &&
-       test -z "$(git diff second)" &&
+       git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
 '
 
 test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        git am patch1-crlf.eml &&
        ! test -d .git/rebase-apply &&
-       test -z "$(git diff second)" &&
+       git diff --exit-code second &&
        test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
        test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
 '
 
-GIT_AUTHOR_NAME="Another Thor"
-GIT_AUTHOR_EMAIL="a.thor@example.com"
-GIT_COMMITTER_NAME="Co M Miter"
-GIT_COMMITTER_EMAIL="c.miter@example.com"
-export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+test_expect_success 'setup: new author and committer' '
+       GIT_AUTHOR_NAME="Another Thor" &&
+       GIT_AUTHOR_EMAIL="a.thor@example.com" &&
+       GIT_COMMITTER_NAME="Co M Miter" &&
+       GIT_COMMITTER_EMAIL="c.miter@example.com" &&
+       export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+'
 
 compare () {
-       test "$(git cat-file commit "$2" | grep "^$1 ")" = \
-            "$(git cat-file commit "$3" | grep "^$1 ")"
+       a=$(git cat-file commit "$2" | grep "^$1 ") &&
+       b=$(git cat-file commit "$3" | grep "^$1 ") &&
+       test "$a" = "$b"
 }
 
 test_expect_success 'am changes committer and keeps author' '
        test_tick &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        git am patch2 &&
        ! test -d .git/rebase-apply &&
        test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
-       test -z "$(git diff master..HEAD)" &&
-       test -z "$(git diff master^..HEAD^)" &&
+       git diff --exit-code master..HEAD &&
+       git diff --exit-code master^..HEAD^ &&
        compare author master HEAD &&
        compare author master^ HEAD^ &&
        test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
@@ -164,41 +198,55 @@ test_expect_success 'am changes committer and keeps author' '
 '
 
 test_expect_success 'am --signoff adds Signed-off-by: line' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout -b master2 first &&
        git am --signoff <patch2 &&
+       printf "%s\n" "$signoff" >expected &&
        echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
        git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
-       test_cmp actual expected &&
+       test_cmp expected actual &&
        echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
        git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
-       test_cmp actual expected
+       test_cmp expected actual
 '
 
 test_expect_success 'am stays in branch' '
-       test "refs/heads/master2" = "$(git symbolic-ref HEAD)"
+       echo refs/heads/master2 >expected &&
+       git symbolic-ref HEAD >actual &&
+       test_cmp expected actual
 '
 
 test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
        git format-patch --stdout HEAD^ >patch3 &&
        sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout HEAD^ &&
        git am --signoff patch4 &&
-       test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1
+       git cat-file commit HEAD >actual &&
+       test $(grep -c "^Signed-off-by:" actual) -eq 1
 '
 
 test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
-       test "$(git rev-parse HEAD)" = "$(git rev-parse master2)"
+       git rev-parse HEAD >expected &&
+       git rev-parse master2 >actual &&
+       test_cmp expected actual
 '
 
 test_expect_success 'am --keep really keeps the subject' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout HEAD^ &&
        git am --keep patch4 &&
        ! test -d .git/rebase-apply &&
-       git cat-file commit HEAD |
-               fgrep "Re: Re: Re: [PATCH 1/5 v2] third"
+       git cat-file commit HEAD >actual &&
+       grep "Re: Re: Re: \[PATCH 1/5 v2\] third" actual
 '
 
 test_expect_success 'am -3 falls back to 3-way merge' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout -b lorem2 master2 &&
        sed -n -e "3,\$p" msg >file &&
        head -n 9 msg >>file &&
@@ -207,34 +255,75 @@ test_expect_success 'am -3 falls back to 3-way merge' '
        git commit -m "copied stuff" &&
        git am -3 lorem-move.patch &&
        ! test -d .git/rebase-apply &&
-       test -z "$(git diff lorem)"
+       git diff --exit-code lorem
+'
+
+test_expect_success 'am can rename a file' '
+       grep "^rename from" rename.patch &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout lorem^0 &&
+       git am rename.patch &&
+       ! test -d .git/rebase-apply &&
+       git update-index --refresh &&
+       git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file' '
+       grep "^rename from" rename.patch &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout lorem^0 &&
+       git am -3 rename.patch &&
+       ! test -d .git/rebase-apply &&
+       git update-index --refresh &&
+       git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file after falling back to 3-way merge' '
+       grep "^rename from" rename-add.patch &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
+       git checkout lorem^0 &&
+       git am -3 rename-add.patch &&
+       ! test -d .git/rebase-apply &&
+       git update-index --refresh &&
+       git diff --exit-code rename
 '
 
 test_expect_success 'am -3 -q is quiet' '
+       rm -fr .git/rebase-apply &&
+       git checkout -f lorem2 &&
        git reset master2 --hard &&
        sed -n -e "3,\$p" msg >file &&
        head -n 9 msg >>file &&
        git add file &&
        test_tick &&
        git commit -m "copied stuff" &&
-       git am -3 -q lorem-move.patch > output.out 2>&1 &&
+       git am -3 -q lorem-move.patch >output.out 2>&1 &&
        ! test -s output.out
 '
 
 test_expect_success 'am pauses on conflict' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout lorem2^^ &&
        test_must_fail git am lorem-move.patch &&
        test -d .git/rebase-apply
 '
 
 test_expect_success 'am --skip works' '
+       echo goodbye >expected &&
        git am --skip &&
        ! test -d .git/rebase-apply &&
-       test -z "$(git diff lorem2^^ -- file)" &&
-       test goodbye = "$(cat another)"
+       git diff --exit-code lorem2^^ -- file &&
+       test_cmp expected another
 '
 
 test_expect_success 'am --resolved works' '
+       echo goodbye >expected &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout lorem2^^ &&
        test_must_fail git am lorem-move.patch &&
        test -d .git/rebase-apply &&
@@ -242,22 +331,29 @@ test_expect_success 'am --resolved works' '
        git add file &&
        git am --resolved &&
        ! test -d .git/rebase-apply &&
-       test goodbye = "$(cat another)"
+       test_cmp expected another
 '
 
 test_expect_success 'am takes patches from a Pine mailbox' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        cat pine patch1 | git am &&
        ! test -d .git/rebase-apply &&
-       test -z "$(git diff master^..HEAD)"
+       git diff --exit-code master^..HEAD
 '
 
 test_expect_success 'am fails on mail without patch' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        test_must_fail git am <failmail &&
-       rm -r .git/rebase-apply/
+       git am --abort &&
+       ! test -d .git/rebase-apply
 '
 
 test_expect_success 'am fails on empty patch' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        echo "---" >>failmail &&
        test_must_fail git am <failmail &&
        git am --skip &&
@@ -266,28 +362,34 @@ test_expect_success 'am fails on empty patch' '
 
 test_expect_success 'am works from stdin in subdirectory' '
        rm -fr subdir &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        (
                mkdir -p subdir &&
                cd subdir &&
                git am <../patch1
        ) &&
-       test -z "$(git diff second)"
+       git diff --exit-code second
 '
 
 test_expect_success 'am works from file (relative path given) in subdirectory' '
        rm -fr subdir &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        (
                mkdir -p subdir &&
                cd subdir &&
                git am ../patch1
        ) &&
-       test -z "$(git diff second)"
+       git diff --exit-code second
 '
 
 test_expect_success 'am works from file (absolute path given) in subdirectory' '
        rm -fr subdir &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        P=$(pwd) &&
        (
@@ -295,27 +397,31 @@ test_expect_success 'am works from file (absolute path given) in subdirectory' '
                cd subdir &&
                git am "$P/patch1"
        ) &&
-       test -z "$(git diff second)"
+       git diff --exit-code second
 '
 
 test_expect_success 'am --committer-date-is-author-date' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        test_tick &&
        git am --committer-date-is-author-date patch1 &&
        git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
-       at=$(sed -ne "/^author /s/.*> //p" head1) &&
-       ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
-       test "$at" = "$ct"
+       sed -ne "/^author /s/.*> //p" head1 >at &&
+       sed -ne "/^committer /s/.*> //p" head1 >ct &&
+       test_cmp at ct
 '
 
 test_expect_success 'am without --committer-date-is-author-date' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        test_tick &&
        git am patch1 &&
        git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
-       at=$(sed -ne "/^author /s/.*> //p" head1) &&
-       ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
-       test "$at" != "$ct"
+       sed -ne "/^author /s/.*> //p" head1 >at &&
+       sed -ne "/^committer /s/.*> //p" head1 >ct &&
+       ! test_cmp at ct
 '
 
 # This checks for +0000 because TZ is set to UTC and that should
@@ -323,41 +429,51 @@ test_expect_success 'am without --committer-date-is-author-date' '
 # by test_tick that uses -0700 timezone; if this feature does not
 # work, we will see that instead of +0000.
 test_expect_success 'am --ignore-date' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        test_tick &&
        git am --ignore-date patch1 &&
        git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
-       at=$(sed -ne "/^author /s/.*> //p" head1) &&
-       echo "$at" | grep "+0000"
+       sed -ne "/^author /s/.*> //p" head1 >at &&
+       grep "+0000" at
 '
 
 test_expect_success 'am into an unborn branch' '
+       git rev-parse first^{tree} >expected &&
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        rm -fr subdir &&
-       mkdir -p subdir &&
+       mkdir subdir &&
        git format-patch --numbered-files -o subdir -1 first &&
        (
                cd subdir &&
                git init &&
                git am 1
        ) &&
-       result=$(
-               cd subdir && git rev-parse HEAD^{tree}
+       (
+               cd subdir &&
+               git rev-parse HEAD^{tree} >../actual
        ) &&
-       test "z$result" = "z$(git rev-parse first^{tree})"
+       test_cmp expected actual
 '
 
 test_expect_success 'am newline in subject' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        test_tick &&
-       sed -e "s/second/second \\\n foo/" patch1 > patchnl &&
-       git am < patchnl > output.out 2>&1 &&
+       sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
+       git am <patchnl >output.out 2>&1 &&
        grep "^Applying: second \\\n foo$" output.out
 '
 
 test_expect_success 'am -q is quiet' '
+       rm -fr .git/rebase-apply &&
+       git reset --hard &&
        git checkout first &&
        test_tick &&
-       git am -q < patch1 > output.out 2>&1 &&
+       git am -q <patch1 >output.out 2>&1 &&
        ! test -s output.out
 '
 
index 70856d07ed113b731d149bde73fe7b4eb25a72f2..36255d608a7af7d85f479986e302138401f25a8d 100755 (executable)
 #
 
 test_description='git rerere
+
+! [fifth] version1
+ ! [first] first
+  ! [fourth] version1
+   ! [master] initial
+    ! [second] prefer first over second
+     ! [third] version2
+------
+     + [third] version2
++      [fifth] version1
+  +    [fourth] version1
++ +  + [third^] third
+    -  [second] prefer first over second
+ +  +  [first] first
+    +  [second^] second
+++++++ [master] initial
 '
 
 . ./test-lib.sh
 
-test_expect_success 'setup' "
-       cat > a1 <<- EOF &&
+test_expect_success 'setup' '
+       cat >a1 <<-\EOF &&
        Some title
        ==========
-       Whether 'tis nobler in the mind to suffer
+       Whether '\''tis nobler in the mind to suffer
        The slings and arrows of outrageous fortune,
        Or to take arms against a sea of troubles,
        And by opposing end them? To die: to sleep;
        No more; and by a sleep to say we end
        The heart-ache and the thousand natural shocks
-       That flesh is heir to, 'tis a consummation
-       Devoutly to be wish'd.
+       That flesh is heir to, '\''tis a consummation
+       Devoutly to be wish'\''d.
        EOF
 
        git add a1 &&
+       test_tick &&
        git commit -q -a -m initial &&
 
-       git checkout -b first &&
-       cat >> a1 <<- EOF &&
+       cat >>a1 <<-\EOF &&
        Some title
        ==========
        To die, to sleep;
-       To sleep: perchance to dream: ay, there's the rub;
+       To sleep: perchance to dream: ay, there'\''s the rub;
        For in that sleep of death what dreams may come
        When we have shuffled off this mortal coil,
-       Must give us pause: there's the respect
+       Must give us pause: there'\''s the respect
        That makes calamity of so long life;
        EOF
+
+       git checkout -b first &&
+       test_tick &&
        git commit -q -a -m first &&
 
        git checkout -b second master &&
        git show first:a1 |
-       sed -e 's/To die, t/To die! T/' -e 's/Some title/Some Title/' > a1 &&
-       echo '* END *' >>a1 &&
+       sed -e "s/To die, t/To die! T/" -e "s/Some title/Some Title/" >a1 &&
+       echo "* END *" >>a1 &&
+       test_tick &&
        git commit -q -a -m second
-"
+'
 
 test_expect_success 'nothing recorded without rerere' '
-       (rm -rf .git/rr-cache; git config rerere.enabled false) &&
+       rm -rf .git/rr-cache &&
+       git config rerere.enabled false &&
        test_must_fail git merge first &&
        ! test -d .git/rr-cache
 '
 
-# activate rerere, old style
-test_expect_success 'conflicting merge' '
+test_expect_success 'activate rerere, old style (conflicting merge)' '
        git reset --hard &&
        mkdir .git/rr-cache &&
-       git config --unset rerere.enabled &&
-       test_must_fail git merge first
-'
+       test_might_fail git config --unset rerere.enabled &&
+       test_must_fail git merge first &&
 
-sha1=$(perl -pe 's/    .*//' .git/MERGE_RR)
-rr=.git/rr-cache/$sha1
-test_expect_success 'recorded preimage' "grep ^=======$ $rr/preimage"
+       sha1=$(perl -pe "s/     .*//" .git/MERGE_RR) &&
+       rr=.git/rr-cache/$sha1 &&
+       grep "^=======\$" $rr/preimage &&
+       ! test -f $rr/postimage &&
+       ! test -f $rr/thisimage
+'
 
 test_expect_success 'rerere.enabled works, too' '
        rm -rf .git/rr-cache &&
        git config rerere.enabled true &&
        git reset --hard &&
        test_must_fail git merge first &&
+
+       sha1=$(perl -pe "s/     .*//" .git/MERGE_RR) &&
+       rr=.git/rr-cache/$sha1 &&
        grep ^=======$ $rr/preimage
 '
 
-test_expect_success 'no postimage or thisimage yet' \
-       "test ! -f $rr/postimage -a ! -f $rr/thisimage"
+test_expect_success 'set up rr-cache' '
+       rm -rf .git/rr-cache &&
+       git config rerere.enabled true &&
+       git reset --hard &&
+       test_must_fail git merge first &&
+       sha1=$(perl -pe "s/     .*//" .git/MERGE_RR) &&
+       rr=.git/rr-cache/$sha1
+'
 
-test_expect_success 'preimage has right number of lines' '
+test_expect_success 'rr-cache looks sane' '
+       # no postimage or thisimage yet
+       ! test -f $rr/postimage &&
+       ! test -f $rr/thisimage &&
 
+       # preimage has right number of lines
        cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
+       echo $cnt &&
        test $cnt = 13
-
 '
 
-git show first:a1 > a1
-
-cat > expect << EOF
---- a/a1
-+++ b/a1
-@@ -1,4 +1,4 @@
--Some Title
-+Some title
- ==========
- Whether 'tis nobler in the mind to suffer
- The slings and arrows of outrageous fortune,
-@@ -8,21 +8,11 @@
- The heart-ache and the thousand natural shocks
- That flesh is heir to, 'tis a consummation
- Devoutly to be wish'd.
--<<<<<<<
--Some Title
--==========
--To die! To sleep;
--=======
- Some title
- ==========
- To die, to sleep;
-->>>>>>>
- To sleep: perchance to dream: ay, there's the rub;
- For in that sleep of death what dreams may come
- When we have shuffled off this mortal coil,
- Must give us pause: there's the respect
- That makes calamity of so long life;
--<<<<<<<
--=======
--* END *
-->>>>>>>
-EOF
-git rerere diff > out
-
-test_expect_success 'rerere diff' 'test_cmp expect out'
-
-cat > expect << EOF
-a1
-EOF
-
-git rerere status > out
-
-test_expect_success 'rerere status' 'test_cmp expect out'
-
-test_expect_success 'commit succeeds' \
-       "git commit -q -a -m 'prefer first over second'"
-
-test_expect_success 'recorded postimage' "test -f $rr/postimage"
-
-test_expect_success 'another conflicting merge' '
-       git checkout -b third master &&
-       git show second^:a1 | sed "s/To die: t/To die! T/" > a1 &&
-       git commit -q -a -m third &&
-       test_must_fail git pull . first
+test_expect_success 'rerere diff' '
+       git show first:a1 >a1 &&
+       cat >expect <<-\EOF &&
+       --- a/a1
+       +++ b/a1
+       @@ -1,4 +1,4 @@
+       -Some Title
+       +Some title
+        ==========
+        Whether '\''tis nobler in the mind to suffer
+        The slings and arrows of outrageous fortune,
+       @@ -8,21 +8,11 @@
+        The heart-ache and the thousand natural shocks
+        That flesh is heir to, '\''tis a consummation
+        Devoutly to be wish'\''d.
+       -<<<<<<<
+       -Some Title
+       -==========
+       -To die! To sleep;
+       -=======
+        Some title
+        ==========
+        To die, to sleep;
+       ->>>>>>>
+        To sleep: perchance to dream: ay, there'\''s the rub;
+        For in that sleep of death what dreams may come
+        When we have shuffled off this mortal coil,
+        Must give us pause: there'\''s the respect
+        That makes calamity of so long life;
+       -<<<<<<<
+       -=======
+       -* END *
+       ->>>>>>>
+       EOF
+       git rerere diff >out &&
+       test_cmp expect out
 '
 
-git show first:a1 | sed 's/To die: t/To die! T/' > expect
-test_expect_success 'rerere kicked in' "! grep ^=======$ a1"
+test_expect_success 'rerere status' '
+       echo a1 >expect &&
+       git rerere status >out &&
+       test_cmp expect out
+'
 
-test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'
+test_expect_success 'first postimage wins' '
+       git show first:a1 | sed "s/To die: t/To die! T/" >expect &&
 
-rm $rr/postimage
-echo "$sha1    a1" | perl -pe 'y/\012/\000/' > .git/MERGE_RR
+       git commit -q -a -m "prefer first over second" &&
+       test -f $rr/postimage &&
 
-test_expect_success 'rerere clear' 'git rerere clear'
+       oldmtimepost=$(test-chmtime -v -60 $rr/postimage | cut -f 1) &&
 
-test_expect_success 'clear removed the directory' "test ! -d $rr"
+       git checkout -b third master &&
+       git show second^:a1 | sed "s/To die: t/To die! T/" >a1 &&
+       git commit -q -a -m third &&
 
-mkdir $rr
-echo Hello > $rr/preimage
-echo World > $rr/postimage
+       test_must_fail git pull . first &&
+       # rerere kicked in
+       ! grep "^=======\$" a1 &&
+       test_cmp expect a1
+'
 
-sha2=4000000000000000000000000000000000000000
-rr2=.git/rr-cache/$sha2
-mkdir $rr2
-echo Hello > $rr2/preimage
+test_expect_success 'rerere updates postimage timestamp' '
+       newmtimepost=$(test-chmtime -v +0 $rr/postimage | cut -f 1) &&
+       test $oldmtimepost -lt $newmtimepost
+'
 
-almost_15_days_ago=$((60-15*86400))
-just_over_15_days_ago=$((-1-15*86400))
-almost_60_days_ago=$((60-60*86400))
-just_over_60_days_ago=$((-1-60*86400))
+test_expect_success 'rerere clear' '
+       rm $rr/postimage &&
+       echo "$sha1     a1" | perl -pe "y/\012/\000/" >.git/MERGE_RR &&
+       git rerere clear &&
+       ! test -d $rr
+'
 
-test-chmtime =$almost_60_days_ago $rr/preimage
-test-chmtime =$almost_15_days_ago $rr2/preimage
+test_expect_success 'set up for garbage collection tests' '
+       mkdir -p $rr &&
+       echo Hello >$rr/preimage &&
+       echo World >$rr/postimage &&
 
-test_expect_success 'garbage collection (part1)' 'git rerere gc'
+       sha2=4000000000000000000000000000000000000000 &&
+       rr2=.git/rr-cache/$sha2 &&
+       mkdir $rr2 &&
+       echo Hello >$rr2/preimage &&
 
-test_expect_success 'young records still live' \
-       "test -f $rr/preimage && test -f $rr2/preimage"
+       almost_15_days_ago=$((60-15*86400)) &&
+       just_over_15_days_ago=$((-1-15*86400)) &&
+       almost_60_days_ago=$((60-60*86400)) &&
+       just_over_60_days_ago=$((-1-60*86400)) &&
 
-test-chmtime =$just_over_60_days_ago $rr/preimage
-test-chmtime =$just_over_15_days_ago $rr2/preimage
+       test-chmtime =$just_over_60_days_ago $rr/preimage &&
+       test-chmtime =$almost_60_days_ago $rr/postimage &&
+       test-chmtime =$almost_15_days_ago $rr2/preimage
+'
 
-test_expect_success 'garbage collection (part2)' 'git rerere gc'
+test_expect_success 'gc preserves young or recently used records' '
+       git rerere gc &&
+       test -f $rr/preimage &&
+       test -f $rr2/preimage
+'
 
-test_expect_success 'old records rest in peace' \
-       "test ! -f $rr/preimage && test ! -f $rr2/preimage"
+test_expect_success 'old records rest in peace' '
+       test-chmtime =$just_over_60_days_ago $rr/postimage &&
+       test-chmtime =$just_over_15_days_ago $rr2/preimage &&
+       git rerere gc &&
+       ! test -f $rr/preimage &&
+       ! test -f $rr2/preimage
+'
 
-test_expect_success 'file2 added differently in two branches' '
+test_expect_success 'setup: file2 added differently in two branches' '
        git reset --hard &&
+
        git checkout -b fourth &&
-       echo Hallo > file2 &&
+       echo Hallo >file2 &&
        git add file2 &&
+       test_tick &&
        git commit -m version1 &&
+
        git checkout third &&
-       echo Bello > file2 &&
+       echo Bello >file2 &&
        git add file2 &&
+       test_tick &&
        git commit -m version2 &&
+
        test_must_fail git merge fourth &&
-       echo Cello > file2 &&
+       echo Cello >file2 &&
        git add file2 &&
        git commit -m resolution
 '
 
 test_expect_success 'resolution was recorded properly' '
+       echo Cello >expected &&
+
        git reset --hard HEAD~2 &&
        git checkout -b fifth &&
-       echo Hallo > file3 &&
+
+       echo Hallo >file3 &&
        git add file3 &&
+       test_tick &&
        git commit -m version1 &&
+
        git checkout third &&
-       echo Bello > file3 &&
+       echo Bello >file3 &&
        git add file3 &&
+       test_tick &&
        git commit -m version2 &&
        git tag version2 &&
+
        test_must_fail git merge fifth &&
-       test Cello = "$(cat file3)" &&
-       test 0 != $(git ls-files -u | wc -l)
+       test_cmp expected file3 &&
+       test_must_fail git update-index --refresh
 '
 
 test_expect_success 'rerere.autoupdate' '
-       git config rerere.autoupdate true
+       git config rerere.autoupdate true &&
        git reset --hard &&
        git checkout version2 &&
        test_must_fail git merge fifth &&
-       test 0 = $(git ls-files -u | wc -l)
+       git update-index --refresh
 '
 
 test_expect_success 'merge --rerere-autoupdate' '
-       git config --unset rerere.autoupdate
+       test_might_fail git config --unset rerere.autoupdate &&
        git reset --hard &&
        git checkout version2 &&
        test_must_fail git merge --rerere-autoupdate fifth &&
-       test 0 = $(git ls-files -u | wc -l)
+       git update-index --refresh
 '
 
 test_expect_success 'merge --no-rerere-autoupdate' '
-       git config rerere.autoupdate true
+       headblob=$(git rev-parse version2:file3) &&
+       mergeblob=$(git rev-parse fifth:file3) &&
+       cat >expected <<-EOF &&
+       100644 $headblob 2      file3
+       100644 $mergeblob 3     file3
+       EOF
+
+       git config rerere.autoupdate true &&
        git reset --hard &&
        git checkout version2 &&
        test_must_fail git merge --no-rerere-autoupdate fifth &&
-       test 2 = $(git ls-files -u | wc -l)
+       git ls-files -u >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'set up an unresolved merge' '
+       headblob=$(git rev-parse version2:file3) &&
+       mergeblob=$(git rev-parse fifth:file3) &&
+       cat >expected.unresolved <<-EOF &&
+       100644 $headblob 2      file3
+       100644 $mergeblob 3     file3
+       EOF
+
+       test_might_fail git config --unset rerere.autoupdate &&
+       git reset --hard &&
+       git checkout version2 &&
+       fifth=$(git rev-parse fifth) &&
+       echo "$fifth            branch 'fifth' of ." |
+       git fmt-merge-msg >msg &&
+       ancestor=$(git merge-base version2 fifth) &&
+       test_must_fail git merge-recursive "$ancestor" -- HEAD fifth &&
+
+       git ls-files --stage >failedmerge &&
+       cp file3 file3.conflict &&
+
+       git ls-files -u >actual &&
+       test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere' '
+       test_might_fail git config --unset rerere.autoupdate &&
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       test_must_fail git update-index --refresh -q &&
+
+       git rerere &&
+       git ls-files -u >actual &&
+       test_cmp expected.unresolved actual
+'
+
+test_expect_success 'explicit rerere with autoupdate' '
+       git config rerere.autoupdate true &&
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       test_must_fail git update-index --refresh -q &&
+
+       git rerere &&
+       git update-index --refresh
+'
+
+test_expect_success 'explicit rerere --rerere-autoupdate overrides' '
+       git config rerere.autoupdate false &&
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere &&
+       git ls-files -u >actual1 &&
+
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere --rerere-autoupdate &&
+       git update-index --refresh &&
+
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere --rerere-autoupdate --no-rerere-autoupdate &&
+       git ls-files -u >actual2 &&
+
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       git rerere --rerere-autoupdate --no-rerere-autoupdate --rerere-autoupdate &&
+       git update-index --refresh &&
+
+       test_cmp expected.unresolved actual1 &&
+       test_cmp expected.unresolved actual2
+'
+
+test_expect_success 'rerere --no-no-rerere-autoupdate' '
+       git rm -fr --cached . &&
+       git update-index --index-info <failedmerge &&
+       cp file3.conflict file3 &&
+       test_must_fail git rerere --no-no-rerere-autoupdate 2>err &&
+       grep [Uu]sage err &&
+       test_must_fail git update-index --refresh
+'
+
+test_expect_success 'rerere -h' '
+       test_must_fail git rerere -h >help &&
+       grep [Uu]sage help
 '
 
 test_done
index 2230e606edea0c4f3dcd0b6b99298074d5341e4a..2e513569477bb5efc389a2088a4d896620e50b8f 100755 (executable)
@@ -100,13 +100,11 @@ test_expect_success 'oneline' '
 
 test_expect_success 'diff-filter=A' '
 
-       actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
-       expect=$(echo fifth ; echo fourth ; echo third ; echo initial) &&
-       test "$actual" = "$expect" || {
-               echo Oops
-               echo "Actual: $actual"
-               false
-       }
+       git log --pretty="format:%s" --diff-filter=A HEAD > actual &&
+       git log --pretty="format:%s" --diff-filter A HEAD > actual-separate &&
+       printf "fifth\nfourth\nthird\ninitial" > expect &&
+       test_cmp expect actual &&
+       test_cmp expect actual-separate
 
 '
 
@@ -203,6 +201,13 @@ test_expect_success 'log --grep' '
        test_cmp expect actual
 '
 
+test_expect_success 'log --grep option parsing' '
+       echo second >expect &&
+       git log -1 --pretty="tformat:%s" --grep sec >actual &&
+       test_cmp expect actual &&
+       test_must_fail git log -1 --pretty="tformat:%s" --grep
+'
+
 test_expect_success 'log -i --grep' '
        echo Second >expect &&
        git log -1 --pretty="tformat:%s" -i --grep=sec >actual &&
@@ -436,5 +441,17 @@ test_expect_success 'log.decorate configuration' '
 
 '
 
-test_done
+test_expect_success 'show added path under "--follow -M"' '
+       # This tests for a regression introduced in v1.7.2-rc0~103^2~2
+       test_create_repo regression &&
+       (
+               cd regression &&
+               test_commit needs-another-commit &&
+               test_commit foo.bar &&
+               git log -M --follow -p foo.bar.t &&
+               git log -M --follow --stat foo.bar.t &&
+               git log -M --follow --name-only foo.bar.t
+       )
+'
 
+test_done
index 426b319bd36257331f07d5aee1a20ed7e515408a..02d4d2284d8bcf9637dbd6a670e376b4ae9f7af1 100755 (executable)
@@ -4,7 +4,7 @@ test_description='git archive attribute tests'
 
 . ./test-lib.sh
 
-SUBSTFORMAT=%H%n
+SUBSTFORMAT='%H (%h)%n'
 
 test_expect_exists() {
        test_expect_success " $1 exists" "test -e $1"
index 319e389ed0dbb9c920d8d619cdf94ab52145e9f4..0b489f5b1227268c050c1256d105d57d871f5698 100755 (executable)
@@ -4,6 +4,11 @@ test_description='pulling into void'
 
 . ./test-lib.sh
 
+modify () {
+       sed -e "$1" <"$2" >"$2.x" &&
+       mv "$2.x" "$2"
+}
+
 D=`pwd`
 
 test_expect_success setup '
@@ -160,4 +165,61 @@ test_expect_success 'pull --rebase works on branch yet to be born' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup for detecting upstreamed changes' '
+       mkdir src &&
+       (cd src &&
+        git init &&
+        printf "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n" > stuff &&
+        git add stuff &&
+        git commit -m "Initial revision"
+       ) &&
+       git clone src dst &&
+       (cd src &&
+        modify s/5/43/ stuff &&
+        git commit -a -m "5->43" &&
+        modify s/6/42/ stuff &&
+        git commit -a -m "Make it bigger"
+       ) &&
+       (cd dst &&
+        modify s/5/43/ stuff &&
+        git commit -a -m "Independent discovery of 5->43"
+       )
+'
+
+test_expect_success 'git pull --rebase detects upstreamed changes' '
+       (cd dst &&
+        git pull --rebase &&
+        test -z "$(git ls-files -u)"
+       )
+'
+
+test_expect_success 'setup for avoiding reapplying old patches' '
+       (cd dst &&
+        test_might_fail git rebase --abort &&
+        git reset --hard origin/master
+       ) &&
+       git clone --bare src src-replace.git &&
+       rm -rf src &&
+       mv src-replace.git src &&
+       (cd dst &&
+        modify s/2/22/ stuff &&
+        git commit -a -m "Change 2" &&
+        modify s/3/33/ stuff &&
+        git commit -a -m "Change 3" &&
+        modify s/4/44/ stuff &&
+        git commit -a -m "Change 4" &&
+        git push &&
+
+        modify s/44/55/ stuff &&
+        git commit --amend -a -m "Modified Change 4"
+       )
+'
+
+test_expect_success 'git pull --rebase does not reapply old patches' '
+       (cd dst &&
+        test_must_fail git pull --rebase &&
+        test 1 = $(find .git/rebase-apply -name "000*" | wc -l)
+       )
+'
+
 test_done
diff --git a/t/t5525-fetch-tagopt.sh b/t/t5525-fetch-tagopt.sh
new file mode 100755 (executable)
index 0000000..4fbf7a1
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='tagopt variable affects "git fetch" and is overridden by commandline.'
+
+. ./test-lib.sh
+
+setup_clone () {
+       git clone --mirror . $1 &&
+       git remote add remote_$1 $1 &&
+       (cd $1 &&
+       git tag tag_$1)
+}
+
+test_expect_success setup '
+       test_commit test &&
+       setup_clone one &&
+       git config remote.remote_one.tagopt --no-tags &&
+       setup_clone two &&
+       git config remote.remote_two.tagopt --tags
+       '
+
+test_expect_success "fetch with tagopt=--no-tags does not get tag" '
+       git fetch remote_one &&
+       test_must_fail git show-ref tag_one
+       '
+
+test_expect_success "fetch --tags with tagopt=--no-tags gets tag" '
+       git fetch --tags remote_one &&
+       git show-ref tag_one
+       '
+
+test_expect_success "fetch --no-tags with tagopt=--tags does not get tag" '
+       git fetch --no-tags remote_two &&
+       test_must_fail git show-ref tag_two
+       '
+
+test_expect_success "fetch with tagopt=--tags gets tag" '
+       git fetch remote_two &&
+       git show-ref tag_two
+       '
+test_done
index 044603c26ed62e3ddf03ebb2542f783e4dd7d9ff..6b2a5f4a65659b7fcfcd9d096a0b43473e1cf1d6 100755 (executable)
@@ -60,6 +60,15 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
        grep "bad tree object" output.err
 '
 
+test_expect_success 'upload-pack error message when bad ref requested' '
+
+       printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+               "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+       test_must_fail git upload-pack . <input >output 2>output.err &&
+       grep -q "not our ref" output.err &&
+       ! grep -q multi_ack_detailed output.err
+'
+
 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
 
        printf "0032want %s\n00000009done\n0000" \
index 8abb71afcd4d7389260baa6f82ecb9b53bb9524c..4431dfd02b59710b8e7fc48928dd846615a8c000 100755 (executable)
@@ -178,8 +178,14 @@ test_expect_success 'clone respects global branch.autosetuprebase' '
 
 test_expect_success 'respect url-encoding of file://' '
        git init x+y &&
-       test_must_fail git clone "file://$PWD/x+y" xy-url &&
-       git clone "file://$PWD/x%2By" xy-url
+       git clone "file://$PWD/x+y" xy-url-1 &&
+       git clone "file://$PWD/x%2By" xy-url-2
+'
+
+test_expect_success 'do not query-string-decode + in URLs' '
+       rm -rf x+y &&
+       git init "x y" &&
+       test_must_fail git clone "file://$PWD/x+y" xy-no-plus
 '
 
 test_expect_success 'do not respect url-encoding of non-url path' '
index 58428d9f5c3d079b0e842b5851b100f9e895a239..fb8291c812854608777dd8d35edfcda78df5069a 100755 (executable)
@@ -123,6 +123,12 @@ test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
 
 '
 
+test_expect_success 'rev-list --glob refs/heads/subspace/*' '
+
+       compare rev-list "subspace/one subspace/two" "--glob refs/heads/subspace/*"
+
+'
+
 test_expect_success 'rev-list --glob=heads/subspace/*' '
 
        compare rev-list "subspace/one subspace/two" "--glob=heads/subspace/*"
index e71c687f2b9473842684fae1a1b4b013c721497f..490d3971142a87e940f3d27e7e7f068b276ff602 100755 (executable)
@@ -22,7 +22,7 @@ git commit -m "File: dir"'
 
 test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
 
-test_expect_failure 'F/D conflict' '
+test_expect_success 'F/D conflict' '
        git reset --hard &&
        git checkout master &&
        rm .git/index &&
index 8a3304fb0b5901fb02435d3b77c3d049404f4e25..bd75e0e6430ff6e536f0b10d28cc4793d0f7fe6c 100755 (executable)
@@ -57,4 +57,35 @@ test_expect_success FILEMODE 'verify executable bit on file' '
        test -x file2
 '
 
+test_expect_success 'merging with triple rename across D/F conflict' '
+       git reset --hard HEAD &&
+       git checkout -b main &&
+       git rm -rf . &&
+
+       echo "just a file" >sub1 &&
+       mkdir -p sub2 &&
+       echo content1 >sub2/file1 &&
+       echo content2 >sub2/file2 &&
+       echo content3 >sub2/file3 &&
+       mkdir simple &&
+       echo base >simple/bar &&
+       git add -A &&
+       test_tick &&
+       git commit -m base &&
+
+       git checkout -b other &&
+       echo more >>simple/bar &&
+       test_tick &&
+       git commit -a -m changesimplefile &&
+
+       git checkout main &&
+       git rm sub1 &&
+       git mv sub2 sub1 &&
+       test_tick &&
+       git commit -m changefiletodir &&
+
+       test_tick &&
+       git merge other
+'
+
 test_done
index cd3190c4a61f0404491b41a1b22f5143b63f4992..dc09513be5a78bf12139efd931ee0344b2710764 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success 'setup for merge test' '
        git tag baseline
 '
 
-test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve master &&
@@ -56,7 +56,7 @@ test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
        test -f a/b-2/c/d
 '
 
-test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
+test_expect_success 'Handle D/F conflict, do not lose a/b-2/c/d in merge (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s recursive master &&
@@ -64,6 +64,54 @@ test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
        test -f a/b-2/c/d
 '
 
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (resolve)' '
+       git reset --hard &&
+       git checkout master^0 &&
+       git merge -s resolve baseline^0 &&
+       test -h a/b &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success 'Handle F/D conflict, do not lose a/b-2/c/d in merge (recursive)' '
+       git reset --hard &&
+       git checkout master^0 &&
+       git merge -s recursive baseline^0 &&
+       test -h a/b &&
+       test -f a/b-2/c/d
+'
+
+test_expect_failure 'do not lose untracked in merge (resolve)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       >a/b/c/e &&
+       test_must_fail git merge -s resolve master &&
+       test -f a/b/c/e &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success 'do not lose untracked in merge (recursive)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       >a/b/c/e &&
+       test_must_fail git merge -s recursive master &&
+       test -f a/b/c/e &&
+       test -f a/b-2/c/d
+'
+
+test_expect_success 'do not lose modifications in merge (resolve)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       echo more content >>a/b/c/d &&
+       test_must_fail git merge -s resolve master
+'
+
+test_expect_success 'do not lose modifications in merge (recursive)' '
+       git reset --hard &&
+       git checkout baseline^0 &&
+       echo more content >>a/b/c/d &&
+       test_must_fail git merge -s recursive master
+'
+
 test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
        git reset --hard &&
        git checkout start^0 &&
@@ -74,7 +122,7 @@ test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
        git tag test2
 '
 
-test_expect_success 'merge should not have conflicts (resolve)' '
+test_expect_success 'merge should not have D/F conflicts (resolve)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s resolve test2 &&
@@ -82,7 +130,7 @@ test_expect_success 'merge should not have conflicts (resolve)' '
        test -f a/b/c/d
 '
 
-test_expect_failure 'merge should not have conflicts (recursive)' '
+test_expect_success 'merge should not have D/F conflicts (recursive)' '
        git reset --hard &&
        git checkout baseline^0 &&
        git merge -s recursive test2 &&
@@ -90,4 +138,12 @@ test_expect_failure 'merge should not have conflicts (recursive)' '
        test -f a/b/c/d
 '
 
+test_expect_success 'merge should not have F/D conflicts (recursive)' '
+       git reset --hard &&
+       git checkout -b foo test2 &&
+       git merge -s recursive baseline^0 &&
+       test -h a/b-2 &&
+       test -f a/b/c/d
+'
+
 test_done
index 8ab3d61f445725ec48b8b4ce28cfbf4a9ce13844..2cf42c73f14ef5069d096fb29e67dac571227cfe 100755 (executable)
@@ -58,7 +58,7 @@ test_expect_success 'pull with -X' '
        git reset --hard master && git pull -s recursive -X ours . side &&
        git reset --hard master && git pull -s recursive -Xtheirs . side &&
        git reset --hard master && git pull -s recursive -X theirs . side &&
-       git reset --hard master && ! git pull -s recursive -X bork . side
+       git reset --hard master && test_must_fail git pull -s recursive -X bork . side
 '
 
 test_done
diff --git a/t/t6038-merge-text-auto.sh b/t/t6038-merge-text-auto.sh
new file mode 100755 (executable)
index 0000000..52d0dc4
--- /dev/null
@@ -0,0 +1,189 @@
+#!/bin/sh
+
+test_description='CRLF merge conflict across text=auto change
+
+* [master] remove .gitattributes
+ ! [side] add line from b
+--
+ + [side] add line from b
+*  [master] remove .gitattributes
+*  [master^] add line from a
+*  [master~2] normalize file
+*+ [side^] Initial
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+       git config core.autocrlf false &&
+
+       echo first line | append_cr >file &&
+       echo first line >control_file &&
+       echo only line >inert_file &&
+
+       git add file control_file inert_file &&
+       test_tick &&
+       git commit -m "Initial" &&
+       git tag initial &&
+       git branch side &&
+
+       echo "* text=auto" >.gitattributes &&
+       touch file &&
+       git add .gitattributes file &&
+       test_tick &&
+       git commit -m "normalize file" &&
+
+       echo same line | append_cr >>file &&
+       echo same line >>control_file &&
+       git add file control_file &&
+       test_tick &&
+       git commit -m "add line from a" &&
+       git tag a &&
+
+       git rm .gitattributes &&
+       rm file &&
+       git checkout file &&
+       test_tick &&
+       git commit -m "remove .gitattributes" &&
+       git tag c &&
+
+       git checkout side &&
+       echo same line | append_cr >>file &&
+       echo same line >>control_file &&
+       git add file control_file &&
+       test_tick &&
+       git commit -m "add line from b" &&
+       git tag b &&
+
+       git checkout master
+'
+
+test_expect_success 'set up fuzz_conflict() helper' '
+       fuzz_conflict() {
+               sed -e "s/^\([<>=]......\) .*/\1/" "$@"
+       }
+'
+
+test_expect_success 'Merge after setting text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard a &&
+       git merge b &&
+       test_cmp expected file
+'
+
+test_expect_success 'Merge addition of text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard b &&
+       git merge a &&
+       test_cmp expected file
+'
+
+test_expect_success 'Detect CRLF/LF conflict after setting text=auto' '
+       q_to_cr <<-\EOF >expected &&
+       <<<<<<<
+       first line
+       same line
+       =======
+       first lineQ
+       same lineQ
+       >>>>>>>
+       EOF
+
+       git config merge.renormalize false &&
+       rm -f .gitattributes &&
+       git reset --hard a &&
+       test_must_fail git merge b &&
+       fuzz_conflict file >file.fuzzy &&
+       test_cmp expected file.fuzzy
+'
+
+test_expect_success 'Detect LF/CRLF conflict from addition of text=auto' '
+       q_to_cr <<-\EOF >expected &&
+       <<<<<<<
+       first lineQ
+       same lineQ
+       =======
+       first line
+       same line
+       >>>>>>>
+       EOF
+
+       git config merge.renormalize false &&
+       rm -f .gitattributes &&
+       git reset --hard b &&
+       test_must_fail git merge a &&
+       fuzz_conflict file >file.fuzzy &&
+       test_cmp expected file.fuzzy
+'
+
+test_expect_failure 'checkout -m after setting text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard initial &&
+       git checkout a -- . &&
+       git checkout -m b &&
+       test_cmp expected file
+'
+
+test_expect_failure 'checkout -m addition of text=auto' '
+       cat <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       rm -f .gitattributes file &&
+       git reset --hard initial &&
+       git checkout b -- . &&
+       git checkout -m a &&
+       test_cmp expected file
+'
+
+test_expect_failure 'cherry-pick patch from after text=auto was added' '
+       append_cr <<-\EOF >expected &&
+       first line
+       same line
+       EOF
+
+       git config merge.renormalize true &&
+       git rm -fr . &&
+       git reset --hard b &&
+       test_must_fail git cherry-pick a >err 2>&1 &&
+       grep "[Nn]othing added" err &&
+       test_cmp expected file
+'
+
+test_expect_success 'Test delete/normalize conflict' '
+       git checkout -f side &&
+       git rm -fr . &&
+       rm -f .gitattributes &&
+       git reset --hard initial &&
+       git rm file &&
+       git commit -m "remove file" &&
+       git checkout master &&
+       git reset --hard a^ &&
+       git merge side
+'
+
+test_done
index 203ffdb17a914654d35416575b6797a2825ce4e6..4185b7ca1d3679b458d16753581fe78e3b68b376 100755 (executable)
@@ -219,6 +219,12 @@ test_expect_success 'bisect and replacements' '
      git bisect reset
 '
 
+test_expect_success 'index-pack and replacements' '
+       git --no-replace-objects rev-list --objects HEAD |
+       git --no-replace-objects pack-objects test- &&
+       git index-pack test-*.pack
+'
+
 #
 #
 test_done
index 0da13a8d6b8f75ba041a353dfc6a356c1e8bbed0..2c55801ee8b88b2d97968a24706dcfdab824668b 100755 (executable)
@@ -143,11 +143,12 @@ test_expect_success 'more setup' '
 test_expect_success 'use index-filter to move into a subdirectory' '
        git branch directorymoved &&
        git filter-branch -f --index-filter \
-                "git ls-files -s | sed \"s-\\t-&newsubdir/-\" |
+                "git ls-files -s | sed \"s-    -&newsubdir/-\" |
                  GIT_INDEX_FILE=\$GIT_INDEX_FILE.new \
                        git update-index --index-info &&
                  mv \"\$GIT_INDEX_FILE.new\" \"\$GIT_INDEX_FILE\"" directorymoved &&
-       test -z "$(git diff HEAD directorymoved:newsubdir)"'
+       git diff --exit-code HEAD directorymoved:newsubdir
+'
 
 test_expect_success 'stops when msg filter fails' '
        old=$(git rev-parse HEAD) &&
index 71d3ceff8f06ad33aee5e48f85e97164df447a59..fb744e3c4a9a19d9285fc04053044242ffed0c65 100755 (executable)
@@ -58,6 +58,21 @@ test_expect_success TTY 'some commands use a pager' '
        test -e paginated.out
 '
 
+test_expect_failure TTY 'pager runs from subdir' '
+       echo subdir/paginated.out >expected &&
+       mkdir -p subdir &&
+       rm -f paginated.out subdir/paginated.out &&
+       (
+               cd subdir &&
+               test_terminal git log
+       ) &&
+       {
+               ls paginated.out subdir/paginated.out ||
+               :
+       } >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success TTY 'some commands do not use a pager' '
        rm -f paginated.out ||
        cleanup_fail &&
@@ -106,6 +121,45 @@ test_expect_success TTY 'no pager with --no-pager' '
        ! test -e paginated.out
 '
 
+test_expect_success TTY 'configuration can disable pager' '
+       rm -f paginated.out &&
+       test_might_fail git config --unset pager.grep &&
+       test_terminal git grep initial &&
+       test -e paginated.out &&
+
+       rm -f paginated.out &&
+       git config pager.grep false &&
+       test_when_finished "git config --unset pager.grep" &&
+       test_terminal git grep initial &&
+       ! test -e paginated.out
+'
+
+test_expect_success TTY 'git config uses a pager if configured to' '
+       rm -f paginated.out &&
+       git config pager.config true &&
+       test_when_finished "git config --unset pager.config" &&
+       test_terminal git config --list &&
+       test -e paginated.out
+'
+
+test_expect_success TTY 'configuration can enable pager (from subdir)' '
+       rm -f paginated.out &&
+       mkdir -p subdir &&
+       git config pager.bundle true &&
+       test_when_finished "git config --unset pager.bundle" &&
+
+       git bundle create test.bundle --all &&
+       rm -f paginated.out subdir/paginated.out &&
+       (
+               cd subdir &&
+               test_terminal git bundle unbundle ../test.bundle
+       ) &&
+       {
+               test -e paginated.out ||
+               test -e subdir/paginated.out
+       }
+'
+
 # A colored commit log will begin with an appropriate ANSI escape
 # for the first color; the text "commit" comes later.
 colorful() {
@@ -369,4 +423,16 @@ test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p'
 
 test_doesnt_paginate      expect_failure test_must_fail 'git -p nonsense'
 
+test_pager_choices                       'git shortlog'
+test_expect_success 'setup: configure shortlog not to paginate' '
+       git config pager.shortlog false
+'
+test_doesnt_paginate      expect_success 'git shortlog'
+test_no_local_config_subdir expect_success 'git shortlog'
+test_default_pager        expect_success 'git -p shortlog'
+test_core_pager_subdir    expect_success 'git -p shortlog'
+
+test_core_pager_subdir    expect_success test_must_fail \
+                                        'git -p apply </dev/null'
+
 test_done
index 7d8ed68befed0e85ad85f9f933a2b887125e38e8..3a43571cab78bce546ca714c8d53b76fc94c6ee2 100755 (executable)
@@ -438,4 +438,20 @@ test_expect_success 'force removal of nested git work tree' '
        ! test -d bar
 '
 
+test_expect_success 'git clean -e' '
+       rm -fr repo &&
+       mkdir repo &&
+       (
+               cd repo &&
+               git init &&
+               touch 1 2 3 known &&
+               git add known &&
+               git clean -f -e 1 -e 2 &&
+               test -e 1 &&
+               test -e 2 &&
+               ! (test -e 3) &&
+               test -e known
+       )
+'
+
 test_done
index 7538756487d9b9483143b338104cd6af13397a91..02522f9627eea8166b2d4709ef3519b23c8dd3d5 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success setup '
        echo file > file &&
        git add file &&
        test_tick &&
-       git commit -m upstream
+       git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
        (cd super &&
@@ -42,7 +42,7 @@ test_expect_success 'change submodule url' '
        ) &&
        mv submodule moved-submodule &&
        (cd super &&
-        git config -f .gitmodules submodule.submodule.url ../moved-submodule
+        git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
         test_tick &&
         git commit -a -m moved-submodule
        )
@@ -58,6 +58,9 @@ test_expect_success '"git submodule sync" should update submodule URLs' '
        (cd super-clone/submodule &&
         git checkout master &&
         git pull
+       ) &&
+       (cd super-clone &&
+        test -d "$(git config submodule.submodule.url)"
        )
 '
 
index 4a7b8933f4bf1036ba1e044d6e2dc54d72084039..7e2e258950772c91dfc04aff72ed74e95b5df884 100755 (executable)
@@ -54,21 +54,132 @@ test_expect_success setup '
        git merge -s ours a
 '
 
-test_expect_success 'merging with modify/modify conflict' '
+# History setup
+#
+#      b
+#    /   \
+#   a     d
+#    \   /
+#      c
+#
+# a in the main repository records to sub-a in the submodule and
+# analogous b and c. d should be automatically found by merging c into
+# b in the main repository.
+test_expect_success 'setup for merge search' '
+       mkdir merge-search &&
+       (cd merge-search &&
+       git init &&
+       mkdir sub &&
+       (cd sub &&
+        git init &&
+        echo "file-a" > file-a &&
+        git add file-a &&
+        git commit -m "sub-a" &&
+        git branch sub-a) &&
+       git add sub &&
+       git commit -m "a" &&
+       git branch a &&
+
+       git checkout -b b &&
+       (cd sub &&
+        git checkout -b sub-b &&
+        echo "file-b" > file-b &&
+        git add file-b &&
+        git commit -m "sub-b") &&
+       git commit -a -m "b" &&
+
+       git checkout -b c a &&
+       (cd sub &&
+        git checkout -b sub-c sub-a &&
+        echo "file-c" > file-c &&
+        git add file-c &&
+        git commit -m "sub-c") &&
+       git commit -a -m "c" &&
 
-       git checkout -b test1 a &&
-       test_must_fail git merge b &&
-       test -f .git/MERGE_MSG &&
-       git diff &&
-       test -n "$(git ls-files -u)"
+       git checkout -b d a &&
+       (cd sub &&
+        git checkout -b sub-d sub-b &&
+        git merge sub-c) &&
+       git commit -a -m "d" &&
+       git branch test b)
 '
 
-test_expect_success 'merging with a modify/modify conflict between merge bases' '
+test_expect_success 'merge with one side as a fast-forward of the other' '
+       (cd merge-search &&
+        git checkout -b test-forward b &&
+        git merge d &&
+        git ls-tree test-forward sub | cut -f1 | cut -f3 -d" " > actual &&
+        (cd sub &&
+         git rev-parse sub-d > ../expect) &&
+        test_cmp actual expect)
+'
 
+test_expect_success 'merging should conflict for non fast-forward' '
+       (cd merge-search &&
+        git checkout -b test-nonforward b &&
+        (cd sub &&
+         git rev-parse sub-d > ../expect) &&
+        test_must_fail git merge c 2> actual  &&
+        grep $(cat expect) actual > /dev/null &&
+        git reset --hard)
+'
+
+test_expect_success 'merging should fail for ambiguous common parent' '
+       (cd merge-search &&
+       git checkout -b test-ambiguous b &&
+       (cd sub &&
+        git checkout -b ambiguous sub-b &&
+        git merge sub-c &&
+        git rev-parse sub-d > ../expect1 &&
+        git rev-parse ambiguous > ../expect2) &&
+       test_must_fail git merge c 2> actual &&
+       grep $(cat expect1) actual > /dev/null &&
+       grep $(cat expect2) actual > /dev/null &&
+       git reset --hard)
+'
+
+# in a situation like this
+#
+# submodule tree:
+#
+#    sub-a --- sub-b --- sub-d
+#
+# main tree:
+#
+#    e (sub-a)
+#   /
+#  bb (sub-b)
+#   \
+#    f (sub-d)
+#
+# A merge between e and f should fail because one of the submodule
+# commits (sub-a) does not descend from the submodule merge-base (sub-b).
+#
+test_expect_success 'merging should fail for changes that are backwards' '
+       (cd merge-search &&
+       git checkout -b bb a &&
+       (cd sub &&
+        git checkout sub-b) &&
+       git commit -a -m "bb" &&
+
+       git checkout -b e bb &&
+       (cd sub &&
+        git checkout sub-a) &&
+       git commit -a -m "e" &&
+
+       git checkout -b f bb &&
+       (cd sub &&
+        git checkout sub-d) &&
+       git commit -a -m "f" &&
+
+       git checkout -b test-backward e &&
+       test_must_fail git merge f)
+'
+
+test_expect_success 'merging with a modify/modify conflict between merge bases' '
        git reset --hard HEAD &&
        git checkout -b test2 c &&
        git merge d
-
 '
 
 test_done
index 1382a8e58a14c8fcc92889aad492d5883fd64f2b..bfb4975e940e29edf3b06e5c074b8e963cfbbdcf 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'setup a submodule tree' '
        echo file > file &&
        git add file &&
        test_tick &&
-       git commit -m upstream
+       git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
        git clone super rebasing &&
index db9365b6454b18eb07f5c0798e13e18d696498c6..905a8baae9a03a4b47512bb3eb3e0f9ec84f5ca4 100755 (executable)
@@ -16,7 +16,7 @@ test_expect_success 'setup a submodule tree' '
        echo file > file &&
        git add file &&
        test_tick &&
-       git commit -m upstream
+       git commit -m upstream &&
        git clone . super &&
        git clone super submodule &&
        (
@@ -30,7 +30,7 @@ test_expect_success 'setup a submodule tree' '
                        submodule.sub2 submodule.foo2 &&
                git config -f .gitmodules --rename-section \
                        submodule.sub3 submodule.foo3 &&
-               git add .gitmodules
+               git add .gitmodules &&
                test_tick &&
                git commit -m "submodules" &&
                git submodule init sub1 &&
index a72fe3ae640378350102a07124aee201fb1c637b..9c14b853c07f26722953c8a1f778a41af8808cf1 100755 (executable)
@@ -808,24 +808,38 @@ test_expect_success POSIXPERM 'status succeeds in a read-only repository' '
        (exit $status)
 '
 
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar' && cd .. && git add sm)
+new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+touch .gitmodules
+
 cat > expect << EOF
 # On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   sm
+#
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
 #
 #      modified:   dir1/modified
 #
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
 #      expect
 #      output
 #      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 
 test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
@@ -834,19 +848,89 @@ test_expect_success '--ignore-submodules=untracked suppresses submodules with un
        test_cmp expect output
 '
 
+test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
+       git config diff.ignoreSubmodules dirty &&
+       git status >output &&
+       test_cmp expect output &&
+       git config --add -f .gitmodules submodule.subname.ignore untracked &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname &&
+       git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore untracked &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config --remove-section -f .gitmodules submodule.subname
+'
+
 test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
        git status --ignore-submodules=dirty > output &&
        test_cmp expect output
 '
 
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
+       git config diff.ignoreSubmodules dirty &&
+       git status >output &&
+       ! test -s actual &&
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname &&
+       git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore dirty &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
        echo modified > sm/foo &&
        git status --ignore-submodules=dirty > output &&
        test_cmp expect output
 '
 
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore dirty &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 cat > expect << EOF
 # On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   sm
+#
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
@@ -855,16 +939,21 @@ cat > expect << EOF
 #      modified:   dir1/modified
 #      modified:   sm (modified content)
 #
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
 #      expect
 #      output
 #      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 
 test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
@@ -872,10 +961,34 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodules w
        test_cmp expect output
 '
 
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
+       git config --add -f .gitmodules submodule.subname.ignore untracked &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore untracked &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
 
 cat > expect << EOF
 # On branch master
+# Changes to be committed:
+#   (use "git reset HEAD <file>..." to unstage)
+#
+#      modified:   sm
+#
 # Changed but not updated:
 #   (use "git add <file>..." to update what will be committed)
 #   (use "git checkout -- <file>..." to discard changes in working directory)
@@ -883,21 +996,26 @@ cat > expect << EOF
 #      modified:   dir1/modified
 #      modified:   sm (new commits)
 #
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+#   > Add bar
+#
 # Submodules changed but not updated:
 #
-# * sm $head...$head2 (1):
+# * sm $new_head...$head2 (1):
 #   > 2nd commit
 #
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
 #      expect
 #      output
 #      untracked
-no changes added to commit (use "git add" and/or "git commit -a")
 EOF
 
 test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
@@ -905,10 +1023,47 @@ test_expect_success "--ignore-submodules=untracked doesn't suppress submodule su
        test_cmp expect output
 '
 
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore untracked &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore untracked &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
        git status --ignore-submodules=dirty > output &&
        test_cmp expect output
 '
+test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore dirty &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore dirty &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
 
 cat > expect << EOF
 # On branch master
@@ -921,6 +1076,7 @@ cat > expect << EOF
 # Untracked files:
 #   (use "git add <file>..." to include in what will be committed)
 #
+#      .gitmodules
 #      dir1/untracked
 #      dir2/modified
 #      dir2/untracked
@@ -935,4 +1091,23 @@ test_expect_success "--ignore-submodules=all suppresses submodule summary" '
        test_cmp expect output
 '
 
+test_expect_failure '.gitmodules ignore=all suppresses submodule summary' '
+       git config --add -f .gitmodules submodule.subname.ignore all &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
+test_expect_failure '.git/config ignore=all suppresses submodule summary' '
+       git config --add -f .gitmodules submodule.subname.ignore none &&
+       git config --add -f .gitmodules submodule.subname.path sm &&
+       git config --add submodule.subname.ignore all &&
+       git config --add submodule.subname.path sm &&
+       git status > output &&
+       test_cmp expect output &&
+       git config --remove-section submodule.subname &&
+       git config -f .gitmodules  --remove-section submodule.subname
+'
+
 test_done
index 3ea33db6c73df763c22ccfab9f0cdcb2bbaef4d7..643ab03f992be8339b169b3bef0922ff3cdb04b9 100755 (executable)
@@ -111,7 +111,7 @@ test_expect_success '--amend option with empty author' '
        test_when_finished "git checkout Initial" &&
        echo "Empty author test" >>foo &&
        test_tick &&
-       ! git commit -a -m "empty author" --amend 2>err &&
+       test_must_fail git commit -a -m "empty author" --amend 2>err &&
        grep "empty ident" err
 '
 
@@ -125,7 +125,7 @@ test_expect_success '--amend option with missing author' '
        test_when_finished "git checkout Initial" &&
        echo "Missing author test" >>foo &&
        test_tick &&
-       ! git commit -a -m "malformed author" --amend 2>err &&
+       test_must_fail git commit -a -m "malformed author" --amend 2>err &&
        grep "empty ident" err
 '
 
index 49f4e1599acd829fdd930c1f9b5bc30ac719053d..d82349a6a8d6c12c3ca4398a18bd6c1dd42efdc9 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success 'setup' '
 test_expect_success 'will not overwrite untracked file' '
        git reset --hard c1 &&
        cat important > c2.c &&
-       ! git merge c2 &&
+       test_must_fail git merge c2 &&
        test_cmp important c2.c
 '
 
@@ -39,7 +39,7 @@ test_expect_success 'will not overwrite new file' '
        git reset --hard c1 &&
        cat important > c2.c &&
        git add c2.c &&
-       ! git merge c2 &&
+       test_must_fail git merge c2 &&
        test_cmp important c2.c
 '
 
@@ -48,7 +48,7 @@ test_expect_success 'will not overwrite staged changes' '
        cat important > c2.c &&
        git add c2.c &&
        rm c2.c &&
-       ! git merge c2 &&
+       test_must_fail git merge c2 &&
        git checkout c2.c &&
        test_cmp important c2.c
 '
@@ -58,7 +58,7 @@ test_expect_success 'will not overwrite removed file' '
        git rm c1.c &&
        git commit -m "rm c1.c" &&
        cat important > c1.c &&
-       ! git merge c1a &&
+       test_must_fail git merge c1a &&
        test_cmp important c1.c
 '
 
@@ -68,7 +68,7 @@ test_expect_success 'will not overwrite re-added file' '
        git commit -m "rm c1.c" &&
        cat important > c1.c &&
        git add c1.c &&
-       ! git merge c1a &&
+       test_must_fail git merge c1a &&
        test_cmp important c1.c
 '
 
@@ -79,7 +79,7 @@ test_expect_success 'will not overwrite removed file with staged changes' '
        cat important > c1.c &&
        git add c1.c &&
        rm c1.c &&
-       ! git merge c1a &&
+       test_must_fail git merge c1a &&
        git checkout c1.c &&
        test_cmp important c1.c
 '
diff --git a/t/t7609-merge-co-error-msgs.sh b/t/t7609-merge-co-error-msgs.sh
new file mode 100755 (executable)
index 0000000..1a109b4
--- /dev/null
@@ -0,0 +1,125 @@
+#!/bin/sh
+
+test_description='unpack-trees error messages'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup' '
+       echo one >one &&
+       git add one &&
+       git commit -a -m First &&
+
+       git checkout -b branch &&
+       echo two >two &&
+       echo three >three &&
+       echo four >four &&
+       echo five >five &&
+       git add two three four five &&
+       git commit -m Second &&
+
+       git checkout master &&
+       echo other >two &&
+       echo other >three &&
+       echo other >four &&
+       echo other >five
+'
+
+cat >expect <<\EOF
+error: The following untracked working tree files would be overwritten by merge:
+       two
+       three
+       four
+       five
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'untracked files overwritten by merge' '
+       test_must_fail git merge branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by merge:
+       two
+       three
+       four
+Please, commit your changes or stash them before you can merge.
+error: The following untracked working tree files would be overwritten by merge:
+       five
+Please move or remove them before you can merge.
+EOF
+
+test_expect_success 'untracked files or local changes ovewritten by merge' '
+       git add two &&
+       git add three &&
+       git add four &&
+       test_must_fail git merge branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+       rep/two
+       rep/one
+Please, commit your changes or stash them before you can switch branches.
+EOF
+
+test_expect_success 'cannot switch branches because of local changes' '
+       git add five &&
+       mkdir rep &&
+       echo one >rep/one &&
+       echo two >rep/two &&
+       git add rep/one rep/two &&
+       git commit -m Fourth &&
+       git checkout master &&
+       echo uno >rep/one &&
+       echo dos >rep/two &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Your local changes to the following files would be overwritten by checkout:
+       rep/two
+       rep/one
+Please, commit your changes or stash them before you can switch branches.
+EOF
+
+test_expect_success 'not uptodate file porcelain checkout error' '
+       git add rep/one rep/two &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out expect
+'
+
+cat >expect <<\EOF
+error: Updating the following directories would lose untracked files in it:
+       rep2
+       rep
+
+EOF
+
+test_expect_success 'not_uptodate_dir porcelain checkout error' '
+       git init uptodate &&
+       cd uptodate &&
+       mkdir rep &&
+       mkdir rep2 &&
+       touch rep/foo &&
+       touch rep2/foo &&
+       git add rep/foo rep2/foo &&
+       git commit -m init &&
+       git checkout -b branch &&
+       git rm rep -r &&
+       git rm rep2 -r &&
+       >rep &&
+       >rep2 &&
+       git add rep rep2&&
+       git commit -m "added test as a file" &&
+       git checkout master &&
+       >rep/untracked-file &&
+       >rep2/untracked-file &&
+       test_must_fail git checkout branch 2>out &&
+       test_cmp out ../expect
+'
+
+test_done
index e768c3eb2d48a9af2fc92729a6c20126f67ec866..3bd74042ef46ee8b4e4477d1c23ba0914963de33 100755 (executable)
@@ -14,6 +14,7 @@ Testing basic merge tool invocation'
 # running mergetool
 
 test_expect_success 'setup' '
+    git config rerere.enabled true &&
     echo master >file1 &&
     mkdir subdir &&
     echo master sub >subdir/file3 &&
@@ -67,23 +68,47 @@ test_expect_success 'mergetool crlf' '
 '
 
 test_expect_success 'mergetool in subdir' '
-    git checkout -b test3 branch1
-    cd subdir && (
-    test_must_fail git merge master >/dev/null 2>&1 &&
-    ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
-    test "$(cat file3)" = "master new sub" )
+    git checkout -b test3 branch1 &&
+    (
+       cd subdir &&
+       test_must_fail git merge master >/dev/null 2>&1 &&
+       ( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
+       test "$(cat file3)" = "master new sub"
+    )
 '
 
-# We can't merge files from parent directories when running mergetool
-# from a subdir. Is this a bug?
-#
-#test_expect_failure 'mergetool in subdir' '
-#    cd subdir && (
-#    ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
-#    ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
-#    test "$(cat ../file1)" = "master updated" &&
-#    test "$(cat ../file2)" = "master new" &&
-#    git commit -m "branch1 resolved with mergetool - subdir" )
-#'
+test_expect_success 'mergetool on file in parent dir' '
+    (
+       cd subdir &&
+       ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+       ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+       test "$(cat ../file1)" = "master updated" &&
+       test "$(cat ../file2)" = "master new" &&
+       git commit -m "branch1 resolved with mergetool - subdir"
+    )
+'
+
+test_expect_success 'mergetool skips autoresolved' '
+    git checkout -b test4 branch1 &&
+    test_must_fail git merge master &&
+    test -n "$(git ls-files -u)" &&
+    output="$(git mergetool --no-prompt)" &&
+    test "$output" = "No files need merging" &&
+    git reset --hard
+'
+
+test_expect_success 'mergetool merges all from subdir' '
+    (
+       cd subdir &&
+       git config rerere.enabled false &&
+       test_must_fail git merge master &&
+       git mergetool --no-prompt &&
+       test "$(cat ../file1)" = "master updated" &&
+       test "$(cat ../file2)" = "master new" &&
+       test "$(cat file3)" = "master new sub" &&
+       git add ../file1 ../file2 file3 &&
+       git commit -m "branch2 resolved by mergetool from subdir"
+    )
+'
 
 test_done
index 8a6322765c965bfc3a9158cc14312fdf03295090..023f225a4b1f22c66fb291a76690564696d56035 100755 (executable)
@@ -65,7 +65,7 @@ do
 
        test_expect_success "grep -w $L (w)" '
                : >expected &&
-               ! git grep -n -w -e "^w" >actual &&
+               test_must_fail git grep -n -w -e "^w" >actual &&
                test_cmp expected actual
        '
 
diff --git a/t/t9010-svn-fe.sh b/t/t9010-svn-fe.sh
new file mode 100755 (executable)
index 0000000..a713dfc
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='check svn dumpfile importer'
+
+. ./lib-git-svn.sh
+
+test_dump() {
+       label=$1
+       dump=$2
+       test_expect_success "$dump" '
+               svnadmin create "$label-svn" &&
+               svnadmin load "$label-svn" < "$TEST_DIRECTORY/$dump" &&
+               svn_cmd export "file://$PWD/$label-svn" "$label-svnco" &&
+               git init "$label-git" &&
+               test-svn-fe "$TEST_DIRECTORY/$dump" >"$label.fe" &&
+               (
+                       cd "$label-git" &&
+                       git fast-import < ../"$label.fe"
+               ) &&
+               (
+                       cd "$label-svnco" &&
+                       git init &&
+                       git add . &&
+                       git fetch "../$label-git" master &&
+                       git diff --exit-code FETCH_HEAD
+               )
+       '
+}
+
+test_dump simple t9135/svn.dump
+
+test_done
index 13766ab160e48d2b2ecb6e95079077e4a0704c2c..d5adae640b0b92e049de3c5d5e8996e1db06bcc4 100755 (executable)
@@ -271,6 +271,17 @@ test_expect_success 'able to dcommit to a subdirectory' "
        test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
        "
 
+test_expect_success 'dcommit should not fail with a touched file' '
+       test_commit "commit-new-file-foo2" foo2 &&
+       test-chmtime =-60 foo &&
+       git svn dcommit
+'
+
+test_expect_success 'rebase should not fail with a touched file' '
+       test-chmtime =-60 foo &&
+       git svn rebase
+'
+
 test_expect_success 'able to set-tree to a subdirectory' "
        echo cba > d &&
        git update-index d &&
index 134411e0a56142930a418ca15bd0902837c7bdc1..3c4f31925fe0498717b56ff95cf42a9a7450a236 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success 'setup svnrepo' '
        '
 
 test_expect_success 'start import with incomplete authors file' '
-       ! git svn clone --authors-file=svn-authors "$svnrepo" x
+       test_must_fail git svn clone --authors-file=svn-authors "$svnrepo" x
        '
 
 test_expect_success 'imported 2 revisions successfully' '
@@ -63,7 +63,7 @@ test_expect_success 'authors-file against globs' '
        '
 
 test_expect_success 'fetch fails on ee' '
-       ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+       ( cd aa-work && test_must_fail git svn fetch --authors-file=../svn-authors )
        '
 
 tmp_config_get () {
index f337959cccc78c40094ed6d81bdd502860e5e02b..22d80b0be2b94515132a79401b719f98794f4616 100755 (executable)
@@ -39,7 +39,7 @@ do
        (
                cd $H &&
                git config --unset i18n.commitencoding &&
-               ! git svn dcommit
+               test_must_fail git svn dcommit
        )
        '
 done
index 0735526d4ba4f2b692e000bcd4116be91d650355..e8559046296ce5570ec46b2b3a9280f0a493bbc4 100755 (executable)
@@ -41,7 +41,7 @@ test_expect_success 'modify hidden file in SVN repo' '
 test_expect_success 'fetch fails on modified hidden file' '
        ( cd g &&
          git svn find-rev refs/remotes/git-svn > ../expect &&
-         ! git svn fetch 2> ../errors &&
+         test_must_fail git svn fetch 2> ../errors &&
          git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
        fgrep "not found in commit" errors &&
        test_cmp expect expect2
diff --git a/t/t9155-git-svn-fetch-deleted-tag.sh b/t/t9155-git-svn-fetch-deleted-tag.sh
new file mode 100755 (executable)
index 0000000..a486a98
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+       mkdir -p import/trunk/subdir &&
+       mkdir -p import/branches &&
+       mkdir -p import/tags &&
+       echo "base" >import/trunk/subdir/file &&
+       svn_cmd import -m "import for git svn" import "$svnrepo" &&
+       rm -rf import &&
+
+       svn_cmd mkdir -m "create mybranch directory" "$svnrepo/branches/mybranch" &&
+       svn_cmd cp -m "create branch mybranch" "$svnrepo/trunk" "$svnrepo/branches/mybranch/trunk" &&
+
+       svn_cmd co "$svnrepo/trunk" svn_project &&
+       (cd svn_project &&
+               echo "trunk change" >>subdir/file &&
+               svn_cmd ci -m "trunk change" subdir/file &&
+
+               svn_cmd switch "$svnrepo/branches/mybranch/trunk" &&
+               echo "branch change" >>subdir/file &&
+               svn_cmd ci -m "branch change" subdir/file
+       ) &&
+
+       svn_cmd cp -m "create mytag attempt 1" -r5 "$svnrepo/trunk/subdir" "$svnrepo/tags/mytag" &&
+       svn_cmd rm -m "delete mytag attempt 1" "$svnrepo/tags/mytag" &&
+       svn_cmd cp -m "create mytag attempt 2" -r5 "$svnrepo/branches/mybranch/trunk/subdir" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with checksum error' '
+       git svn init --stdlayout "$svnrepo" git_project &&
+       cd git_project &&
+       git svn fetch &&
+
+       git diff --exit-code mybranch:trunk/subdir/file tags/mytag:file &&
+       git diff --exit-code master:subdir/file tags/mytag^:file
+'
+
+test_done
diff --git a/t/t9156-git-svn-fetch-deleted-tag-2.sh b/t/t9156-git-svn-fetch-deleted-tag-2.sh
new file mode 100755 (executable)
index 0000000..5ce7e2f
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description='git svn fetch deleted tag 2'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+       mkdir -p import/branches &&
+       mkdir -p import/tags &&
+       mkdir -p import/trunk/subdir1 &&
+       mkdir -p import/trunk/subdir2 &&
+       mkdir -p import/trunk/subdir3 &&
+       echo "file1" >import/trunk/subdir1/file &&
+       echo "file2" >import/trunk/subdir2/file &&
+       echo "file3" >import/trunk/subdir3/file &&
+       svn_cmd import -m "import for git svn" import "$svnrepo" &&
+       rm -rf import &&
+
+       svn_cmd co "$svnrepo/trunk" svn_project &&
+       (cd svn_project &&
+               echo "change1" >>subdir1/file &&
+               echo "change2" >>subdir2/file &&
+               echo "change3" >>subdir3/file &&
+               svn_cmd ci -m "change" .
+       ) &&
+
+       svn_cmd cp -m "create mytag 1" -r2 "$svnrepo/trunk/subdir1" "$svnrepo/tags/mytag" &&
+       svn_cmd rm -m "delete mytag 1" "$svnrepo/tags/mytag" &&
+       svn_cmd cp -m "create mytag 2" -r2 "$svnrepo/trunk/subdir2" "$svnrepo/tags/mytag" &&
+       svn_cmd rm -m "delete mytag 2" "$svnrepo/tags/mytag" &&
+       svn_cmd cp -m "create mytag 3" -r2 "$svnrepo/trunk/subdir3" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with no checksum error' '
+       git svn init --stdlayout "$svnrepo" git_project &&
+       cd git_project &&
+       git svn fetch &&
+
+       git diff --exit-code master:subdir3/file tags/mytag:file &&
+       git diff --exit-code master:subdir2/file tags/mytag^:file &&
+       git diff --exit-code master:subdir1/file tags/mytag^^:file
+'
+
+test_done
index 131f03298809ad193cc75ab77deda6daaf713d1f..96d07f183377f8822257cb3a1dad29023b391462 100755 (executable)
@@ -166,6 +166,63 @@ test_expect_success \
         test `git rev-parse --verify master:file2` \
            = `git rev-parse --verify verify--import-marks:copy-of-file2`'
 
+test_tick
+mt=$(git hash-object --stdin < /dev/null)
+: >input.blob
+: >marks.exp
+: >tree.exp
+
+cat >input.commit <<EOF
+commit refs/heads/verify--dump-marks
+committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+data <<COMMIT
+test the sparse array dumping routines with exponentially growing marks
+COMMIT
+EOF
+
+i=0
+l=4
+m=6
+n=7
+while test "$i" -lt 27; do
+    cat >>input.blob <<EOF
+blob
+mark :$l
+data 0
+blob
+mark :$m
+data 0
+blob
+mark :$n
+data 0
+EOF
+    echo "M 100644 :$l l$i" >>input.commit
+    echo "M 100644 :$m m$i" >>input.commit
+    echo "M 100644 :$n n$i" >>input.commit
+
+    echo ":$l $mt" >>marks.exp
+    echo ":$m $mt" >>marks.exp
+    echo ":$n $mt" >>marks.exp
+
+    printf "100644 blob $mt\tl$i\n" >>tree.exp
+    printf "100644 blob $mt\tm$i\n" >>tree.exp
+    printf "100644 blob $mt\tn$i\n" >>tree.exp
+
+    l=$(($l + $l))
+    m=$(($m + $m))
+    n=$(($l + $n))
+
+    i=$((1 + $i))
+done
+
+sort tree.exp > tree.exp_s
+
+test_expect_success 'A: export marks with large values' '
+       cat input.blob input.commit | git fast-import --export-marks=marks.large &&
+       git ls-tree refs/heads/verify--dump-marks >tree.out &&
+       test_cmp tree.exp_s tree.out &&
+       test_cmp marks.exp marks.large'
+
 ###
 ### series B
 ###
@@ -796,6 +853,60 @@ test_expect_success \
        'git fast-import <input &&
         test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
 
+test_expect_success \
+       'N: copy directory by id' \
+       'cat >expect <<-\EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
+       EOF
+        subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy by tree hash
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $subdir file3
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+        compare_diff_raw expect actual'
+
+test_expect_success \
+       'N: modify copied tree' \
+       'cat >expect <<-\EOF &&
+       :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100   newdir/interesting      file3/file5
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
+       EOF
+        subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+        cat >input <<-INPUT_END &&
+       commit refs/heads/N5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy by tree hash
+       COMMIT
+
+       from refs/heads/branch^0
+       M 040000 $subdir file3
+
+       commit refs/heads/N5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       modify directory copy
+       COMMIT
+
+       M 644 inline file3/file5
+       data <<EOF
+       $file5_data
+       EOF
+       INPUT_END
+        git fast-import <input &&
+        git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+        compare_diff_raw expect actual'
+
 ###
 ### series O
 ###
index d43f37ccafb4ecf6f649a5504d2626f652d48d02..8c8e679468f4b191f93ca68a973d4d58fa1b72d2 100755 (executable)
@@ -355,6 +355,20 @@ test_expect_failure 'no exact-ref revisions included' '
        )
 '
 
+test_expect_success 'path limiting with import-marks does not lose unmodified files'        '
+       git checkout -b simple marks~2 &&
+       git fast-export --export-marks=marks simple -- file > /dev/null &&
+       echo more content >> file &&
+       test_tick &&
+       git commit -mnext file &&
+       git fast-export --import-marks=marks simple -- file file0 | grep file0
+'
+
+test_expect_success 'full-tree re-shows unmodified files'        '
+       git checkout -f simple &&
+       test $(git fast-export --full-tree simple | grep -c file0) -eq 3
+'
+
 test_expect_success 'set-up a few more tags for tag export tests' '
        git checkout -f master &&
        HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
@@ -376,4 +390,28 @@ test_expect_success 'tree_tag-obj'    'git fast-export tree_tag-obj'
 test_expect_success 'tag-obj_tag'     'git fast-export tag-obj_tag'
 test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
 
+test_expect_success SYMLINKS 'directory becomes symlink'        '
+       git init dirtosymlink &&
+       git init result &&
+       (
+               cd dirtosymlink &&
+               mkdir foo &&
+               mkdir bar &&
+               echo hello > foo/world &&
+               echo hello > bar/world &&
+               git add foo/world bar/world &&
+               git commit -q -mone &&
+               git rm -r foo &&
+               ln -s bar foo &&
+               git add foo &&
+               git commit -q -mtwo
+       ) &&
+       (
+               cd dirtosymlink &&
+               git fast-export master -- foo |
+               (cd ../result && git fast-import --quiet)
+       ) &&
+       (cd result && git show master:foo)
+'
+
 test_done
index e5523dd690a43fdcf315e7e952d4d316182eeb25..3a3d4c4723d4dece710f4f959ab689e1f8fb8760 100644 (file)
@@ -127,14 +127,13 @@ do
        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
                verbose=t; shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
-               quiet=t; shift ;;
+               # Ignore --quiet under a TAP::Harness. Saying how many tests
+               # passed without the ok/not ok details is always an error.
+               test -z "$HARNESS_ACTIVE" && quiet=t; shift ;;
        --with-dashes)
                with_dashes=t; shift ;;
        --no-color)
                color=; shift ;;
-       --no-python)
-               # noop now...
-               shift ;;
        --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
                valgrind=t; verbose=t; shift ;;
        --tee)
@@ -257,6 +256,10 @@ q_to_cr () {
        tr Q '\015'
 }
 
+q_to_tab () {
+       tr Q '\011'
+}
+
 append_cr () {
        sed -e 's/$/Q/' | tr Q '\015'
 }
@@ -542,6 +545,38 @@ test_external_without_stderr () {
        fi
 }
 
+# debugging-friendly alternatives to "test [-f|-d|-e]"
+# The commands test the existence or non-existence of $1. $2 can be
+# given to provide a more precise diagnosis.
+test_path_is_file () {
+       if ! [ -f "$1" ]
+       then
+               echo "File $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_dir () {
+       if ! [ -d "$1" ]
+       then
+               echo "Directory $1 doesn't exist. $*"
+               false
+       fi
+}
+
+test_path_is_missing () {
+       if [ -e "$1" ]
+       then
+               echo "Path exists:"
+               ls -ld "$1"
+               if [ $# -ge 1 ]; then
+                       echo "$*"
+               fi
+               false
+       fi
+}
+
+
 # This is not among top-level (test_expect_success | test_expect_failure)
 # but is a prefix that can be used in the test script, like:
 #
diff --git a/test-line-buffer.c b/test-line-buffer.c
new file mode 100644 (file)
index 0000000..c11bf7f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * test-line-buffer.c: code to exercise the svn importer's input helper
+ *
+ * Input format:
+ *     number NL
+ *     (number bytes) NL
+ *     number NL
+ *     ...
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/line_buffer.h"
+
+static uint32_t strtouint32(const char *s)
+{
+       char *end;
+       uintmax_t n = strtoumax(s, &end, 10);
+       if (*s == '\0' || *end != '\0')
+               die("invalid count: %s", s);
+       return (uint32_t) n;
+}
+
+int main(int argc, char *argv[])
+{
+       char *s;
+
+       if (argc != 1)
+               usage("test-line-buffer < input.txt");
+       if (buffer_init(NULL))
+               die_errno("open error");
+       while ((s = buffer_read_line())) {
+               s = buffer_read_string(strtouint32(s));
+               fputs(s, stdout);
+               fputc('\n', stdout);
+               buffer_skip_bytes(1);
+               if (!(s = buffer_read_line()))
+                       break;
+               buffer_copy_bytes(strtouint32(s) + 1);
+       }
+       if (buffer_deinit())
+               die("input error");
+       if (ferror(stdout))
+               die("output error");
+       buffer_reset();
+       return 0;
+}
diff --git a/test-obj-pool.c b/test-obj-pool.c
new file mode 100644 (file)
index 0000000..5018863
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * test-obj-pool.c: code to exercise the svn importer's object pool
+ */
+
+#include "cache.h"
+#include "vcs-svn/obj_pool.h"
+
+enum pool { POOL_ONE, POOL_TWO };
+obj_pool_gen(one, int, 1)
+obj_pool_gen(two, int, 4096)
+
+static uint32_t strtouint32(const char *s)
+{
+       char *end;
+       uintmax_t n = strtoumax(s, &end, 10);
+       if (*s == '\0' || (*end != '\n' && *end != '\0'))
+               die("invalid offset: %s", s);
+       return (uint32_t) n;
+}
+
+static void handle_command(const char *command, enum pool pool, const char *arg)
+{
+       switch (*command) {
+       case 'a':
+               if (!prefixcmp(command, "alloc ")) {
+                       uint32_t n = strtouint32(arg);
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_alloc(n) : two_alloc(n));
+                       return;
+               }
+       case 'c':
+               if (!prefixcmp(command, "commit ")) {
+                       pool == POOL_ONE ? one_commit() : two_commit();
+                       return;
+               }
+               if (!prefixcmp(command, "committed ")) {
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_pool.committed : two_pool.committed);
+                       return;
+               }
+       case 'f':
+               if (!prefixcmp(command, "free ")) {
+                       uint32_t n = strtouint32(arg);
+                       pool == POOL_ONE ? one_free(n) : two_free(n);
+                       return;
+               }
+       case 'n':
+               if (!prefixcmp(command, "null ")) {
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_offset(NULL) : two_offset(NULL));
+                       return;
+               }
+       case 'o':
+               if (!prefixcmp(command, "offset ")) {
+                       uint32_t n = strtouint32(arg);
+                       printf("%"PRIu32"\n",
+                               pool == POOL_ONE ?
+                               one_offset(one_pointer(n)) :
+                               two_offset(two_pointer(n)));
+                       return;
+               }
+       case 'r':
+               if (!prefixcmp(command, "reset ")) {
+                       pool == POOL_ONE ? one_reset() : two_reset();
+                       return;
+               }
+       case 's':
+               if (!prefixcmp(command, "set ")) {
+                       uint32_t n = strtouint32(arg);
+                       if (pool == POOL_ONE)
+                               *one_pointer(n) = 1;
+                       else
+                               *two_pointer(n) = 1;
+                       return;
+               }
+       case 't':
+               if (!prefixcmp(command, "test ")) {
+                       uint32_t n = strtouint32(arg);
+                       printf("%d\n", pool == POOL_ONE ?
+                               *one_pointer(n) : *two_pointer(n));
+                       return;
+               }
+       default:
+               die("unrecognized command: %s", command);
+       }
+}
+
+static void handle_line(const char *line)
+{
+       const char *arg = strchr(line, ' ');
+       enum pool pool;
+
+       if (arg && !prefixcmp(arg + 1, "one"))
+               pool = POOL_ONE;
+       else if (arg && !prefixcmp(arg + 1, "two"))
+               pool = POOL_TWO;
+       else
+               die("no pool specified: %s", line);
+
+       handle_command(line, pool, arg + strlen("one "));
+}
+
+int main(int argc, char *argv[])
+{
+       struct strbuf sb = STRBUF_INIT;
+       if (argc != 1)
+               usage("test-obj-str < script");
+
+       while (strbuf_getline(&sb, stdin, '\n') != EOF)
+               handle_line(sb.buf);
+       strbuf_release(&sb);
+       return 0;
+}
diff --git a/test-string-pool.c b/test-string-pool.c
new file mode 100644 (file)
index 0000000..c5782e6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * test-string-pool.c: code to exercise the svn importer's string pool
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/string_pool.h"
+
+int main(int argc, char *argv[])
+{
+       const uint32_t unequal = pool_intern("does not equal");
+       const uint32_t equal = pool_intern("equals");
+       uint32_t buf[3];
+       uint32_t n;
+
+       if (argc != 2)
+               usage("test-string-pool <string>,<string>");
+
+       n = pool_tok_seq(3, buf, ",-", argv[1]);
+       if (n >= 3)
+               die("too many strings");
+       if (n <= 1)
+               die("too few strings");
+
+       buf[2] = buf[1];
+       buf[1] = (buf[0] == buf[2]) ? equal : unequal;
+       pool_print_seq(3, buf, ' ', stdout);
+       fputc('\n', stdout);
+
+       pool_reset();
+       return 0;
+}
diff --git a/test-svn-fe.c b/test-svn-fe.c
new file mode 100644 (file)
index 0000000..77cf78a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * test-svn-fe: Code to exercise the svn import lib
+ */
+
+#include "git-compat-util.h"
+#include "vcs-svn/svndump.h"
+
+int main(int argc, char *argv[])
+{
+       if (argc != 2)
+               usage("test-svn-fe <file>");
+       svndump_init(argv[1]);
+       svndump_read(NULL);
+       svndump_deinit();
+       svndump_reset();
+       return 0;
+}
diff --git a/test-treap.c b/test-treap.c
new file mode 100644 (file)
index 0000000..cdba511
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * test-treap.c: code to exercise the svn importer's treap structure
+ */
+
+#include "cache.h"
+#include "vcs-svn/obj_pool.h"
+#include "vcs-svn/trp.h"
+
+struct int_node {
+       uintmax_t n;
+       struct trp_node children;
+};
+
+obj_pool_gen(node, struct int_node, 3)
+
+static int node_cmp(struct int_node *a, struct int_node *b)
+{
+       return (a->n > b->n) - (a->n < b->n);
+}
+
+trp_gen(static, treap_, struct int_node, children, node, node_cmp)
+
+static void strtonode(struct int_node *item, const char *s)
+{
+       char *end;
+       item->n = strtoumax(s, &end, 10);
+       if (*s == '\0' || (*end != '\n' && *end != '\0'))
+               die("invalid integer: %s", s);
+}
+
+int main(int argc, char *argv[])
+{
+       struct strbuf sb = STRBUF_INIT;
+       struct trp_root root = { ~0 };
+       uint32_t item;
+
+       if (argc != 1)
+               usage("test-treap < ints");
+
+       while (strbuf_getline(&sb, stdin, '\n') != EOF) {
+               item = node_alloc(1);
+               strtonode(node_pointer(item), sb.buf);
+               treap_insert(&root, node_pointer(item));
+       }
+
+       item = node_offset(treap_first(&root));
+       while (~item) {
+               uint32_t next;
+               struct int_node *tmp = node_pointer(node_alloc(1));
+
+               tmp->n = node_pointer(item)->n;
+               next = node_offset(treap_next(&root, node_pointer(item)));
+
+               treap_remove(&root, node_pointer(item));
+               item = node_offset(treap_nsearch(&root, tmp));
+
+               if (item != next && (!~item || node_pointer(item)->n != tmp->n))
+                       die("found %"PRIuMAX" in place of %"PRIuMAX"",
+                               ~item ? node_pointer(item)->n : ~(uintmax_t) 0,
+                               ~next ? node_pointer(next)->n : ~(uintmax_t) 0);
+               printf("%"PRIuMAX"\n", tmp->n);
+       }
+       node_reset();
+       return 0;
+}
index 191fbf798a5e5db1abeafc719e8ec0d986a1050c..acfc88e3f1f0e99863512bf2c6ea82f1283cc35d 100644 (file)
@@ -689,7 +689,7 @@ static int push_refs_with_export(struct transport *transport,
        struct child_process *helper, exporter;
        struct helper_data *data = transport->data;
        char *export_marks = NULL, *import_marks = NULL;
-       struct string_list revlist_args = { NULL, 0, 0 };
+       struct string_list revlist_args = STRING_LIST_INIT_NODUP;
        struct strbuf buf = STRBUF_INIT;
 
        helper = get_helper(transport);
index 1fb3e94614de4973a242b1f039ee7711a74fad9d..cd659c6fe447b6fcdedee2843113feb358f23849 100644 (file)
@@ -359,6 +359,7 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        diff_tree_release_paths(&diff_opts);
 
        /* Go through the new set of filepairing, and see if we find a more interesting one */
+       opt->found_follow = 0;
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
 
@@ -376,6 +377,16 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
                        diff_tree_release_paths(opt);
                        opt->paths[0] = xstrdup(p->one->path);
                        diff_tree_setup_paths(opt->paths, opt);
+
+                       /*
+                        * The caller expects us to return a set of vanilla
+                        * filepairs to let a later call to diffcore_std()
+                        * it makes to sort the renames out (among other
+                        * things), but we already have found renames
+                        * ourselves; signal diffcore_std() not to muck with
+                        * rename information.
+                        */
+                       opt->found_follow = 1;
                        break;
                }
        }
@@ -412,7 +423,7 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
        init_tree_desc(&t1, tree1, size1);
        init_tree_desc(&t2, tree2, size2);
        retval = diff_tree(&t1, &t2, base, opt);
-       if (DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
+       if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
                init_tree_desc(&t1, tree1, size1);
                init_tree_desc(&t2, tree2, size2);
                try_to_follow_renames(&t1, &t2, base, opt);
index 67a9a0c5a5bf7d7125765679318cfcd68c160da7..a9bbf4e2354df5ce6b010873b419731343a12c7d 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "unpack-trees.h"
 #include "tree.h"
 
 static const char *get_mode(const char *str, unsigned int *modep)
@@ -310,6 +311,7 @@ static void free_extended_entry(struct tree_desc_x *t)
 int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 {
        int ret = 0;
+       int error = 0;
        struct name_entry *entry = xmalloc(n*sizeof(*entry));
        int i;
        struct tree_desc_x *tx = xcalloc(n, sizeof(*tx));
@@ -377,8 +379,11 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
                if (!mask)
                        break;
                ret = info->fn(n, mask, dirmask, entry, info);
-               if (ret < 0)
-                       break;
+               if (ret < 0) {
+                       error = ret;
+                       if (!info->show_all_errors)
+                               break;
+               }
                mask &= ret;
                ret = 0;
                for (i = 0; i < n; i++)
@@ -389,7 +394,7 @@ int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
        for (i = 0; i < n; i++)
                free_extended_entry(tx + i);
        free(tx);
-       return ret;
+       return error;
 }
 
 static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
index 42110a465f9a8c91d1bc643dfae7a9b9c32e3719..7e3e0b5ad16710c06464726ac04d2b1c48af3708 100644 (file)
@@ -28,7 +28,10 @@ static inline int tree_entry_len(const char *name, const unsigned char *sha1)
 void update_tree_entry(struct tree_desc *);
 void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
 
-/* Helper function that does both of the above and returns true for success */
+/*
+ * Helper function that does both tree_entry_extract() and update_tree_entry()
+ * and returns true for success
+ */
 int tree_entry(struct tree_desc *, struct name_entry *);
 
 void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1);
@@ -45,6 +48,7 @@ struct traverse_info {
        unsigned long conflicts;
        traverse_callback_t fn;
        void *data;
+       int show_all_errors;
 };
 
 int get_tree_entry(const unsigned char *, const char *, unsigned char *, unsigned *);
index 8cf0da317d8afe4bfe3cae7051e3a385d44cf7ef..3c7a7c9cde745c1b438f60aceea7e556dded81d7 100644 (file)
  * Error messages expected by scripts out of plumbing commands such as
  * read-tree.  Non-scripted Porcelain is not required to use these messages
  * and in fact are encouraged to reword them to better suit their particular
- * situation better.  See how "git checkout" replaces not_uptodate_file to
- * explain why it does not allow switching between branches when you have
- * local changes, for example.
+ * situation better.  See how "git checkout" and "git merge" replaces
+ * them using set_porcelain_error_msgs(), for example.
  */
-static struct unpack_trees_error_msgs unpack_plumbing_errors = {
-       /* would_overwrite */
+const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
+       /* ERROR_WOULD_OVERWRITE */
        "Entry '%s' would be overwritten by merge. Cannot merge.",
 
-       /* not_uptodate_file */
+       /* ERROR_NOT_UPTODATE_FILE */
        "Entry '%s' not uptodate. Cannot merge.",
 
-       /* not_uptodate_dir */
+       /* ERROR_NOT_UPTODATE_DIR */
        "Updating '%s' would lose untracked files in it",
 
-       /* would_lose_untracked */
-       "Untracked working tree file '%s' would be %s by merge.",
+       /* ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN */
+       "Untracked working tree file '%s' would be overwritten by merge.",
 
-       /* bind_overlap */
+       /* ERROR_WOULD_LOSE_UNTRACKED_REMOVED */
+       "Untracked working tree file '%s' would be removed by merge.",
+
+       /* ERROR_BIND_OVERLAP */
        "Entry '%s' overlaps with '%s'.  Cannot bind.",
 
-       /* sparse_not_uptodate_file */
+       /* ERROR_SPARSE_NOT_UPTODATE_FILE */
        "Entry '%s' not uptodate. Cannot update sparse checkout.",
 
-       /* would_lose_orphaned */
-       "Working tree file '%s' would be %s by sparse checkout update.",
+       /* ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN */
+       "Working tree file '%s' would be overwritten by sparse checkout update.",
+
+       /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
+       "Working tree file '%s' would be removed by sparse checkout update.",
 };
 
-#define ERRORMSG(o,fld) \
-       ( ((o) && (o)->msgs.fld) \
-       ? ((o)->msgs.fld) \
-       : (unpack_plumbing_errors.fld) )
+#define ERRORMSG(o,type) \
+       ( ((o) && (o)->msgs[(type)]) \
+         ? ((o)->msgs[(type)])      \
+         : (unpack_plumbing_errors[(type)]) )
 
 static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
        unsigned int set, unsigned int clear)
@@ -53,12 +58,82 @@ static void add_entry(struct unpack_trees_options *o, struct cache_entry *ce,
 
        clear |= CE_HASHED | CE_UNHASHED;
 
+       if (set & CE_REMOVE)
+               set |= CE_WT_REMOVE;
+
        memcpy(new, ce, size);
        new->next = NULL;
        new->ce_flags = (new->ce_flags & ~clear) | set;
        add_index_entry(&o->result, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 }
 
+/*
+ * add error messages on path <path>
+ * corresponding to the type <e> with the message <msg>
+ * indicating if it should be display in porcelain or not
+ */
+static int add_rejected_path(struct unpack_trees_options *o,
+                            enum unpack_trees_error_types e,
+                            const char *path)
+{
+       struct rejected_paths_list *newentry;
+       int porcelain = o && (o)->msgs[e];
+       /*
+        * simply display the given error message if in plumbing mode
+        */
+       if (!porcelain)
+               o->show_all_errors = 0;
+       if (!o->show_all_errors)
+               return error(ERRORMSG(o, e), path);
+
+       /*
+        * Otherwise, insert in a list for future display by
+        * display_error_msgs()
+        */
+       newentry = xmalloc(sizeof(struct rejected_paths_list));
+       newentry->path = (char *)path;
+       newentry->next = o->unpack_rejects[e];
+       o->unpack_rejects[e] = newentry;
+       return -1;
+}
+
+/*
+ * free all the structures allocated for the error <e>
+ */
+static void free_rejected_paths(struct unpack_trees_options *o,
+                               enum unpack_trees_error_types e)
+{
+       while (o->unpack_rejects[e]) {
+               struct rejected_paths_list *del = o->unpack_rejects[e];
+               o->unpack_rejects[e] = o->unpack_rejects[e]->next;
+               free(del);
+       }
+       free(o->unpack_rejects[e]);
+}
+
+/*
+ * display all the error messages stored in a nice way
+ */
+static void display_error_msgs(struct unpack_trees_options *o)
+{
+       int e;
+       int something_displayed = 0;
+       for (e = 0; e < NB_UNPACK_TREES_ERROR_TYPES; e++) {
+               if (o->unpack_rejects[e]) {
+                       struct rejected_paths_list *rp;
+                       struct strbuf path = STRBUF_INIT;
+                       something_displayed = 1;
+                       for (rp = o->unpack_rejects[e]; rp; rp = rp->next)
+                               strbuf_addf(&path, "\t%s\n", rp->path);
+                       error(ERRORMSG(o, e), path.buf);
+                       strbuf_release(&path);
+                       free_rejected_paths(o, e);
+               }
+       }
+       if (something_displayed)
+               printf("Aborting\n");
+}
+
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
@@ -84,7 +159,7 @@ static int check_updates(struct unpack_trees_options *o)
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
                        struct cache_entry *ce = index->cache[cnt];
-                       if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
+                       if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
                                total++;
                }
 
@@ -104,12 +179,6 @@ static int check_updates(struct unpack_trees_options *o)
                                unlink_entry(ce);
                        continue;
                }
-
-               if (ce->ce_flags & CE_REMOVE) {
-                       display_progress(progress, ++cnt);
-                       if (o->update)
-                               unlink_entry(ce);
-               }
        }
        remove_marked_cache_entries(&o->result);
        remove_scheduled_dirs();
@@ -132,15 +201,12 @@ static int check_updates(struct unpack_trees_options *o)
 }
 
 static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
-static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);
+static int verify_absent_sparse(struct cache_entry *ce, enum unpack_trees_error_types, struct unpack_trees_options *o);
 
 static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)
 {
        const char *basename;
 
-       if (ce_stage(ce))
-               return 0;
-
        basename = strrchr(ce->name, '/');
        basename = basename ? basename+1 : ce->name;
        return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
@@ -150,19 +216,36 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
 {
        int was_skip_worktree = ce_skip_worktree(ce);
 
-       if (will_have_skip_worktree(ce, o))
+       if (!ce_stage(ce) && will_have_skip_worktree(ce, o))
                ce->ce_flags |= CE_SKIP_WORKTREE;
        else
                ce->ce_flags &= ~CE_SKIP_WORKTREE;
 
        /*
-        * We only care about files getting into the checkout area
-        * If merge strategies want to remove some, go ahead, this
-        * flag will be removed eventually in unpack_trees() if it's
-        * outside checkout area.
+        * if (!was_skip_worktree && !ce_skip_worktree()) {
+        *      This is perfectly normal. Move on;
+        * }
         */
-       if (ce->ce_flags & CE_REMOVE)
-               return 0;
+
+       /*
+        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
+        * area as a result of ce_skip_worktree() shortcuts in
+        * verify_absent() and verify_uptodate().
+        * Make sure they don't modify worktree if they are already
+        * outside checkout area
+        */
+       if (was_skip_worktree && ce_skip_worktree(ce)) {
+               ce->ce_flags &= ~CE_UPDATE;
+
+               /*
+                * By default, when CE_REMOVE is on, CE_WT_REMOVE is also
+                * on to get that file removed from both index and worktree.
+                * If that file is already outside worktree area, don't
+                * bother remove it.
+                */
+               if (ce->ce_flags & CE_REMOVE)
+                       ce->ce_flags &= ~CE_WT_REMOVE;
+       }
 
        if (!was_skip_worktree && ce_skip_worktree(ce)) {
                /*
@@ -175,7 +258,7 @@ static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_opt
                ce->ce_flags |= CE_WT_REMOVE;
        }
        if (was_skip_worktree && !ce_skip_worktree(ce)) {
-               if (verify_absent_sparse(ce, "overwritten", o))
+               if (verify_absent_sparse(ce, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
                ce->ce_flags |= CE_UPDATE;
        }
@@ -329,6 +412,7 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long
 {
        int i, ret, bottom;
        struct tree_desc t[MAX_UNPACK_TREES];
+       void *buf[MAX_UNPACK_TREES];
        struct traverse_info newinfo;
        struct name_entry *p;
 
@@ -346,12 +430,16 @@ static int traverse_trees_recursive(int n, unsigned long dirmask, unsigned long
                const unsigned char *sha1 = NULL;
                if (dirmask & 1)
                        sha1 = names[i].sha1;
-               fill_tree_descriptor(t+i, sha1);
+               buf[i] = fill_tree_descriptor(t+i, sha1);
        }
 
        bottom = switch_cache_bottom(&newinfo);
        ret = traverse_trees(n, t, &newinfo);
        restore_cache_bottom(&newinfo, bottom);
+
+       for (i = 0; i < n; i++)
+               free(buf[i]);
+
        return ret;
 }
 
@@ -750,6 +838,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                setup_traverse_info(&info, prefix);
                info.fn = unpack_callback;
                info.data = o;
+               info.show_all_errors = o->show_all_errors;
 
                if (o->prefix) {
                        /*
@@ -798,14 +887,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                                ret = -1;
                                goto done;
                        }
-                       /*
-                        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
-                        * area as a result of ce_skip_worktree() shortcuts in
-                        * verify_absent() and verify_uptodate(). Clear them.
-                        */
-                       if (ce_skip_worktree(ce))
-                               ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
-                       else
+                       if (!ce_skip_worktree(ce))
                                empty_worktree = 0;
 
                }
@@ -829,6 +911,8 @@ done:
        return ret;
 
 return_failed:
+       if (o->show_all_errors)
+               display_error_msgs(o);
        mark_all_ce_unused(o->src_index);
        ret = unpack_failed(o, NULL);
        goto done;
@@ -838,7 +922,7 @@ return_failed:
 
 static int reject_merge(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       return error(ERRORMSG(o, would_overwrite), ce->name);
+       return add_rejected_path(o, ERROR_WOULD_OVERWRITE, ce->name);
 }
 
 static int same(struct cache_entry *a, struct cache_entry *b)
@@ -860,7 +944,7 @@ static int same(struct cache_entry *a, struct cache_entry *b)
  */
 static int verify_uptodate_1(struct cache_entry *ce,
                                   struct unpack_trees_options *o,
-                                  const char *error_msg)
+                                  enum unpack_trees_error_types error_type)
 {
        struct stat st;
 
@@ -885,7 +969,7 @@ static int verify_uptodate_1(struct cache_entry *ce,
        if (errno == ENOENT)
                return 0;
        return o->gently ? -1 :
-               error(error_msg, ce->name);
+               add_rejected_path(o, error_type, ce->name);
 }
 
 static int verify_uptodate(struct cache_entry *ce,
@@ -893,13 +977,13 @@ static int verify_uptodate(struct cache_entry *ce,
 {
        if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
                return 0;
-       return verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));
+       return verify_uptodate_1(ce, o, ERROR_NOT_UPTODATE_FILE);
 }
 
 static int verify_uptodate_sparse(struct cache_entry *ce,
                                  struct unpack_trees_options *o)
 {
-       return verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));
+       return verify_uptodate_1(ce, o, ERROR_SPARSE_NOT_UPTODATE_FILE);
 }
 
 static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -915,13 +999,15 @@ static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_optio
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(struct cache_entry *ce, const char *action,
+static int verify_clean_submodule(struct cache_entry *ce,
+                                     enum unpack_trees_error_types error_type,
                                      struct unpack_trees_options *o)
 {
        return 0;
 }
 
-static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
+static int verify_clean_subdirectory(struct cache_entry *ce,
+                                     enum unpack_trees_error_types error_type,
                                      struct unpack_trees_options *o)
 {
        /*
@@ -942,7 +1028,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
                 */
                if (!hashcmp(sha1, ce->sha1))
                        return 0;
-               return verify_clean_submodule(ce, action, o);
+               return verify_clean_submodule(ce, error_type, o);
        }
 
        /*
@@ -986,7 +1072,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        i = read_directory(&d, pathbuf, namelen+1, NULL);
        if (i)
                return o->gently ? -1 :
-                       error(ERRORMSG(o, not_uptodate_dir), ce->name);
+                       add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
        free(pathbuf);
        return cnt;
 }
@@ -1011,9 +1097,9 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent_1(struct cache_entry *ce, const char *action,
-                                struct unpack_trees_options *o,
-                                const char *error_msg)
+static int verify_absent_1(struct cache_entry *ce,
+                                enum unpack_trees_error_types error_type,
+                                struct unpack_trees_options *o)
 {
        struct stat st;
 
@@ -1051,7 +1137,7 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,
                         * files that are in "foo/" we would lose
                         * them.
                         */
-                       if (verify_clean_subdirectory(ce, action, o) < 0)
+                       if (verify_clean_subdirectory(ce, error_type, o) < 0)
                                return -1;
                        return 0;
                }
@@ -1068,22 +1154,28 @@ static int verify_absent_1(struct cache_entry *ce, const char *action,
                }
 
                return o->gently ? -1 :
-                       error(ERRORMSG(o, would_lose_untracked), ce->name, action);
+                       add_rejected_path(o, error_type, ce->name);
        }
        return 0;
 }
-static int verify_absent(struct cache_entry *ce, const char *action,
+static int verify_absent(struct cache_entry *ce,
+                        enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
        if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
                return 0;
-       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));
+       return verify_absent_1(ce, error_type, o);
 }
 
-static int verify_absent_sparse(struct cache_entry *ce, const char *action,
+static int verify_absent_sparse(struct cache_entry *ce,
+                        enum unpack_trees_error_types error_type,
                         struct unpack_trees_options *o)
 {
-       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));
+       enum unpack_trees_error_types orphaned_error = error_type;
+       if (orphaned_error == ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN)
+               orphaned_error = ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN;
+
+       return verify_absent_1(ce, orphaned_error, o);
 }
 
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
@@ -1092,8 +1184,10 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
        int update = CE_UPDATE;
 
        if (!old) {
-               if (verify_absent(merge, "overwritten", o))
+               if (verify_absent(merge, ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN, o))
                        return -1;
+               if (!o->skip_sparse_checkout && will_have_skip_worktree(merge, o))
+                       update |= CE_SKIP_WORKTREE;
                invalidate_ce_path(merge, o);
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
@@ -1130,7 +1224,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
 {
        /* Did it exist in the index? */
        if (!old) {
-               if (verify_absent(ce, "removed", o))
+               if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
                        return -1;
                return 0;
        }
@@ -1279,7 +1373,7 @@ int threeway_merge(struct cache_entry **stages, struct unpack_trees_options *o)
                        if (index)
                                return deleted_entry(index, index, o);
                        if (ce && !head_deleted) {
-                               if (verify_absent(ce, "removed", o))
+                               if (verify_absent(ce, ERROR_WOULD_LOSE_UNTRACKED_REMOVED, o))
                                        return -1;
                        }
                        return 0;
@@ -1412,7 +1506,7 @@ int bind_merge(struct cache_entry **src,
                             o->merge_size);
        if (a && old)
                return o->gently ? -1 :
-                       error(ERRORMSG(o, bind_overlap), a->name, old->name);
+                       error(ERRORMSG(o, ERROR_BIND_OVERLAP), a->name, old->name);
        if (!a)
                return keep_entry(old, o);
        else
index ef70eab39025fcdaccda059692ae447a13fa0aeb..6e049b09977cf89eca87f5ef22e645dd2027e414 100644 (file)
@@ -9,14 +9,22 @@ struct exclude_list;
 typedef int (*merge_fn_t)(struct cache_entry **src,
                struct unpack_trees_options *options);
 
-struct unpack_trees_error_msgs {
-       const char *would_overwrite;
-       const char *not_uptodate_file;
-       const char *not_uptodate_dir;
-       const char *would_lose_untracked;
-       const char *bind_overlap;
-       const char *sparse_not_uptodate_file;
-       const char *would_lose_orphaned;
+enum unpack_trees_error_types {
+       ERROR_WOULD_OVERWRITE = 0,
+       ERROR_NOT_UPTODATE_FILE,
+       ERROR_NOT_UPTODATE_DIR,
+       ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN,
+       ERROR_WOULD_LOSE_UNTRACKED_REMOVED,
+       ERROR_BIND_OVERLAP,
+       ERROR_SPARSE_NOT_UPTODATE_FILE,
+       ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN,
+       ERROR_WOULD_LOSE_ORPHANED_REMOVED,
+       NB_UNPACK_TREES_ERROR_TYPES
+};
+
+struct rejected_paths_list {
+       char *path;
+       struct rejected_paths_list *next;
 };
 
 struct unpack_trees_options {
@@ -33,12 +41,18 @@ struct unpack_trees_options {
                     diff_index_cached,
                     debug_unpack,
                     skip_sparse_checkout,
-                    gently;
+                    gently,
+                    show_all_errors;
        const char *prefix;
        int cache_bottom;
        struct dir_struct *dir;
        merge_fn_t fn;
-       struct unpack_trees_error_msgs msgs;
+       const char *msgs[NB_UNPACK_TREES_ERROR_TYPES];
+       /*
+        * Store error messages in an array, each case
+        * corresponding to a error message type
+        */
+       struct rejected_paths_list *unpack_rejects[NB_UNPACK_TREES_ERROR_TYPES];
 
        int head_idx;
        int merge_size;
index dc464d78b35659705ffb0cd233b80ab27e24e8bc..92f9530c6572a212c12ac917d774736286325e52 100644 (file)
@@ -105,7 +105,7 @@ static void show_edge(struct commit *commit)
        fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
 }
 
-static int do_rev_list(int in, int out, void *create_full_pack)
+static int do_rev_list(int in, int out, void *user_data)
 {
        int i;
        struct rev_info revs;
@@ -118,23 +118,18 @@ static int do_rev_list(int in, int out, void *create_full_pack)
        if (use_thin_pack)
                revs.edge_hint = 1;
 
-       if (create_full_pack) {
-               const char *args[] = {"rev-list", "--all", NULL};
-               setup_revisions(2, args, &revs, NULL);
-       } else {
-               for (i = 0; i < want_obj.nr; i++) {
-                       struct object *o = want_obj.objects[i].item;
-                       /* why??? */
-                       o->flags &= ~UNINTERESTING;
-                       add_pending_object(&revs, o, NULL);
-               }
-               for (i = 0; i < have_obj.nr; i++) {
-                       struct object *o = have_obj.objects[i].item;
-                       o->flags |= UNINTERESTING;
-                       add_pending_object(&revs, o, NULL);
-               }
-               setup_revisions(0, NULL, &revs, NULL);
+       for (i = 0; i < want_obj.nr; i++) {
+               struct object *o = want_obj.objects[i].item;
+               /* why??? */
+               o->flags &= ~UNINTERESTING;
+               add_pending_object(&revs, o, NULL);
+       }
+       for (i = 0; i < have_obj.nr; i++) {
+               struct object *o = have_obj.objects[i].item;
+               o->flags |= UNINTERESTING;
+               add_pending_object(&revs, o, NULL);
        }
+       setup_revisions(0, NULL, &revs, NULL);
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        mark_edges_uninteresting(revs.commits, &revs, show_edge);
@@ -487,7 +482,7 @@ static int get_common_commits(void)
 
 static void receive_needs(void)
 {
-       struct object_array shallows = {0, 0, NULL};
+       struct object_array shallows = OBJECT_ARRAY_INIT;
        static char line[1000];
        int len, depth = 0;
 
@@ -554,7 +549,8 @@ static void receive_needs(void)
                 */
                o = lookup_object(sha1_buf);
                if (!o || !(o->flags & OUR_REF))
-                       die("git upload-pack: not our ref %s", line+5);
+                       die("git upload-pack: not our ref %s",
+                           sha1_to_hex(sha1_buf));
                if (!(o->flags & WANTED)) {
                        o->flags |= WANTED;
                        add_object_array(o, NULL, &want_obj);
diff --git a/url.c b/url.c
index 230623657a0e06975c4721a9fddb117ba84883b1..cd8f74f00c345294f94cb25e4e00849a0618168b 100644 (file)
--- a/url.c
+++ b/url.c
@@ -67,7 +67,8 @@ static int url_decode_char(const char *q)
        return val;
 }
 
-static char *url_decode_internal(const char **query, const char *stop_at, struct strbuf *out)
+static char *url_decode_internal(const char **query, const char *stop_at,
+                                struct strbuf *out, int decode_plus)
 {
        const char *q = *query;
 
@@ -90,7 +91,7 @@ static char *url_decode_internal(const char **query, const char *stop_at, struct
                        }
                }
 
-               if (c == '+')
+               if (decode_plus && c == '+')
                        strbuf_addch(out, ' ');
                else
                        strbuf_addch(out, c);
@@ -110,17 +111,17 @@ char *url_decode(const char *url)
                strbuf_add(&out, url, colon - url);
                url = colon;
        }
-       return url_decode_internal(&url, NULL, &out);
+       return url_decode_internal(&url, NULL, &out, 0);
 }
 
 char *url_decode_parameter_name(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
-       return url_decode_internal(query, "&=", &out);
+       return url_decode_internal(query, "&=", &out, 1);
 }
 
 char *url_decode_parameter_value(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
-       return url_decode_internal(query, "&", &out);
+       return url_decode_internal(query, "&", &out, 1);
 }
index c49cc1b67e1164c561fb20839272bd8b1f7dde6d..e5522159b362811346b4306246391e960a4e7041 100644 (file)
@@ -82,6 +82,22 @@ PATTERNS("cpp",
         "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
         "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
         "|[^[:space:]]|[\x80-\xff]+"),
+PATTERNS("csharp",
+        /* Keywords */
+        "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+        /* Methods and constructors */
+        "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+        /* Properties */
+        "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+        /* Type definitions */
+        "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+        /* Namespace */
+        "^[ \t]*(namespace[ \t]+.*)$",
+        /* -- */
+        "[a-zA-Z_][a-zA-Z0-9_]*"
+        "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+        "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"
+        "|[^[:space:]]|[\x80-\xff]+"),
 { "default", NULL, -1, { NULL, 0 } },
 };
 #undef PATTERNS
diff --git a/vcs-svn/LICENSE b/vcs-svn/LICENSE
new file mode 100644 (file)
index 0000000..0a5e3c4
--- /dev/null
@@ -0,0 +1,33 @@
+Copyright (C) 2010 David Barr <david.barr@cordelta.com>.
+All rights reserved.
+
+Copyright (C) 2008 Jason Evans <jasone@canonware.com>.
+All rights reserved.
+
+Copyright (C) 2005 Stefan Hegny, hydrografix Consulting GmbH,
+Frankfurt/Main, Germany
+and others, see http://svn2cc.sarovar.org
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice(s), this list of conditions and the following disclaimer
+   unmodified other than the allowable addition of one or more
+   copyright notices.
+2. Redistributions in binary form must reproduce the above copyright
+   notice(s), this list of conditions and the following disclaimer in
+   the documentation and/or other materials provided with the
+   distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
new file mode 100644 (file)
index 0000000..256a052
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "fast_export.h"
+#include "line_buffer.h"
+#include "repo_tree.h"
+#include "string_pool.h"
+
+#define MAX_GITSVN_LINE_LEN 4096
+
+static uint32_t first_commit_done;
+
+void fast_export_delete(uint32_t depth, uint32_t *path)
+{
+       putchar('D');
+       putchar(' ');
+       pool_print_seq(depth, path, '/', stdout);
+       putchar('\n');
+}
+
+void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
+                       uint32_t mark)
+{
+       /* Mode must be 100644, 100755, 120000, or 160000. */
+       printf("M %06o :%d ", mode, mark);
+       pool_print_seq(depth, path, '/', stdout);
+       putchar('\n');
+}
+
+static char gitsvnline[MAX_GITSVN_LINE_LEN];
+void fast_export_commit(uint32_t revision, uint32_t author, char *log,
+                       uint32_t uuid, uint32_t url,
+                       unsigned long timestamp)
+{
+       if (!log)
+               log = "";
+       if (~uuid && ~url) {
+               snprintf(gitsvnline, MAX_GITSVN_LINE_LEN, "\n\ngit-svn-id: %s@%d %s\n",
+                                pool_fetch(url), revision, pool_fetch(uuid));
+       } else {
+               *gitsvnline = '\0';
+       }
+       printf("commit refs/heads/master\n");
+       printf("committer %s <%s@%s> %ld +0000\n",
+                  ~author ? pool_fetch(author) : "nobody",
+                  ~author ? pool_fetch(author) : "nobody",
+                  ~uuid ? pool_fetch(uuid) : "local", timestamp);
+       printf("data %"PRIu32"\n%s%s\n",
+                  (uint32_t) (strlen(log) + strlen(gitsvnline)),
+                  log, gitsvnline);
+       if (!first_commit_done) {
+               if (revision > 1)
+                       printf("from refs/heads/master^0\n");
+               first_commit_done = 1;
+       }
+       repo_diff(revision - 1, revision);
+       fputc('\n', stdout);
+
+       printf("progress Imported commit %d.\n\n", revision);
+}
+
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len)
+{
+       if (mode == REPO_MODE_LNK) {
+               /* svn symlink blobs start with "link " */
+               buffer_skip_bytes(5);
+               len -= 5;
+       }
+       printf("blob\nmark :%d\ndata %d\n", mark, len);
+       buffer_copy_bytes(len);
+       fputc('\n', stdout);
+}
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
new file mode 100644 (file)
index 0000000..2aaaea5
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef FAST_EXPORT_H_
+#define FAST_EXPORT_H_
+
+void fast_export_delete(uint32_t depth, uint32_t *path);
+void fast_export_modify(uint32_t depth, uint32_t *path, uint32_t mode,
+                       uint32_t mark);
+void fast_export_commit(uint32_t revision, uint32_t author, char *log,
+                       uint32_t uuid, uint32_t url, unsigned long timestamp);
+void fast_export_blob(uint32_t mode, uint32_t mark, uint32_t len);
+
+#endif
diff --git a/vcs-svn/line_buffer.c b/vcs-svn/line_buffer.c
new file mode 100644 (file)
index 0000000..1543567
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "line_buffer.h"
+#include "obj_pool.h"
+
+#define LINE_BUFFER_LEN 10000
+#define COPY_BUFFER_LEN 4096
+
+/* Create memory pool for char sequence of known length */
+obj_pool_gen(blob, char, 4096)
+
+static char line_buffer[LINE_BUFFER_LEN];
+static char byte_buffer[COPY_BUFFER_LEN];
+static FILE *infile;
+
+int buffer_init(const char *filename)
+{
+       infile = filename ? fopen(filename, "r") : stdin;
+       if (!infile)
+               return -1;
+       return 0;
+}
+
+int buffer_deinit(void)
+{
+       int err;
+       if (infile == stdin)
+               return ferror(infile);
+       err = ferror(infile);
+       err |= fclose(infile);
+       return err;
+}
+
+/* Read a line without trailing newline. */
+char *buffer_read_line(void)
+{
+       char *end;
+       if (!fgets(line_buffer, sizeof(line_buffer), infile))
+               /* Error or data exhausted. */
+               return NULL;
+       end = line_buffer + strlen(line_buffer);
+       if (end[-1] == '\n')
+               end[-1] = '\0';
+       else if (feof(infile))
+               ; /* No newline at end of file.  That's fine. */
+       else
+               /*
+                * Line was too long.
+                * There is probably a saner way to deal with this,
+                * but for now let's return an error.
+                */
+               return NULL;
+       return line_buffer;
+}
+
+char *buffer_read_string(uint32_t len)
+{
+       char *s;
+       blob_free(blob_pool.size);
+       s = blob_pointer(blob_alloc(len + 1));
+       s[fread(s, 1, len, infile)] = '\0';
+       return ferror(infile) ? NULL : s;
+}
+
+void buffer_copy_bytes(uint32_t len)
+{
+       uint32_t in;
+       while (len > 0 && !feof(infile) && !ferror(infile)) {
+               in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
+               in = fread(byte_buffer, 1, in, infile);
+               len -= in;
+               fwrite(byte_buffer, 1, in, stdout);
+               if (ferror(stdout)) {
+                       buffer_skip_bytes(len);
+                       return;
+               }
+       }
+}
+
+void buffer_skip_bytes(uint32_t len)
+{
+       uint32_t in;
+       while (len > 0 && !feof(infile) && !ferror(infile)) {
+               in = len < COPY_BUFFER_LEN ? len : COPY_BUFFER_LEN;
+               in = fread(byte_buffer, 1, in, infile);
+               len -= in;
+       }
+}
+
+void buffer_reset(void)
+{
+       blob_reset();
+}
diff --git a/vcs-svn/line_buffer.h b/vcs-svn/line_buffer.h
new file mode 100644 (file)
index 0000000..9c78ae1
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef LINE_BUFFER_H_
+#define LINE_BUFFER_H_
+
+int buffer_init(const char *filename);
+int buffer_deinit(void);
+char *buffer_read_line(void);
+char *buffer_read_string(uint32_t len);
+void buffer_copy_bytes(uint32_t len);
+void buffer_skip_bytes(uint32_t len);
+void buffer_reset(void);
+
+#endif
diff --git a/vcs-svn/line_buffer.txt b/vcs-svn/line_buffer.txt
new file mode 100644 (file)
index 0000000..8906fb1
--- /dev/null
@@ -0,0 +1,58 @@
+line_buffer API
+===============
+
+The line_buffer library provides a convenient interface for
+mostly-line-oriented input.
+
+Each line is not permitted to exceed 10000 bytes.  The provided
+functions are not thread-safe or async-signal-safe, and like
+`fgets()`, they generally do not function correctly if interrupted
+by a signal without SA_RESTART set.
+
+Calling sequence
+----------------
+
+The calling program:
+
+ - specifies a file to read with `buffer_init`
+ - processes input with `buffer_read_line`, `buffer_read_string`,
+   `buffer_skip_bytes`, and `buffer_copy_bytes`
+ - closes the file with `buffer_deinit`, perhaps to start over and
+   read another file.
+
+Before exiting, the caller can use `buffer_reset` to deallocate
+resources for the benefit of profiling tools.
+
+Functions
+---------
+
+`buffer_init`::
+       Open the named file for input.  If filename is NULL,
+       start reading from stdin.  On failure, returns -1 (with
+       errno indicating the nature of the failure).
+
+`buffer_deinit`::
+       Stop reading from the current file (closing it unless
+       it was stdin).  Returns nonzero if `fclose` fails or
+       the error indicator was set.
+
+`buffer_read_line`::
+       Read a line and strip off the trailing newline.
+       On failure or end of file, returns NULL.
+
+`buffer_read_string`::
+       Read `len` characters of input or up to the end of the
+       file, whichever comes first.  Returns NULL on error.
+       Returns whatever characters were read (possibly "")
+       for end of file.
+
+`buffer_copy_bytes`::
+       Read `len` bytes of input and dump them to the standard output
+       stream.  Returns early for error or end of file.
+
+`buffer_skip_bytes`::
+       Discards `len` bytes from the input stream (stopping early
+       if necessary because of an error or eof).
+
+`buffer_reset`::
+       Deallocates non-static buffers.
diff --git a/vcs-svn/obj_pool.h b/vcs-svn/obj_pool.h
new file mode 100644 (file)
index 0000000..deb6eb8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#ifndef OBJ_POOL_H_
+#define OBJ_POOL_H_
+
+#include "git-compat-util.h"
+
+#define MAYBE_UNUSED __attribute__((__unused__))
+
+#define obj_pool_gen(pre, obj_t, initial_capacity) \
+static struct { \
+       uint32_t committed; \
+       uint32_t size; \
+       uint32_t capacity; \
+       obj_t *base; \
+} pre##_pool = {0, 0, 0, NULL}; \
+static MAYBE_UNUSED uint32_t pre##_alloc(uint32_t count) \
+{ \
+       uint32_t offset; \
+       if (pre##_pool.size + count > pre##_pool.capacity) { \
+               while (pre##_pool.size + count > pre##_pool.capacity) \
+                       if (pre##_pool.capacity) \
+                               pre##_pool.capacity *= 2; \
+                       else \
+                               pre##_pool.capacity = initial_capacity; \
+               pre##_pool.base = realloc(pre##_pool.base, \
+                                       pre##_pool.capacity * sizeof(obj_t)); \
+       } \
+       offset = pre##_pool.size; \
+       pre##_pool.size += count; \
+       return offset; \
+} \
+static MAYBE_UNUSED void pre##_free(uint32_t count) \
+{ \
+       pre##_pool.size -= count; \
+} \
+static MAYBE_UNUSED uint32_t pre##_offset(obj_t *obj) \
+{ \
+       return obj == NULL ? ~0 : obj - pre##_pool.base; \
+} \
+static MAYBE_UNUSED obj_t *pre##_pointer(uint32_t offset) \
+{ \
+       return offset >= pre##_pool.size ? NULL : &pre##_pool.base[offset]; \
+} \
+static MAYBE_UNUSED void pre##_commit(void) \
+{ \
+       pre##_pool.committed = pre##_pool.size; \
+} \
+static MAYBE_UNUSED void pre##_reset(void) \
+{ \
+       free(pre##_pool.base); \
+       pre##_pool.base = NULL; \
+       pre##_pool.size = 0; \
+       pre##_pool.capacity = 0; \
+       pre##_pool.committed = 0; \
+}
+
+#endif
diff --git a/vcs-svn/repo_tree.c b/vcs-svn/repo_tree.c
new file mode 100644 (file)
index 0000000..e94d91d
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+
+#include "string_pool.h"
+#include "repo_tree.h"
+#include "obj_pool.h"
+#include "fast_export.h"
+
+#include "trp.h"
+
+struct repo_dirent {
+       uint32_t name_offset;
+       struct trp_node children;
+       uint32_t mode;
+       uint32_t content_offset;
+};
+
+struct repo_dir {
+       struct trp_root entries;
+};
+
+struct repo_commit {
+       uint32_t root_dir_offset;
+};
+
+/* Memory pools for commit, dir and dirent */
+obj_pool_gen(commit, struct repo_commit, 4096)
+obj_pool_gen(dir, struct repo_dir, 4096)
+obj_pool_gen(dent, struct repo_dirent, 4096)
+
+static uint32_t active_commit;
+static uint32_t mark;
+
+static int repo_dirent_name_cmp(const void *a, const void *b);
+
+/* Treap for directory entries */
+trp_gen(static, dent_, struct repo_dirent, children, dent, repo_dirent_name_cmp);
+
+uint32_t next_blob_mark(void)
+{
+       return mark++;
+}
+
+static struct repo_dir *repo_commit_root_dir(struct repo_commit *commit)
+{
+       return dir_pointer(commit->root_dir_offset);
+}
+
+static struct repo_dirent *repo_first_dirent(struct repo_dir *dir)
+{
+       return dent_first(&dir->entries);
+}
+
+static int repo_dirent_name_cmp(const void *a, const void *b)
+{
+       const struct repo_dirent *dent1 = a, *dent2 = b;
+       uint32_t a_offset = dent1->name_offset;
+       uint32_t b_offset = dent2->name_offset;
+       return (a_offset > b_offset) - (a_offset < b_offset);
+}
+
+static int repo_dirent_is_dir(struct repo_dirent *dent)
+{
+       return dent != NULL && dent->mode == REPO_MODE_DIR;
+}
+
+static struct repo_dir *repo_dir_from_dirent(struct repo_dirent *dent)
+{
+       if (!repo_dirent_is_dir(dent))
+               return NULL;
+       return dir_pointer(dent->content_offset);
+}
+
+static struct repo_dir *repo_clone_dir(struct repo_dir *orig_dir)
+{
+       uint32_t orig_o, new_o;
+       orig_o = dir_offset(orig_dir);
+       if (orig_o >= dir_pool.committed)
+               return orig_dir;
+       new_o = dir_alloc(1);
+       orig_dir = dir_pointer(orig_o);
+       *dir_pointer(new_o) = *orig_dir;
+       return dir_pointer(new_o);
+}
+
+static struct repo_dirent *repo_read_dirent(uint32_t revision, uint32_t *path)
+{
+       uint32_t name = 0;
+       struct repo_dirent *key = dent_pointer(dent_alloc(1));
+       struct repo_dir *dir = NULL;
+       struct repo_dirent *dent = NULL;
+       dir = repo_commit_root_dir(commit_pointer(revision));
+       while (~(name = *path++)) {
+               key->name_offset = name;
+               dent = dent_search(&dir->entries, key);
+               if (dent == NULL || !repo_dirent_is_dir(dent))
+                       break;
+               dir = repo_dir_from_dirent(dent);
+       }
+       dent_free(1);
+       return dent;
+}
+
+static void repo_write_dirent(uint32_t *path, uint32_t mode,
+                             uint32_t content_offset, uint32_t del)
+{
+       uint32_t name, revision, dir_o = ~0, parent_dir_o = ~0;
+       struct repo_dir *dir;
+       struct repo_dirent *key;
+       struct repo_dirent *dent = NULL;
+       revision = active_commit;
+       dir = repo_commit_root_dir(commit_pointer(revision));
+       dir = repo_clone_dir(dir);
+       commit_pointer(revision)->root_dir_offset = dir_offset(dir);
+       while (~(name = *path++)) {
+               parent_dir_o = dir_offset(dir);
+
+               key = dent_pointer(dent_alloc(1));
+               key->name_offset = name;
+
+               dent = dent_search(&dir->entries, key);
+               if (dent == NULL)
+                       dent = key;
+               else
+                       dent_free(1);
+
+               if (dent == key) {
+                       dent->mode = REPO_MODE_DIR;
+                       dent->content_offset = 0;
+                       dent_insert(&dir->entries, dent);
+               }
+
+               if (dent_offset(dent) < dent_pool.committed) {
+                       dir_o = repo_dirent_is_dir(dent) ?
+                                       dent->content_offset : ~0;
+                       dent_remove(&dir->entries, dent);
+                       dent = dent_pointer(dent_alloc(1));
+                       dent->name_offset = name;
+                       dent->mode = REPO_MODE_DIR;
+                       dent->content_offset = dir_o;
+                       dent_insert(&dir->entries, dent);
+               }
+
+               dir = repo_dir_from_dirent(dent);
+               dir = repo_clone_dir(dir);
+               dent->content_offset = dir_offset(dir);
+       }
+       if (dent == NULL)
+               return;
+       dent->mode = mode;
+       dent->content_offset = content_offset;
+       if (del && ~parent_dir_o)
+               dent_remove(&dir_pointer(parent_dir_o)->entries, dent);
+}
+
+uint32_t repo_copy(uint32_t revision, uint32_t *src, uint32_t *dst)
+{
+       uint32_t mode = 0, content_offset = 0;
+       struct repo_dirent *src_dent;
+       src_dent = repo_read_dirent(revision, src);
+       if (src_dent != NULL) {
+               mode = src_dent->mode;
+               content_offset = src_dent->content_offset;
+               repo_write_dirent(dst, mode, content_offset, 0);
+       }
+       return mode;
+}
+
+void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark)
+{
+       repo_write_dirent(path, mode, blob_mark, 0);
+}
+
+uint32_t repo_replace(uint32_t *path, uint32_t blob_mark)
+{
+       uint32_t mode = 0;
+       struct repo_dirent *src_dent;
+       src_dent = repo_read_dirent(active_commit, path);
+       if (src_dent != NULL) {
+               mode = src_dent->mode;
+               repo_write_dirent(path, mode, blob_mark, 0);
+       }
+       return mode;
+}
+
+void repo_modify(uint32_t *path, uint32_t mode, uint32_t blob_mark)
+{
+       struct repo_dirent *src_dent;
+       src_dent = repo_read_dirent(active_commit, path);
+       if (src_dent != NULL && blob_mark == 0)
+               blob_mark = src_dent->content_offset;
+       repo_write_dirent(path, mode, blob_mark, 0);
+}
+
+void repo_delete(uint32_t *path)
+{
+       repo_write_dirent(path, 0, 0, 1);
+}
+
+static void repo_git_add_r(uint32_t depth, uint32_t *path, struct repo_dir *dir);
+
+static void repo_git_add(uint32_t depth, uint32_t *path, struct repo_dirent *dent)
+{
+       if (repo_dirent_is_dir(dent))
+               repo_git_add_r(depth, path, repo_dir_from_dirent(dent));
+       else
+               fast_export_modify(depth, path,
+                                  dent->mode, dent->content_offset);
+}
+
+static void repo_git_add_r(uint32_t depth, uint32_t *path, struct repo_dir *dir)
+{
+       struct repo_dirent *de = repo_first_dirent(dir);
+       while (de) {
+               path[depth] = de->name_offset;
+               repo_git_add(depth + 1, path, de);
+               de = dent_next(&dir->entries, de);
+       }
+}
+
+static void repo_diff_r(uint32_t depth, uint32_t *path, struct repo_dir *dir1,
+                       struct repo_dir *dir2)
+{
+       struct repo_dirent *de1, *de2;
+       de1 = repo_first_dirent(dir1);
+       de2 = repo_first_dirent(dir2);
+
+       while (de1 && de2) {
+               if (de1->name_offset < de2->name_offset) {
+                       path[depth] = de1->name_offset;
+                       fast_export_delete(depth + 1, path);
+                       de1 = dent_next(&dir1->entries, de1);
+                       continue;
+               }
+               if (de1->name_offset > de2->name_offset) {
+                       path[depth] = de2->name_offset;
+                       repo_git_add(depth + 1, path, de2);
+                       de2 = dent_next(&dir2->entries, de2);
+                       continue;
+               }
+               path[depth] = de1->name_offset;
+
+               if (de1->mode == de2->mode &&
+                   de1->content_offset == de2->content_offset) {
+                       ; /* No change. */
+               } else if (repo_dirent_is_dir(de1) && repo_dirent_is_dir(de2)) {
+                       repo_diff_r(depth + 1, path,
+                                   repo_dir_from_dirent(de1),
+                                   repo_dir_from_dirent(de2));
+               } else if (!repo_dirent_is_dir(de1) && !repo_dirent_is_dir(de2)) {
+                       repo_git_add(depth + 1, path, de2);
+               } else {
+                       fast_export_delete(depth + 1, path);
+                       repo_git_add(depth + 1, path, de2);
+               }
+               de1 = dent_next(&dir1->entries, de1);
+               de2 = dent_next(&dir2->entries, de2);
+       }
+       while (de1) {
+               path[depth] = de1->name_offset;
+               fast_export_delete(depth + 1, path);
+               de1 = dent_next(&dir1->entries, de1);
+       }
+       while (de2) {
+               path[depth] = de2->name_offset;
+               repo_git_add(depth + 1, path, de2);
+               de2 = dent_next(&dir2->entries, de2);
+       }
+}
+
+static uint32_t path_stack[REPO_MAX_PATH_DEPTH];
+
+void repo_diff(uint32_t r1, uint32_t r2)
+{
+       repo_diff_r(0,
+                   path_stack,
+                   repo_commit_root_dir(commit_pointer(r1)),
+                   repo_commit_root_dir(commit_pointer(r2)));
+}
+
+void repo_commit(uint32_t revision, uint32_t author, char *log, uint32_t uuid,
+                uint32_t url, unsigned long timestamp)
+{
+       fast_export_commit(revision, author, log, uuid, url, timestamp);
+       dent_commit();
+       dir_commit();
+       active_commit = commit_alloc(1);
+       commit_pointer(active_commit)->root_dir_offset =
+               commit_pointer(active_commit - 1)->root_dir_offset;
+}
+
+static void mark_init(void)
+{
+       uint32_t i;
+       mark = 0;
+       for (i = 0; i < dent_pool.size; i++)
+               if (!repo_dirent_is_dir(dent_pointer(i)) &&
+                   dent_pointer(i)->content_offset > mark)
+                       mark = dent_pointer(i)->content_offset;
+       mark++;
+}
+
+void repo_init(void)
+{
+       mark_init();
+       if (commit_pool.size == 0) {
+               /* Create empty tree for commit 0. */
+               commit_alloc(1);
+               commit_pointer(0)->root_dir_offset = dir_alloc(1);
+               dir_pointer(0)->entries.trp_root = ~0;
+               dir_commit();
+       }
+       /* Preallocate next commit, ready for changes. */
+       active_commit = commit_alloc(1);
+       commit_pointer(active_commit)->root_dir_offset =
+               commit_pointer(active_commit - 1)->root_dir_offset;
+}
+
+void repo_reset(void)
+{
+       pool_reset();
+       commit_reset();
+       dir_reset();
+       dent_reset();
+}
diff --git a/vcs-svn/repo_tree.h b/vcs-svn/repo_tree.h
new file mode 100644 (file)
index 0000000..5476175
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef REPO_TREE_H_
+#define REPO_TREE_H_
+
+#include "git-compat-util.h"
+
+#define REPO_MODE_DIR 0040000
+#define REPO_MODE_BLB 0100644
+#define REPO_MODE_EXE 0100755
+#define REPO_MODE_LNK 0120000
+
+#define REPO_MAX_PATH_LEN 4096
+#define REPO_MAX_PATH_DEPTH 1000
+
+uint32_t next_blob_mark(void);
+uint32_t repo_copy(uint32_t revision, uint32_t *src, uint32_t *dst);
+void repo_add(uint32_t *path, uint32_t mode, uint32_t blob_mark);
+uint32_t repo_replace(uint32_t *path, uint32_t blob_mark);
+void repo_modify(uint32_t *path, uint32_t mode, uint32_t blob_mark);
+void repo_delete(uint32_t *path);
+void repo_commit(uint32_t revision, uint32_t author, char *log, uint32_t uuid,
+                uint32_t url, long unsigned timestamp);
+void repo_diff(uint32_t r1, uint32_t r2);
+void repo_init(void);
+void repo_reset(void);
+
+#endif
diff --git a/vcs-svn/string_pool.c b/vcs-svn/string_pool.c
new file mode 100644 (file)
index 0000000..f5b1da8
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "git-compat-util.h"
+#include "trp.h"
+#include "obj_pool.h"
+#include "string_pool.h"
+
+static struct trp_root tree = { ~0 };
+
+struct node {
+       uint32_t offset;
+       struct trp_node children;
+};
+
+/* Two memory pools: one for struct node, and another for strings */
+obj_pool_gen(node, struct node, 4096)
+obj_pool_gen(string, char, 4096)
+
+static char *node_value(struct node *node)
+{
+       return node ? string_pointer(node->offset) : NULL;
+}
+
+static int node_cmp(struct node *a, struct node *b)
+{
+       return strcmp(node_value(a), node_value(b));
+}
+
+/* Build a Treap from the node structure (a trp_node w/ offset) */
+trp_gen(static, tree_, struct node, children, node, node_cmp);
+
+const char *pool_fetch(uint32_t entry)
+{
+       return node_value(node_pointer(entry));
+}
+
+uint32_t pool_intern(const char *key)
+{
+       /* Canonicalize key */
+       struct node *match = NULL, *node;
+       uint32_t key_len;
+       if (key == NULL)
+               return ~0;
+       key_len = strlen(key) + 1;
+       node = node_pointer(node_alloc(1));
+       node->offset = string_alloc(key_len);
+       strcpy(node_value(node), key);
+       match = tree_search(&tree, node);
+       if (!match) {
+               tree_insert(&tree, node);
+       } else {
+               node_free(1);
+               string_free(key_len);
+               node = match;
+       }
+       return node_offset(node);
+}
+
+uint32_t pool_tok_r(char *str, const char *delim, char **saveptr)
+{
+       char *token = strtok_r(str, delim, saveptr);
+       return token ? pool_intern(token) : ~0;
+}
+
+void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream)
+{
+       uint32_t i;
+       for (i = 0; i < len && ~seq[i]; i++) {
+               fputs(pool_fetch(seq[i]), stream);
+               if (i < len - 1 && ~seq[i + 1])
+                       fputc(delim, stream);
+       }
+}
+
+uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str)
+{
+       char *context = NULL;
+       uint32_t token = ~0;
+       uint32_t length;
+
+       if (sz == 0)
+               return ~0;
+       if (str)
+               token = pool_tok_r(str, delim, &context);
+       for (length = 0; length < sz; length++) {
+               seq[length] = token;
+               if (token == ~0)
+                       return length;
+               token = pool_tok_r(NULL, delim, &context);
+       }
+       seq[sz - 1] = ~0;
+       return sz;
+}
+
+void pool_reset(void)
+{
+       node_reset();
+       string_reset();
+}
diff --git a/vcs-svn/string_pool.h b/vcs-svn/string_pool.h
new file mode 100644 (file)
index 0000000..222fb66
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef STRING_POOL_H_
+#define STRING_POOL_H_
+
+uint32_t pool_intern(const char *key);
+const char *pool_fetch(uint32_t entry);
+uint32_t pool_tok_r(char *str, const char *delim, char **saveptr);
+void pool_print_seq(uint32_t len, uint32_t *seq, char delim, FILE *stream);
+uint32_t pool_tok_seq(uint32_t sz, uint32_t *seq, const char *delim, char *str);
+void pool_reset(void);
+
+#endif
diff --git a/vcs-svn/string_pool.txt b/vcs-svn/string_pool.txt
new file mode 100644 (file)
index 0000000..1b41f15
--- /dev/null
@@ -0,0 +1,43 @@
+string_pool API
+===============
+
+The string_pool API provides facilities for replacing strings
+with integer keys that can be more easily compared and stored.
+The facilities are designed so that one could teach Git without
+too much trouble to store the information needed for these keys to
+remain valid over multiple executions.
+
+Functions
+---------
+
+pool_intern::
+       Include a string in the string pool and get its key.
+       If that string is already in the pool, retrieves its
+       existing key.
+
+pool_fetch::
+       Retrieve the string associated to a given key.
+
+pool_tok_r::
+       Extract the key of the next token from a string.
+       Interface mimics strtok_r.
+
+pool_print_seq::
+       Print a sequence of strings named by key to a file, using the
+       specified delimiter to separate them.
+
+       If NULL (key ~0) appears in the sequence, the sequence ends
+       early.
+
+pool_tok_seq::
+       Split a string into tokens, storing the keys of segments
+       into a caller-provided array.
+
+       Unless sz is 0, the array will always be ~0-terminated.
+       If there is not enough room for all the tokens, the
+       array holds as many tokens as fit in the entries before
+       the terminating ~0.  Return value is the index after the
+       last token, or sz if the tokens did not fit.
+
+pool_reset::
+       Deallocate storage for the string pool.
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
new file mode 100644 (file)
index 0000000..630eeb5
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Parse and rearrange a svnadmin dump.
+ * Create the dump with:
+ * svnadmin dump --incremental -r<startrev>:<endrev> <repository> >outfile
+ *
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#include "cache.h"
+#include "repo_tree.h"
+#include "fast_export.h"
+#include "line_buffer.h"
+#include "obj_pool.h"
+#include "string_pool.h"
+
+#define NODEACT_REPLACE 4
+#define NODEACT_DELETE 3
+#define NODEACT_ADD 2
+#define NODEACT_CHANGE 1
+#define NODEACT_UNKNOWN 0
+
+#define DUMP_CTX 0
+#define REV_CTX  1
+#define NODE_CTX 2
+
+#define LENGTH_UNKNOWN (~0)
+#define DATE_RFC2822_LEN 31
+
+/* Create memory pool for log messages */
+obj_pool_gen(log, char, 4096)
+
+static char* log_copy(uint32_t length, char *log)
+{
+       char *buffer;
+       log_free(log_pool.size);
+       buffer = log_pointer(log_alloc(length));
+       strncpy(buffer, log, length);
+       return buffer;
+}
+
+static struct {
+       uint32_t action, propLength, textLength, srcRev, srcMode, mark, type;
+       uint32_t src[REPO_MAX_PATH_DEPTH], dst[REPO_MAX_PATH_DEPTH];
+} node_ctx;
+
+static struct {
+       uint32_t revision, author;
+       unsigned long timestamp;
+       char *log;
+} rev_ctx;
+
+static struct {
+       uint32_t uuid, url;
+} dump_ctx;
+
+static struct {
+       uint32_t svn_log, svn_author, svn_date, svn_executable, svn_special, uuid,
+               revision_number, node_path, node_kind, node_action,
+               node_copyfrom_path, node_copyfrom_rev, text_content_length,
+               prop_content_length, content_length;
+} keys;
+
+static void reset_node_ctx(char *fname)
+{
+       node_ctx.type = 0;
+       node_ctx.action = NODEACT_UNKNOWN;
+       node_ctx.propLength = LENGTH_UNKNOWN;
+       node_ctx.textLength = LENGTH_UNKNOWN;
+       node_ctx.src[0] = ~0;
+       node_ctx.srcRev = 0;
+       node_ctx.srcMode = 0;
+       pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.dst, "/", fname);
+       node_ctx.mark = 0;
+}
+
+static void reset_rev_ctx(uint32_t revision)
+{
+       rev_ctx.revision = revision;
+       rev_ctx.timestamp = 0;
+       rev_ctx.log = NULL;
+       rev_ctx.author = ~0;
+}
+
+static void reset_dump_ctx(uint32_t url)
+{
+       dump_ctx.url = url;
+       dump_ctx.uuid = ~0;
+}
+
+static void init_keys(void)
+{
+       keys.svn_log = pool_intern("svn:log");
+       keys.svn_author = pool_intern("svn:author");
+       keys.svn_date = pool_intern("svn:date");
+       keys.svn_executable = pool_intern("svn:executable");
+       keys.svn_special = pool_intern("svn:special");
+       keys.uuid = pool_intern("UUID");
+       keys.revision_number = pool_intern("Revision-number");
+       keys.node_path = pool_intern("Node-path");
+       keys.node_kind = pool_intern("Node-kind");
+       keys.node_action = pool_intern("Node-action");
+       keys.node_copyfrom_path = pool_intern("Node-copyfrom-path");
+       keys.node_copyfrom_rev = pool_intern("Node-copyfrom-rev");
+       keys.text_content_length = pool_intern("Text-content-length");
+       keys.prop_content_length = pool_intern("Prop-content-length");
+       keys.content_length = pool_intern("Content-length");
+}
+
+static void read_props(void)
+{
+       uint32_t len;
+       uint32_t key = ~0;
+       char *val = NULL;
+       char *t;
+       while ((t = buffer_read_line()) && strcmp(t, "PROPS-END")) {
+               if (!strncmp(t, "K ", 2)) {
+                       len = atoi(&t[2]);
+                       key = pool_intern(buffer_read_string(len));
+                       buffer_read_line();
+               } else if (!strncmp(t, "V ", 2)) {
+                       len = atoi(&t[2]);
+                       val = buffer_read_string(len);
+                       if (key == keys.svn_log) {
+                               /* Value length excludes terminating nul. */
+                               rev_ctx.log = log_copy(len + 1, val);
+                       } else if (key == keys.svn_author) {
+                               rev_ctx.author = pool_intern(val);
+                       } else if (key == keys.svn_date) {
+                               if (parse_date_basic(val, &rev_ctx.timestamp, NULL))
+                                       fprintf(stderr, "Invalid timestamp: %s\n", val);
+                       } else if (key == keys.svn_executable) {
+                               node_ctx.type = REPO_MODE_EXE;
+                       } else if (key == keys.svn_special) {
+                               node_ctx.type = REPO_MODE_LNK;
+                       }
+                       key = ~0;
+                       buffer_read_line();
+               }
+       }
+}
+
+static void handle_node(void)
+{
+       if (node_ctx.propLength != LENGTH_UNKNOWN && node_ctx.propLength)
+               read_props();
+
+       if (node_ctx.srcRev)
+               node_ctx.srcMode = repo_copy(node_ctx.srcRev, node_ctx.src, node_ctx.dst);
+
+       if (node_ctx.textLength != LENGTH_UNKNOWN &&
+           node_ctx.type != REPO_MODE_DIR)
+               node_ctx.mark = next_blob_mark();
+
+       if (node_ctx.action == NODEACT_DELETE) {
+               repo_delete(node_ctx.dst);
+       } else if (node_ctx.action == NODEACT_CHANGE ||
+                          node_ctx.action == NODEACT_REPLACE) {
+               if (node_ctx.action == NODEACT_REPLACE &&
+                   node_ctx.type == REPO_MODE_DIR)
+                       repo_replace(node_ctx.dst, node_ctx.mark);
+               else if (node_ctx.propLength != LENGTH_UNKNOWN)
+                       repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
+               else if (node_ctx.textLength != LENGTH_UNKNOWN)
+                       node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
+       } else if (node_ctx.action == NODEACT_ADD) {
+               if (node_ctx.srcRev && node_ctx.propLength != LENGTH_UNKNOWN)
+                       repo_modify(node_ctx.dst, node_ctx.type, node_ctx.mark);
+               else if (node_ctx.srcRev && node_ctx.textLength != LENGTH_UNKNOWN)
+                       node_ctx.srcMode = repo_replace(node_ctx.dst, node_ctx.mark);
+               else if ((node_ctx.type == REPO_MODE_DIR && !node_ctx.srcRev) ||
+                        node_ctx.textLength != LENGTH_UNKNOWN)
+                       repo_add(node_ctx.dst, node_ctx.type, node_ctx.mark);
+       }
+
+       if (node_ctx.propLength == LENGTH_UNKNOWN && node_ctx.srcMode)
+               node_ctx.type = node_ctx.srcMode;
+
+       if (node_ctx.mark)
+               fast_export_blob(node_ctx.type, node_ctx.mark, node_ctx.textLength);
+       else if (node_ctx.textLength != LENGTH_UNKNOWN)
+               buffer_skip_bytes(node_ctx.textLength);
+}
+
+static void handle_revision(void)
+{
+       if (rev_ctx.revision)
+               repo_commit(rev_ctx.revision, rev_ctx.author, rev_ctx.log,
+                       dump_ctx.uuid, dump_ctx.url, rev_ctx.timestamp);
+}
+
+void svndump_read(const char *url)
+{
+       char *val;
+       char *t;
+       uint32_t active_ctx = DUMP_CTX;
+       uint32_t len;
+       uint32_t key;
+
+       reset_dump_ctx(pool_intern(url));
+       while ((t = buffer_read_line())) {
+               val = strstr(t, ": ");
+               if (!val)
+                       continue;
+               *val++ = '\0';
+               *val++ = '\0';
+               key = pool_intern(t);
+
+               if (key == keys.uuid) {
+                       dump_ctx.uuid = pool_intern(val);
+               } else if (key == keys.revision_number) {
+                       if (active_ctx == NODE_CTX)
+                               handle_node();
+                       if (active_ctx != DUMP_CTX)
+                               handle_revision();
+                       active_ctx = REV_CTX;
+                       reset_rev_ctx(atoi(val));
+               } else if (key == keys.node_path) {
+                       if (active_ctx == NODE_CTX)
+                               handle_node();
+                       active_ctx = NODE_CTX;
+                       reset_node_ctx(val);
+               } else if (key == keys.node_kind) {
+                       if (!strcmp(val, "dir"))
+                               node_ctx.type = REPO_MODE_DIR;
+                       else if (!strcmp(val, "file"))
+                               node_ctx.type = REPO_MODE_BLB;
+                       else
+                               fprintf(stderr, "Unknown node-kind: %s\n", val);
+               } else if (key == keys.node_action) {
+                       if (!strcmp(val, "delete")) {
+                               node_ctx.action = NODEACT_DELETE;
+                       } else if (!strcmp(val, "add")) {
+                               node_ctx.action = NODEACT_ADD;
+                       } else if (!strcmp(val, "change")) {
+                               node_ctx.action = NODEACT_CHANGE;
+                       } else if (!strcmp(val, "replace")) {
+                               node_ctx.action = NODEACT_REPLACE;
+                       } else {
+                               fprintf(stderr, "Unknown node-action: %s\n", val);
+                               node_ctx.action = NODEACT_UNKNOWN;
+                       }
+               } else if (key == keys.node_copyfrom_path) {
+                       pool_tok_seq(REPO_MAX_PATH_DEPTH, node_ctx.src, "/", val);
+               } else if (key == keys.node_copyfrom_rev) {
+                       node_ctx.srcRev = atoi(val);
+               } else if (key == keys.text_content_length) {
+                       node_ctx.textLength = atoi(val);
+               } else if (key == keys.prop_content_length) {
+                       node_ctx.propLength = atoi(val);
+               } else if (key == keys.content_length) {
+                       len = atoi(val);
+                       buffer_read_line();
+                       if (active_ctx == REV_CTX) {
+                               read_props();
+                       } else if (active_ctx == NODE_CTX) {
+                               handle_node();
+                               active_ctx = REV_CTX;
+                       } else {
+                               fprintf(stderr, "Unexpected content length header: %d\n", len);
+                               buffer_skip_bytes(len);
+                       }
+               }
+       }
+       if (active_ctx == NODE_CTX)
+               handle_node();
+       if (active_ctx != DUMP_CTX)
+               handle_revision();
+}
+
+void svndump_init(const char *filename)
+{
+       buffer_init(filename);
+       repo_init();
+       reset_dump_ctx(~0);
+       reset_rev_ctx(0);
+       reset_node_ctx(NULL);
+       init_keys();
+}
+
+void svndump_deinit(void)
+{
+       log_reset();
+       repo_reset();
+       reset_dump_ctx(~0);
+       reset_rev_ctx(0);
+       reset_node_ctx(NULL);
+       if (buffer_deinit())
+               fprintf(stderr, "Input error\n");
+       if (ferror(stdout))
+               fprintf(stderr, "Output error\n");
+}
+
+void svndump_reset(void)
+{
+       log_reset();
+       buffer_reset();
+       repo_reset();
+       reset_dump_ctx(~0);
+       reset_rev_ctx(0);
+       reset_node_ctx(NULL);
+}
diff --git a/vcs-svn/svndump.h b/vcs-svn/svndump.h
new file mode 100644 (file)
index 0000000..93c412f
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef SVNDUMP_H_
+#define SVNDUMP_H_
+
+void svndump_init(const char *filename);
+void svndump_read(const char *url);
+void svndump_deinit(void);
+void svndump_reset(void);
+
+#endif
diff --git a/vcs-svn/trp.h b/vcs-svn/trp.h
new file mode 100644 (file)
index 0000000..ee35c68
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * C macro implementation of treaps.
+ *
+ * Usage:
+ *   #include <stdint.h>
+ *   #include "trp.h"
+ *   trp_gen(...)
+ *
+ * Licensed under a two-clause BSD-style license.
+ * See LICENSE for details.
+ */
+
+#ifndef TRP_H_
+#define TRP_H_
+
+#define MAYBE_UNUSED __attribute__((__unused__))
+
+/* Node structure. */
+struct trp_node {
+       uint32_t trpn_left;
+       uint32_t trpn_right;
+};
+
+/* Root structure. */
+struct trp_root {
+       uint32_t trp_root;
+};
+
+/* Pointer/Offset conversion. */
+#define trpn_pointer(a_base, a_offset) (a_base##_pointer(a_offset))
+#define trpn_offset(a_base, a_pointer) (a_base##_offset(a_pointer))
+#define trpn_modify(a_base, a_offset) \
+       do { \
+               if ((a_offset) < a_base##_pool.committed) { \
+                       uint32_t old_offset = (a_offset);\
+                       (a_offset) = a_base##_alloc(1); \
+                       *trpn_pointer(a_base, a_offset) = \
+                               *trpn_pointer(a_base, old_offset); \
+               } \
+       } while (0)
+
+/* Left accessors. */
+#define trp_left_get(a_base, a_field, a_node) \
+       (trpn_pointer(a_base, a_node)->a_field.trpn_left)
+#define trp_left_set(a_base, a_field, a_node, a_left) \
+       do { \
+               trpn_modify(a_base, a_node); \
+               trp_left_get(a_base, a_field, a_node) = (a_left); \
+       } while (0)
+
+/* Right accessors. */
+#define trp_right_get(a_base, a_field, a_node) \
+       (trpn_pointer(a_base, a_node)->a_field.trpn_right)
+#define trp_right_set(a_base, a_field, a_node, a_right) \
+       do { \
+               trpn_modify(a_base, a_node); \
+               trp_right_get(a_base, a_field, a_node) = (a_right); \
+       } while (0)
+
+/*
+ * Fibonacci hash function.
+ * The multiplier is the nearest prime to (2^32 times (√5 - 1)/2).
+ * See Knuth §6.4: volume 3, 3rd ed, p518.
+ */
+#define trpn_hash(a_node) (uint32_t) (2654435761u * (a_node))
+
+/* Priority accessors. */
+#define trp_prio_get(a_node) trpn_hash(a_node)
+
+/* Node initializer. */
+#define trp_node_new(a_base, a_field, a_node) \
+       do { \
+               trp_left_set(a_base, a_field, (a_node), ~0); \
+               trp_right_set(a_base, a_field, (a_node), ~0); \
+       } while (0)
+
+/* Internal utility macros. */
+#define trpn_first(a_base, a_field, a_root, r_node) \
+       do { \
+               (r_node) = (a_root); \
+               if ((r_node) == ~0) \
+                       return NULL; \
+               while (~trp_left_get(a_base, a_field, (r_node))) \
+                       (r_node) = trp_left_get(a_base, a_field, (r_node)); \
+       } while (0)
+
+#define trpn_rotate_left(a_base, a_field, a_node, r_node) \
+       do { \
+               (r_node) = trp_right_get(a_base, a_field, (a_node)); \
+               trp_right_set(a_base, a_field, (a_node), \
+                       trp_left_get(a_base, a_field, (r_node))); \
+               trp_left_set(a_base, a_field, (r_node), (a_node)); \
+       } while (0)
+
+#define trpn_rotate_right(a_base, a_field, a_node, r_node) \
+       do { \
+               (r_node) = trp_left_get(a_base, a_field, (a_node)); \
+               trp_left_set(a_base, a_field, (a_node), \
+                       trp_right_get(a_base, a_field, (r_node))); \
+               trp_right_set(a_base, a_field, (r_node), (a_node)); \
+       } while (0)
+
+#define trp_gen(a_attr, a_pre, a_type, a_field, a_base, a_cmp) \
+a_attr a_type MAYBE_UNUSED *a_pre##first(struct trp_root *treap) \
+{ \
+       uint32_t ret; \
+       trpn_first(a_base, a_field, treap->trp_root, ret); \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr a_type MAYBE_UNUSED *a_pre##next(struct trp_root *treap, a_type *node) \
+{ \
+       uint32_t ret; \
+       uint32_t offset = trpn_offset(a_base, node); \
+       if (~trp_right_get(a_base, a_field, offset)) { \
+               trpn_first(a_base, a_field, \
+                       trp_right_get(a_base, a_field, offset), ret); \
+       } else { \
+               uint32_t tnode = treap->trp_root; \
+               ret = ~0; \
+               while (1) { \
+                       int cmp = (a_cmp)(trpn_pointer(a_base, offset), \
+                               trpn_pointer(a_base, tnode)); \
+                       if (cmp < 0) { \
+                               ret = tnode; \
+                               tnode = trp_left_get(a_base, a_field, tnode); \
+                       } else if (cmp > 0) { \
+                               tnode = trp_right_get(a_base, a_field, tnode); \
+                       } else { \
+                               break; \
+                       } \
+               } \
+       } \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr a_type MAYBE_UNUSED *a_pre##search(struct trp_root *treap, a_type *key) \
+{ \
+       int cmp; \
+       uint32_t ret = treap->trp_root; \
+       while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base, ret)))) { \
+               if (cmp < 0) { \
+                       ret = trp_left_get(a_base, a_field, ret); \
+               } else { \
+                       ret = trp_right_get(a_base, a_field, ret); \
+               } \
+       } \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr a_type MAYBE_UNUSED *a_pre##nsearch(struct trp_root *treap, a_type *key) \
+{ \
+       int cmp; \
+       uint32_t ret = treap->trp_root; \
+       while (~ret && (cmp = (a_cmp)(key, trpn_pointer(a_base, ret)))) { \
+               if (cmp < 0) { \
+                       if (!~trp_left_get(a_base, a_field, ret)) \
+                               break; \
+                       ret = trp_left_get(a_base, a_field, ret); \
+               } else { \
+                       ret = trp_right_get(a_base, a_field, ret); \
+               } \
+       } \
+       return trpn_pointer(a_base, ret); \
+} \
+a_attr uint32_t MAYBE_UNUSED a_pre##insert_recurse(uint32_t cur_node, uint32_t ins_node) \
+{ \
+       if (cur_node == ~0) { \
+               return ins_node; \
+       } else { \
+               uint32_t ret; \
+               int cmp = (a_cmp)(trpn_pointer(a_base, ins_node), \
+                                       trpn_pointer(a_base, cur_node)); \
+               if (cmp < 0) { \
+                       uint32_t left = a_pre##insert_recurse( \
+                               trp_left_get(a_base, a_field, cur_node), ins_node); \
+                       trp_left_set(a_base, a_field, cur_node, left); \
+                       if (trp_prio_get(left) < trp_prio_get(cur_node)) \
+                               trpn_rotate_right(a_base, a_field, cur_node, ret); \
+                       else \
+                               ret = cur_node; \
+               } else { \
+                       uint32_t right = a_pre##insert_recurse( \
+                               trp_right_get(a_base, a_field, cur_node), ins_node); \
+                       trp_right_set(a_base, a_field, cur_node, right); \
+                       if (trp_prio_get(right) < trp_prio_get(cur_node)) \
+                               trpn_rotate_left(a_base, a_field, cur_node, ret); \
+                       else \
+                               ret = cur_node; \
+               } \
+               return ret; \
+       } \
+} \
+a_attr void MAYBE_UNUSED a_pre##insert(struct trp_root *treap, a_type *node) \
+{ \
+       uint32_t offset = trpn_offset(a_base, node); \
+       trp_node_new(a_base, a_field, offset); \
+       treap->trp_root = a_pre##insert_recurse(treap->trp_root, offset); \
+} \
+a_attr uint32_t MAYBE_UNUSED a_pre##remove_recurse(uint32_t cur_node, uint32_t rem_node) \
+{ \
+       int cmp = a_cmp(trpn_pointer(a_base, rem_node), \
+                       trpn_pointer(a_base, cur_node)); \
+       if (cmp == 0) { \
+               uint32_t ret; \
+               uint32_t left = trp_left_get(a_base, a_field, cur_node); \
+               uint32_t right = trp_right_get(a_base, a_field, cur_node); \
+               if (left == ~0) { \
+                       if (right == ~0) \
+                               return ~0; \
+               } else if (right == ~0 || trp_prio_get(left) < trp_prio_get(right)) { \
+                       trpn_rotate_right(a_base, a_field, cur_node, ret); \
+                       right = a_pre##remove_recurse(cur_node, rem_node); \
+                       trp_right_set(a_base, a_field, ret, right); \
+                       return ret; \
+               } \
+               trpn_rotate_left(a_base, a_field, cur_node, ret); \
+               left = a_pre##remove_recurse(cur_node, rem_node); \
+               trp_left_set(a_base, a_field, ret, left); \
+               return ret; \
+       } else if (cmp < 0) { \
+               uint32_t left = a_pre##remove_recurse( \
+                       trp_left_get(a_base, a_field, cur_node), rem_node); \
+               trp_left_set(a_base, a_field, cur_node, left); \
+               return cur_node; \
+       } else { \
+               uint32_t right = a_pre##remove_recurse( \
+                       trp_right_get(a_base, a_field, cur_node), rem_node); \
+               trp_right_set(a_base, a_field, cur_node, right); \
+               return cur_node; \
+       } \
+} \
+a_attr void MAYBE_UNUSED a_pre##remove(struct trp_root *treap, a_type *node) \
+{ \
+       treap->trp_root = a_pre##remove_recurse(treap->trp_root, \
+               trpn_offset(a_base, node)); \
+} \
+
+#endif
diff --git a/vcs-svn/trp.txt b/vcs-svn/trp.txt
new file mode 100644 (file)
index 0000000..eb4c191
--- /dev/null
@@ -0,0 +1,103 @@
+Motivation
+==========
+
+Treaps provide a memory-efficient binary search tree structure.
+Insertion/deletion/search are about as about as fast in the average
+case as red-black trees and the chances of worst-case behavior are
+vanishingly small, thanks to (pseudo-)randomness.  The bad worst-case
+behavior is a small price to pay, given that treaps are much simpler
+to implement.
+
+API
+===
+
+The trp API generates a data structure and functions to handle a
+large growing set of objects stored in a pool.
+
+The caller:
+
+. Specifies parameters for the generated functions with the
+  trp_gen(static, foo_, ...) macro.
+
+. Allocates a `struct trp_root` variable and sets it to {~0}.
+
+. Adds new nodes to the set using `foo_insert`.
+
+. Can find a specific item in the set using `foo_search`.
+
+. Can iterate over items in the set using `foo_first` and `foo_next`.
+
+. Can remove an item from the set using `foo_remove`.
+
+Example:
+
+----
+struct ex_node {
+       const char *s;
+       struct trp_node ex_link;
+};
+static struct trp_root ex_base = {~0};
+obj_pool_gen(ex, struct ex_node, 4096);
+trp_gen(static, ex_, struct ex_node, ex_link, ex, strcmp)
+struct ex_node *item;
+
+item = ex_pointer(ex_alloc(1));
+item->s = "hello";
+ex_insert(&ex_base, item);
+item = ex_pointer(ex_alloc(1));
+item->s = "goodbye";
+ex_insert(&ex_base, item);
+for (item = ex_first(&ex_base); item; item = ex_next(&ex_base, item))
+       printf("%s\n", item->s);
+----
+
+Functions
+---------
+
+trp_gen(attr, foo_, node_type, link_field, pool, cmp)::
+
+       Generate a type-specific treap implementation.
++
+. The storage class for generated functions will be 'attr' (e.g., `static`).
+. Generated function names are prefixed with 'foo_' (e.g., `treap_`).
+. Treap nodes will be of type 'node_type' (e.g., `struct treap_node`).
+  This type must be a struct with at least one `struct trp_node` field
+  to point to its children.
+. The field used to access child nodes will be 'link_field'.
+. All treap nodes must lie in the 'pool' object pool.
+. Treap nodes must be totally ordered by the 'cmp' relation, with the
+  following prototype:
++
+int (*cmp)(node_type \*a, node_type \*b)
++
+and returning a value less than, equal to, or greater than zero
+according to the result of comparison.
+
+void foo_insert(struct trp_root *treap, node_type \*node)::
+
+       Insert node into treap.  If inserted multiple times,
+       a node will appear in the treap multiple times.
+
+void foo_remove(struct trp_root *treap, node_type \*node)::
+
+       Remove node from treap.  Caller must ensure node is
+       present in treap before using this function.
+
+node_type *foo_search(struct trp_root \*treap, node_type \*key)::
+
+       Search for a node that matches key.  If no match is found,
+       result is NULL.
+
+node_type *foo_nsearch(struct trp_root \*treap, node_type \*key)::
+
+       Like `foo_search`, but if if the key is missing return what
+       would be key's successor, were key in treap (NULL if no
+       successor).
+
+node_type *foo_first(struct trp_root \*treap)::
+
+       Find the first item from the treap, in sorted order.
+
+node_type *foo_next(struct trp_root \*treap, node_type \*node)::
+
+       Find the next item.
index afb4f6f773a3ed909433778f4b4da3740ded2b18..fd8ead33ed72c37e690ee1fc5b8568f629c95145 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -40,7 +40,8 @@ void *xmalloc(size_t size)
                if (!ret && !size)
                        ret = malloc(1);
                if (!ret)
-                       die("Out of memory, malloc failed");
+                       die("Out of memory, malloc failed (tried to allocate %lu bytes)",
+                           (unsigned long)size);
        }
 #ifdef XMALLOC_POISON
        memset(ret, 0xA5, size);
index 2f9e33c8fa172b129fd956abc4f74a5bf5543ba7..54b6b03b9cc4e168e6b6429679b61dc2a1d5197b 100644 (file)
@@ -313,8 +313,10 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
        DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
        if (!s->show_untracked_files)
                DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
-       if (s->ignore_submodule_arg)
+       if (s->ignore_submodule_arg) {
+               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+    }
        rev.diffopt.format_callback = wt_status_collect_changed_cb;
        rev.diffopt.format_callback_data = s;
        rev.prune_data = s->pathspec;
@@ -331,8 +333,10 @@ static void wt_status_collect_changes_index(struct wt_status *s)
        opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
        setup_revisions(0, NULL, &rev, &opt);
 
-       if (s->ignore_submodule_arg)
+       if (s->ignore_submodule_arg) {
+               DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
                handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+       }
 
        rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = wt_status_collect_updated_cb;