aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.mailmap1
-rw-r--r--Documentation/RelNotes-1.6.5.txt161
-rw-r--r--Documentation/config.txt36
-rw-r--r--Documentation/git-add.txt11
-rw-r--r--Documentation/git-am.txt21
-rw-r--r--Documentation/git-apply.txt13
-rw-r--r--Documentation/git-archive.txt16
-rw-r--r--Documentation/git-branch.txt7
-rw-r--r--Documentation/git-checkout.txt15
-rw-r--r--Documentation/git-clean.txt1
-rw-r--r--Documentation/git-clone.txt17
-rw-r--r--Documentation/git-commit.txt12
-rw-r--r--Documentation/git-fast-export.txt8
-rw-r--r--Documentation/git-grep.txt5
-rw-r--r--Documentation/git-init-db.txt2
-rw-r--r--Documentation/git-init.txt5
-rw-r--r--Documentation/git-instaweb.txt2
-rw-r--r--Documentation/git-ls-files.txt2
-rw-r--r--Documentation/git-mailinfo.txt21
-rw-r--r--Documentation/git-merge-base.txt9
-rw-r--r--Documentation/git-mv.txt1
-rw-r--r--Documentation/git-prune-packed.txt4
-rw-r--r--Documentation/git-push.txt3
-rw-r--r--Documentation/git-quiltimport.txt2
-rw-r--r--Documentation/git-read-tree.txt5
-rw-r--r--Documentation/git-rebase.txt3
-rw-r--r--Documentation/git-remote-helpers.txt71
-rw-r--r--Documentation/git-replace.txt71
-rw-r--r--Documentation/git-reset.txt15
-rw-r--r--Documentation/git-rev-list.txt22
-rw-r--r--Documentation/git-send-email.txt5
-rw-r--r--Documentation/git-show-branch.txt13
-rw-r--r--Documentation/git-stash.txt39
-rw-r--r--Documentation/git-submodule.txt36
-rw-r--r--Documentation/git-svn.txt6
-rw-r--r--Documentation/git-tag.txt20
-rw-r--r--Documentation/git-upload-pack.txt2
-rw-r--r--Documentation/git-verify-pack.txt11
-rw-r--r--Documentation/githooks.txt31
-rw-r--r--Documentation/pt_BR/gittutorial.txt675
-rw-r--r--Documentation/technical/api-run-command.txt31
-rw-r--r--Documentation/technical/api-tree-walking.txt147
-rw-r--r--Documentation/urls.txt18
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--INSTALL48
-rw-r--r--Makefile68
l---------RelNotes2
-rw-r--r--abspath.c6
-rw-r--r--advice.c27
-rw-r--r--advice.h9
-rw-r--r--archive.c2
-rw-r--r--arm/sha1.c82
-rw-r--r--arm/sha1.h23
-rw-r--r--arm/sha1_arm.S183
-rw-r--r--block-sha1/sha1.c282
-rw-r--r--block-sha1/sha1.h22
-rw-r--r--builtin-add.c49
-rw-r--r--builtin-apply.c171
-rw-r--r--builtin-archive.c33
-rw-r--r--builtin-blame.c2
-rw-r--r--builtin-branch.c2
-rw-r--r--builtin-checkout.c21
-rw-r--r--builtin-clean.c2
-rw-r--r--builtin-clone.c90
-rw-r--r--builtin-commit.c175
-rw-r--r--builtin-describe.c5
-rw-r--r--builtin-diff.c2
-rw-r--r--builtin-fast-export.c9
-rw-r--r--builtin-fetch.c2
-rw-r--r--builtin-for-each-ref.c2
-rw-r--r--builtin-fsck.c1
-rw-r--r--builtin-grep.c56
-rw-r--r--builtin-init-db.c86
-rw-r--r--builtin-log.c6
-rw-r--r--builtin-mailinfo.c107
-rw-r--r--builtin-mailsplit.c47
-rw-r--r--builtin-merge-base.c2
-rw-r--r--builtin-merge.c2
-rw-r--r--builtin-mv.c2
-rw-r--r--builtin-pack-objects.c36
-rw-r--r--builtin-prune-packed.c29
-rw-r--r--builtin-prune.c1
-rw-r--r--builtin-push.c7
-rw-r--r--builtin-read-tree.c176
-rw-r--r--builtin-receive-pack.c38
-rw-r--r--builtin-reflog.c2
-rw-r--r--builtin-remote.c4
-rw-r--r--builtin-replace.c159
-rw-r--r--builtin-reset.c26
-rw-r--r--builtin-send-pack.c3
-rw-r--r--builtin-shortlog.c2
-rw-r--r--builtin-show-branch.c6
-rw-r--r--builtin-tag.c2
-rw-r--r--builtin-unpack-objects.c2
-rw-r--r--builtin-update-server-info.c25
-rw-r--r--builtin-verify-pack.c83
-rw-r--r--builtin-verify-tag.c21
-rw-r--r--builtin-write-tree.c40
-rw-r--r--builtin.h3
-rw-r--r--bundle.c2
-rw-r--r--cache.h30
-rw-r--r--commit.c12
-rw-r--r--commit.h4
-rw-r--r--compat/bswap.h36
-rw-r--r--compat/mingw.c16
-rw-r--r--compat/mingw.h8
-rw-r--r--compat/snprintf.c3
-rw-r--r--config.c5
-rw-r--r--configure.ac10
-rw-r--r--connect.c2
-rwxr-xr-xcontrib/completion/git-completion.bash36
-rw-r--r--contrib/emacs/git.el26
-rwxr-xr-xcontrib/fast-import/git-p4181
-rwxr-xr-xcontrib/fast-import/import-directories.perl416
-rwxr-xr-xcontrib/fast-import/import-tars.perl50
-rw-r--r--convert.c2
-rw-r--r--copy.c21
-rw-r--r--date.c280
-rw-r--r--diff-delta.c2
-rw-r--r--diff-lib.c28
-rw-r--r--diff.c2
-rw-r--r--environment.c2
-rwxr-xr-xgit-add--interactive.perl210
-rwxr-xr-xgit-am.sh35
-rw-r--r--git-compat-util.h4
-rwxr-xr-xgit-cvsimport.perl41
-rwxr-xr-xgit-instaweb.sh57
-rwxr-xr-xgit-pull.sh18
-rwxr-xr-xgit-rebase.sh9
-rwxr-xr-xgit-request-pull.sh24
-rwxr-xr-xgit-stash.sh140
-rwxr-xr-xgit-submodule.sh96
-rwxr-xr-xgit-svn.perl110
-rwxr-xr-xgit-web--browse.sh3
-rw-r--r--git.c20
-rw-r--r--git.spec.in2
-rw-r--r--gitk-git/gitk31
-rw-r--r--gitweb/INSTALL9
-rw-r--r--gitweb/git-favicon.pngbin164 -> 115 bytes
-rw-r--r--gitweb/git-logo.pngbin208 -> 207 bytes
-rw-r--r--gitweb/gitweb.css24
-rwxr-xr-xgitweb/gitweb.perl84
-rw-r--r--graph.c19
-rw-r--r--grep.h1
-rw-r--r--http-fetch.c (renamed from builtin-http-fetch.c)5
-rw-r--r--http.c22
-rw-r--r--ll-merge.c4
-rw-r--r--merge-recursive.c25
-rw-r--r--mktag.c7
-rw-r--r--mozilla-sha1/sha1.c151
-rw-r--r--mozilla-sha1/sha1.h50
-rw-r--r--object.c9
-rw-r--r--pack-redundant.c29
-rw-r--r--pager.c6
-rw-r--r--parse-options.c14
-rw-r--r--parse-options.h8
-rw-r--r--patch-delta.c2
-rw-r--r--progress.c2
-rw-r--r--read-cache.c26
-rw-r--r--refs.c5
-rw-r--r--refs.h1
-rw-r--r--remote-curl.c139
-rw-r--r--remote.c85
-rw-r--r--replace_object.c114
-rw-r--r--rerere.c2
-rw-r--r--revision.c17
-rw-r--r--revision.h1
-rw-r--r--run-command.c112
-rw-r--r--run-command.h13
-rw-r--r--send-pack.h1
-rw-r--r--sha1_file.c23
-rw-r--r--strbuf.c15
-rw-r--r--strbuf.h1
-rw-r--r--t/Makefile2
-rw-r--r--t/gitweb-lib.sh73
-rw-r--r--t/lib-cvs.sh75
-rw-r--r--t/lib-git-svn.sh2
-rwxr-xr-xt/lib-patch-mode.sh41
-rwxr-xr-xt/t0001-init.sh83
-rwxr-xr-xt/t0006-date.sh75
-rwxr-xr-xt/t1009-read-tree-new-index.sh25
-rwxr-xr-xt/t2016-checkout-patch.sh107
-rwxr-xr-xt/t3400-rebase.sh26
-rwxr-xr-xt/t3404-rebase-interactive.sh2
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh2
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh2
-rwxr-xr-xt/t3903-stash.sh19
-rwxr-xr-xt/t3904-stash-patch.sh55
-rwxr-xr-xt/t4014-format-patch.sh3
-rwxr-xr-xt/t4020-diff-external.sh2
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh31
-rwxr-xr-xt/t4107-apply-ignore-whitespace.sh185
-rwxr-xr-xt/t5100-mailinfo.sh24
-rw-r--r--t/t5100/info00145
-rw-r--r--t/t5100/info0014--scissors5
-rw-r--r--t/t5100/msg001418
-rw-r--r--t/t5100/msg0014--scissors4
-rw-r--r--t/t5100/patch001464
-rw-r--r--t/t5100/patch0014--scissors64
-rw-r--r--t/t5100/sample.mbox89
-rwxr-xr-xt/t5304-prune.sh54
-rwxr-xr-xt/t5500-fetch-pack.sh47
-rwxr-xr-xt/t5501-post-upload-pack.sh69
-rwxr-xr-xt/t5516-fetch-push.sh47
-rwxr-xr-xt/t5520-pull.sh11
-rwxr-xr-xt/t5530-upload-pack-error.sh17
-rwxr-xr-xt/t5706-clone-branch.sh68
-rwxr-xr-xt/t6010-merge-base.sh18
-rwxr-xr-xt/t6015-rev-list-show-all-parents.sh31
-rwxr-xr-xt/t6016-rev-list-graph-simplify-history.sh276
-rwxr-xr-xt/t6020-merge-df.sh23
-rwxr-xr-xt/t6050-replace.sh200
-rwxr-xr-xt/t7002-grep.sh51
-rwxr-xr-xt/t7060-wtstatus.sh58
-rwxr-xr-xt/t7102-reset.sh3
-rwxr-xr-xt/t7105-reset-patch.sh69
-rwxr-xr-xt/t7401-submodule-summary.sh22
-rwxr-xr-xt/t7407-submodule-foreach.sh237
-rwxr-xr-xt/t7408-submodule-reference.sh (renamed from t/t7406-submodule-reference.sh)0
-rwxr-xr-xt/t9101-git-svn-props.sh5
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh10
-rwxr-xr-xt/t9107-git-svn-migrate.sh28
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh52
-rwxr-xr-xt/t9135-git-svn-moved-branch-empty-file.sh7
-rwxr-xr-xt/t9143-git-svn-gc.sh10
-rwxr-xr-xt/t9144-git-svn-old-rev_map.sh31
-rwxr-xr-xt/t9145-git-svn-master-branch.sh25
-rwxr-xr-xt/t9500-gitweb-standalone-no-errors.sh67
-rw-r--r--t/t9501-gitweb-standalone-http-status.sh78
-rwxr-xr-xt/t9600-cvsimport.sh44
-rwxr-xr-xt/t9601-cvsimport-vendor-branch.sh86
-rw-r--r--t/t9601/cvsroot/.gitattributes1
-rw-r--r--t/t9601/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9601/cvsroot/module/added-imported.txt,v44
-rw-r--r--t/t9601/cvsroot/module/imported-anonymously.txt,v42
-rw-r--r--t/t9601/cvsroot/module/imported-modified-imported.txt,v76
-rw-r--r--t/t9601/cvsroot/module/imported-modified.txt,v59
-rw-r--r--t/t9601/cvsroot/module/imported-once.txt,v43
-rw-r--r--t/t9601/cvsroot/module/imported-twice.txt,v60
-rwxr-xr-xt/t9602-cvsimport-branches-tags.sh79
-rw-r--r--t/t9602/README62
-rw-r--r--t/t9602/cvsroot/.gitattributes1
-rw-r--r--t/t9602/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9602/cvsroot/module/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub1/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub1/subsubA/default,v101
-rw-r--r--t/t9602/cvsroot/module/sub1/subsubB/default,v107
-rw-r--r--t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v59
-rw-r--r--t/t9602/cvsroot/module/sub2/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub2/subsubA/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub3/default,v102
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh40
-rw-r--r--t/t9603/cvsroot/.gitattributes1
-rw-r--r--t/t9603/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9603/cvsroot/module/a,v74
-rw-r--r--t/t9603/cvsroot/module/b,v90
-rw-r--r--t/test-lib.sh25
-rw-r--r--test-date.c63
-rw-r--r--test-delta.c2
-rw-r--r--transport-helper.c168
-rw-r--r--transport.c192
-rw-r--r--transport.h4
-rw-r--r--unpack-trees.h24
-rw-r--r--update-server-info.c28
-rw-r--r--upload-pack.c140
-rw-r--r--var.c5
-rw-r--r--wt-status.c487
-rw-r--r--wt-status.h26
269 files changed, 10125 insertions, 2214 deletions
diff --git a/.gitignore b/.gitignore
index 41c0b20a7..47672b0c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,7 +104,9 @@ git-receive-pack
git-reflog
git-relink
git-remote
+git-remote-curl
git-repack
+git-replace
git-repo-config
git-request-pull
git-rerere
diff --git a/.mailmap b/.mailmap
index 373476bdc..975e6758e 100644
--- a/.mailmap
+++ b/.mailmap
@@ -41,6 +41,7 @@ Michele Ballabio <barra_cuda@katamail.com>
Nanako Shiraishi <nanako3@bluebottle.com>
Nanako Shiraishi <nanako3@lavabit.com>
Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
+<nico@fluxnic.net> <nico@cam.org>
Philippe Bruhat <book@cpan.org>
Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
René Scharfe <rene.scharfe@lsrfire.ath.cx>
diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt
new file mode 100644
index 000000000..25529f723
--- /dev/null
+++ b/Documentation/RelNotes-1.6.5.txt
@@ -0,0 +1,161 @@
+GIT v1.6.5 Release Notes
+========================
+
+In git 1.7.0, which was planned to be the release after 1.6.5, "git
+push" into a branch that is currently checked out will be refused by
+default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyCurrentBranch in the receiving
+repository.
+
+Also, "git push $there :$killed" to delete the branch $killed in a remote
+repository $there, when $killed branch is the current branch pointed at by
+its HEAD, will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyDeleteCurrent in the receiving
+repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing. Please refer to:
+
+ http://git.or.cz/gitwiki/GitFaq#non-bare
+ http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+Updates since v1.6.4
+--------------------
+
+(subsystems)
+
+ * various updates to git-svn and gitweb.
+
+(portability)
+
+ * more improvements on mingw port.
+
+ * mingw will also give FRSX as the default value for the LESS
+ environment variable when the user does not have one.
+
+(performance)
+
+ * On major platforms, the system can be compiled to use with Linus's
+ block-sha1 implementation of the SHA-1 hash algorithm, which
+ outperforms the default fallback implementation we borrowed from
+ Mozzilla.
+
+ * Unnecessary inefficiency in deepening of a shallow repository has
+ been removed.
+
+ * The "git" main binary used to link with libcurl, which then dragged
+ in a large number of external libraries. When using basic plumbing
+ commands in scripts, this unnecessarily slowed things down. We now
+ implement http/https/ftp transfer as a separate executable as we
+ used to.
+
+ * "git clone" run locally hardlinks or copies the files in .git/ to
+ newly created repository. It used to give new mtime to copied files,
+ but this delayed garbage collection to trigger unnecessarily in the
+ cloned repository. We now preserve mtime for these files to avoid
+ this issue.
+
+(usability, bells and whistles)
+
+ * Human writable date format to various options, e.g. --since=yesterday,
+ master@{2000.09.17}, are taught to infer some omitted input properly.
+
+ * A few programs gave verbose "advice" messages to help uninitiated
+ people when issuing error messages. An infrastructure to allow
+ users to squelch them has been introduced, and a few such messages
+ can be silenced now.
+
+ * refs/replace/ hierarchy is designed to be usable as a replacement
+ of the "grafts" mechanism, with the added advantage that it can be
+ transferred across repositories.
+
+ * "git am" learned to optionally ignore whitespace differences.
+
+ * "git am" handles input e-mail files that has CRLF line endings sensibly.
+
+ * "git am" learned "--scissors" option to allow you to discard early part
+ of an incoming e-mail.
+
+ * "git checkout", "git reset" and "git stash" learned to pick and
+ choose to use selected changes you made, similar to "git add -p".
+
+ * "git clone" learned a "-b" option to pick a HEAD to check out
+ different from the remote's default branch.
+
+ * "git commit --dry-run $args" is a new recommended way to ask "what would
+ happen if I try to commit with these arguments."
+
+ * "git commit --dry-run" and "git status" shows conflicted paths in a
+ separate section to make them easier to spot during a merge.
+
+ * "git cvsimport" now supports password-protected pserver access even
+ when the password is not taken from ~/.cvspass file.
+
+ * "git fast-export" learned --no-data option that can be useful when
+ reordering commits and trees without touching the contents of
+ blobs.
+
+ * "git fast-import" has a pair of new front-end in contrib/ area.
+
+ * "git init" learned to mkdir/chdir into a directory when given an
+ extra argument (i.e. "git init this").
+
+ * "git instaweb" optionally can use mongoose as the web server.
+
+ * "git log --decorate" can optionally be told with --decorate=full to
+ give the reference name in full.
+
+ * "git merge" issued an unnecessarily scary message when it detected
+ that the merge may have to touch the path that the user has local
+ uncommitted changes to. The message has been reworded to make it
+ clear that the command aborted, without doing any harm.
+
+ * "git push" can be told to be --quiet.
+
+ * "git push" pays attention to url.$base.pushInsteadOf and uses a URL
+ that is derived from the URL used for fetching.
+
+ * informational output from "git reset" that lists the locally modified
+ paths is made consistent with that of "git checkout $another_branch".
+
+ * "git submodule" learned to give submodule name to scripts run with
+ "foreach" subcommand.
+
+ * various subcommands to "git submodule" learned --recursive option.
+
+ * "git submodule summary" learned --files option to compare the work
+ tree vs the commit bound at submodule path, instead of comparing
+ the index.
+
+ * "git upload-pack", which is the server side support for "git clone" and
+ "git fetch", can call a new post-upload-pack hook for statistics purposes.
+
+(developers)
+
+ * With GIT_TEST_OPTS="--root=/p/a/t/h", tests can be run outside the
+ source directory; using tmpfs may give faster turnaround.
+
+
+Fixes since v1.6.4
+------------------
+
+# All of the fixes in v1.6.4.X maintenance series are included in this
+# release, unless otherwise noted.
+
+# Here are fixes that this release has, but have not been backported to
+# v1.6.4.X series.
+
+--
+exec >/var/tmp/1
+O=v1.6.4.2-298-gdf01e7c
+O=v1.6.5-rc0-49-g5f2b1e6
+echo O=$(git describe master)
+git shortlog --no-merges $O..master --not maint
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2632c5149..be0b8caca 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -113,6 +113,21 @@ For command-specific variables, you will find a more detailed description
in the appropriate manual page. You will find a description of non-core
porcelain configuration variables in the respective porcelain documentation.
+advice.*::
+ When set to 'true', display the given optional help message.
+ When set to 'false', do not display. The configuration variables
+ are:
++
+--
+ pushNonFastForward::
+ Advice shown when linkgit:git-push[1] refuses
+ non-fast-forward refs. Default: true.
+ statusHints::
+ Directions on how to stage/unstage/add shown in the
+ output of linkgit:git-status[1] and the template shown
+ when writing commit messages. Default: true.
+--
+
core.fileMode::
If false, the executable bit differences between the index and
the working copy are ignored; useful on broken filesystems like FAT.
@@ -461,6 +476,14 @@ it will be treated as a shell command. For example, defining
executed from the top-level directory of a repository, which may
not necessarily be the current directory.
+apply.ignorewhitespace::
+ When set to 'change', tells 'git-apply' to ignore changes in
+ whitespace, in the same way as the '--ignore-space-change'
+ option.
+ When set to one of: no, none, never, false tells 'git-apply' to
+ respect all whitespace differences.
+ See linkgit:git-apply[1].
+
apply.whitespace::
Tells 'git-apply' how to handle whitespaces, in the same way
as the '--whitespace' option. See linkgit:git-apply[1].
@@ -1492,6 +1515,19 @@ url.<base>.insteadOf::
never-before-seen repository on the site. When more than one
insteadOf strings match a given URL, the longest match is used.
+url.<base>.pushInsteadOf::
+ Any URL that starts with this value will not be pushed to;
+ instead, it will be rewritten to start with <base>, and the
+ resulting URL will be pushed to. In cases where some site serves
+ a large number of repositories, and serves them with multiple
+ access methods, some of which do not allow push, this feature
+ allows people to specify a pull-only URL and have git
+ automatically use an appropriate URL to push, even for a
+ never-before-seen repository on the site. When more than one
+ pushInsteadOf strings match a given URL, the longest match is
+ used. If a remote has an explicit pushurl, git will ignore this
+ setting for that remote.
+
user.email::
Your email address to be recorded in any newly created commits.
Can be overridden by the 'GIT_AUTHOR_EMAIL', 'GIT_COMMITTER_EMAIL', and
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index e67b7e875..45ebf87ca 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -72,9 +72,14 @@ OPTIONS
-p::
--patch::
- Similar to Interactive mode but the initial command loop is
- bypassed and the 'patch' subcommand is invoked using each of
- the specified filepatterns before exiting.
+ Interactively choose hunks of patch between the index and the
+ work tree and add them to the index. This gives the user a chance
+ to review the difference before adding modified contents to the
+ index.
+
+ This effectively runs ``add --interactive``, but bypasses the
+ initial command menu and directly jumps to `patch` subcommand.
+ See ``Interactive mode'' for details.
-e, \--edit::
Open the diff vs. the index in an editor and let the user
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 32e689b2b..67ad5da9c 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -11,9 +11,9 @@ SYNOPSIS
[verse]
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive] [--committer-date-is-author-date]
- [--ignore-date]
+ [--ignore-date] [--ignore-space-change | --ignore-whitespace]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
- [--reject] [-q | --quiet]
+ [--reject] [-q | --quiet] [--scissors | --no-scissors]
[<mbox> | <Maildir>...]
'git am' (--skip | --resolved | --abort)
@@ -39,6 +39,14 @@ OPTIONS
--keep::
Pass `-k` flag to 'git-mailinfo' (see linkgit:git-mailinfo[1]).
+-c::
+--scissors::
+ Remove everything in body before a scissors line (see
+ linkgit:git-mailinfo[1]).
+
+---no-scissors::
+ Ignore scissors lines (see linkgit:git-mailinfo[1]).
+
-q::
--quiet::
Be quiet. Only print error messages.
@@ -65,6 +73,9 @@ default. You can use `--no-utf8` to override this.
it is supposed to apply to and we have those blobs
available locally.
+--ignore-date::
+--ignore-space-change::
+--ignore-whitespace::
--whitespace=<option>::
-C<n>::
-p<n>::
@@ -125,10 +136,8 @@ the commit, after stripping common prefix "[PATCH <anything>]".
The "Subject: " line is supposed to concisely describe what the
commit is about in one line of text.
-"From: " and "Subject: " lines starting the body (the rest of the
-message after the blank line terminating the RFC2822 headers)
-override the respective commit author name and title values taken
-from the headers.
+"From: " and "Subject: " lines starting the body override the respective
+commit author name and title values taken from the headers.
The commit message is formed by the title taken from the
"Subject: ", a blank line and the body of the message up to
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 735374d7d..5ee8c91f2 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -13,6 +13,7 @@ SYNOPSIS
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
[--allow-binary-replacement | --binary] [--reject] [-z]
[-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
+ [--ignore-space-change | --ignore-whitespace ]
[--whitespace=<nowarn|warn|fix|error|error-all>]
[--exclude=PATH] [--include=PATH] [--directory=<root>]
[--verbose] [<patch>...]
@@ -149,6 +150,14 @@ patch to each path is used. A patch to a path that does not match any
include/exclude pattern is used by default if there is no include pattern
on the command line, and ignored if there is any include pattern.
+--ignore-space-change::
+--ignore-whitespace::
+ When applying a patch, ignore changes in whitespace in context
+ lines if necessary.
+ Context lines will preserve their whitespace, and they will not
+ undergo whitespace fixing regardless of the value of the
+ `--whitespace` option. New lines will still be fixed, though.
+
--whitespace=<action>::
When applying a patch, detect a new or modified line that has
whitespace errors. What are considered whitespace errors is
@@ -205,6 +214,10 @@ running `git apply --directory=modules/git-gui`.
Configuration
-------------
+apply.ignorewhitespace::
+ Set to 'change' if you want changes in whitespace to be ignored by default.
+ Set to one of: no, none, never, false if you want changes in
+ whitespace to be significant.
apply.whitespace::
When no `--whitespace` flag is given from the command
line, this configuration item is used as the default.
diff --git a/Documentation/git-archive.txt b/Documentation/git-archive.txt
index 92444ddf1..3d1c1e75b 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
- [--output=<file>] [--worktree-attributes]
+ [-o | --output=<file>] [--worktree-attributes]
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
[path...]
@@ -34,8 +34,11 @@ OPTIONS
-------
--format=<fmt>::
- Format of the resulting archive: 'tar' or 'zip'. The default
- is 'tar'.
+ Format of the resulting archive: 'tar' or 'zip'. If this option
+ is not given, and the output file is specified, the format is
+ inferred from the filename if possible (e.g. writing to "foo.zip"
+ makes the output to be in the zip format). Otherwise the output
+ format is `tar`.
-l::
--list::
@@ -48,6 +51,7 @@ OPTIONS
--prefix=<prefix>/::
Prepend <prefix>/ to each filename in the archive.
+-o <file>::
--output=<file>::
Write the archive to <file> instead of stdout.
@@ -129,6 +133,12 @@ git archive --format=zip --prefix=git-docs/ HEAD:Documentation/ > git-1.4.0-docs
Put everything in the current head's Documentation/ directory
into 'git-1.4.0-docs.zip', with the prefix 'git-docs/'.
+git archive -o latest.zip HEAD::
+
+ Create a Zip archive that contains the contents of the latest
+ commit on the current branch. Note that the output format is
+ inferred by the extension of the output file.
+
SEE ALSO
--------
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index ae201deb7..aad71dc59 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -76,6 +76,7 @@ OPTIONS
based sha1 expressions such as "<branchname>@\{yesterday}".
-f::
+--force::
Reset <branchname> to <startpoint> if <branchname> exists
already. Without `-f` 'git-branch' refuses to change an existing branch.
@@ -209,6 +210,12 @@ but different purposes:
- `--no-merged` is used to find branches which are candidates for merging
into HEAD, since those branches are not fully contained by HEAD.
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1],
+linkgit:git-fetch[1],
+linkgit:git-remote[1].
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <gitster@pobox.com>
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index ad4b31e89..37c1810e3 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -11,6 +11,7 @@ SYNOPSIS
'git checkout' [-q] [-f] [-m] [<branch>]
'git checkout' [-q] [-f] [-m] [-b <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
+'git checkout' --patch [<tree-ish>] [--] [<paths>...]
DESCRIPTION
-----------
@@ -25,7 +26,7 @@ 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.
-When <paths> are given, this command does *not* switch
+When <paths> or --patch are given, this command does *not* switch
branches. It updates the named paths in the working tree from
the index file, or from a named <tree-ish> (most often a commit). In
this case, the `-b` and `--track` options are meaningless and giving
@@ -45,9 +46,11 @@ file can be discarded to recreate the original conflicted merge result.
OPTIONS
-------
-q::
+--quiet::
Quiet, suppress feedback messages.
-f::
+--force::
When switching branches, proceed even if the index or the
working tree differs from HEAD. This is used to throw away
local changes.
@@ -113,6 +116,16 @@ the conflicted merge in the specified paths.
"merge" (default) and "diff3" (in addition to what is shown by
"merge" style, shows the original contents).
+-p::
+--patch::
+ Interactively select hunks in the difference between the
+ <tree-ish> (or the index, if unspecified) and the working
+ tree. The chosen hunks are then applied in reverse to the
+ working tree (and if a <tree-ish> was specified, the index).
++
+This means that you can use `git checkout -p` to selectively discard
+edits from your current working tree.
+
<branch>::
Branch to checkout; if it refers to a branch (i.e., a name that,
when prepended with "refs/heads/", is a valid ref), then that
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index ae8938b2d..9d291bdd2 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -32,6 +32,7 @@ OPTIONS
if you really want to remove such a directory.
-f::
+--force::
If the git configuration specifies clean.requireForce as true,
'git-clean' will refuse to run unless given -f or -n.
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 1709a2dbd..aacf4fd32 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -12,7 +12,7 @@ SYNOPSIS
'git clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
- [--depth <depth>] [--] <repository> [<directory>]
+ [--depth <depth>] [--recursive] [--] <repository> [<directory>]
DESCRIPTION
-----------
@@ -127,6 +127,13 @@ objects from the source repository into a pack in the cloned repository.
Instead of using the remote name 'origin' to keep track
of the upstream repository, use <name>.
+--branch <name>::
+-b <name>::
+ Instead of pointing the newly created HEAD to the branch pointed
+ to by the cloned repository's HEAD, point to <name> branch
+ instead. In a non-bare repository, this is the branch that will
+ be checked out.
+
--upload-pack <upload-pack>::
-u <upload-pack>::
When given, and the repository to clone from is accessed
@@ -147,6 +154,14 @@ objects from the source repository into a pack in the cloned repository.
with a long history, and would want to send in fixes
as patches.
+--recursive::
+ After the clone is created, initialize all submodules within,
+ using their default settings. This is equivalent to running
+ 'git submodule update --init --recursive' immediately after
+ the clone is finished. This option is ignored if the cloned
+ repository does not have a worktree/checkout (i.e. if any of
+ `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+
<repository>::
The (possibly remote) repository to clone from. See the
<<URLS,URLS>> section below for more information on specifying
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index b5d81be7e..0578a40d8 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -8,7 +8,7 @@ git-commit - Record changes to the repository
SYNOPSIS
--------
[verse]
-'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend]
+'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
[(-c | -C) <commit>] [-F <file> | -m <msg>]
[--allow-empty] [--no-verify] [-e] [--author=<author>]
[--cleanup=<mode>] [--] [[-i | -o ]<file>...]
@@ -42,10 +42,9 @@ The content to be added can be specified in several ways:
by one which files should be part of the commit, before finalizing the
operation. Currently, this is done by invoking 'git-add --interactive'.
-The 'git-status' command can be used to obtain a
+The `--dry-run` option can be used to obtain a
summary of what is included by any of the above for the next
-commit by giving the same set of parameters you would give to
-this command.
+commit by giving the same set of parameters (options and paths).
If you make a commit and then find a mistake immediately after
that, you can recover from it with 'git-reset'.
@@ -198,6 +197,11 @@ specified.
--quiet::
Suppress commit summary message.
+--dry-run::
+ Do not create a commit, but show a list of paths that are
+ to be committed, paths with local changes that will be left
+ uncommitted and paths that are untracked.
+
\--::
Do not interpret any more arguments as options.
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index af2328d40..75b06f33e 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -82,6 +82,14 @@ marks the same across runs.
allow that. So fake a tagger to be able to fast-import the
output.
+--no-data::
+ Skip output of blob objects and instead refer to blobs via
+ their original SHA-1 hash. This is useful when rewriting the
+ directory structure or history of a repository without
+ touching the contents of individual files. Note that the
+ resulting stream can only be used by a repository which
+ already contains the necessary objects.
+
[git-rev-list-args...]::
A list of arguments, acceptable to 'git-rev-parse' and
'git-rev-list', that specifies the specific objects and references
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index b753c9d76..8c700200f 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -17,6 +17,7 @@ SYNOPSIS
[-l | --files-with-matches] [-L | --files-without-match]
[-z | --null]
[-c | --count] [--all-match]
+ [--max-depth <depth>]
[--color | --no-color]
[-A <post-context>] [-B <pre-context>] [-C <context>]
[-f <file>] [-e] <pattern>
@@ -47,6 +48,10 @@ OPTIONS
-I::
Don't match the pattern in binary files.
+--max-depth <depth>::
+ For each pathspec given on command line, descend at most <depth>
+ levels of directories. A negative value means no limit.
+
-w::
--word-regexp::
Match the pattern only at word boundary (either begin at the
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index 1fd0ff261..eba3cb499 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository
SYNOPSIS
--------
-'git init-db' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
DESCRIPTION
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 7151d12f3..f081b24d9 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one
SYNOPSIS
--------
-'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory]
OPTIONS
@@ -74,6 +74,9 @@ By default, the configuration flag receive.denyNonFastForwards is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
+If you name a (possibly non-existent) directory at the end of the command
+line, the command is run inside the directory (possibly after creating it).
+
--
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index 22da21a54..0771f2544 100644
--- a/Documentation/git-instaweb.txt
+++ b/Documentation/git-instaweb.txt
@@ -29,7 +29,7 @@ OPTIONS
The HTTP daemon command-line that will be executed.
Command-line options may be specified here, and the
configuration file will be added at the end of the command-line.
- Currently lighttpd, apache2 and webrick are supported.
+ Currently apache2, lighttpd, mongoose and webrick are supported.
(Default: lighttpd)
-m::
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 057a021eb..021066e95 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -44,7 +44,7 @@ OPTIONS
-o::
--others::
- Show other files in the output
+ Show other (i.e. untracked) files in the output
-i::
--ignored::
diff --git a/Documentation/git-mailinfo.txt b/Documentation/git-mailinfo.txt
index 8d95aaa30..996c3fcc6 100644
--- a/Documentation/git-mailinfo.txt
+++ b/Documentation/git-mailinfo.txt
@@ -8,7 +8,7 @@ git-mailinfo - Extracts patch and authorship from a single e-mail message
SYNOPSIS
--------
-'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] <msg> <patch>
+'git mailinfo' [-k] [-u | --encoding=<encoding> | -n] [--scissors] <msg> <patch>
DESCRIPTION
@@ -49,6 +49,25 @@ conversion, even with this flag.
-n::
Disable all charset re-coding of the metadata.
+--scissors::
+ Remove everything in body before a scissors line. A line that
+ mainly consists of scissors (either ">8" or "8<") and perforation
+ (dash "-") marks is called a scissors line, and is used to request
+ the reader to cut the message at that line. If such a line
+ appears in the body of the message before the patch, everything
+ before it (including the scissors line itself) is ignored when
+ this option is used.
++
+This is useful if you want to begin your message in a discussion thread
+with comments and suggestions on the message you are responding to, and to
+conclude it with a patch submission, separating the discussion and the
+beginning of the proposed commit log message with a scissors line.
++
+This can enabled by default with the configuration option mailinfo.scissors.
+
+--no-scissors::
+ Ignore scissors lines. Useful for overriding mailinfo.scissors settings.
+
<msg>::
The commit log message extracted from e-mail, usually
except the title line which comes from e-mail Subject.
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 767486c77..ce5b36998 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -8,12 +8,12 @@ git-merge-base - Find as good common ancestors as possible for a merge
SYNOPSIS
--------
-'git merge-base' [--all] <commit> <commit>...
+'git merge-base' [-a|--all] <commit> <commit>...
DESCRIPTION
-----------
-'git-merge-base' finds best common ancestor(s) between two commits to use
+'git merge-base' finds best common ancestor(s) between two commits to use
in a three-way merge. One common ancestor is 'better' than another common
ancestor if the latter is an ancestor of the former. A common ancestor
that does not have any better common ancestor is a 'best common
@@ -27,8 +27,13 @@ commits on the command line. As the most common special case, specifying only
two commits on the command line means computing the merge base between
the given two commits.
+As a consequence, the 'merge base' is not necessarily contained in each of the
+commit arguments if more than two commits are specified. This is different
+from linkgit:git-show-branch[1] when used with the `--merge-base` option.
+
OPTIONS
-------
+-a::
--all::
Output all merge bases for the commits, instead of just one.
diff --git a/Documentation/git-mv.txt b/Documentation/git-mv.txt
index 9c5660275..bdcb58526 100644
--- a/Documentation/git-mv.txt
+++ b/Documentation/git-mv.txt
@@ -28,6 +28,7 @@ committed.
OPTIONS
-------
-f::
+--force::
Force renaming or moving of a file even if the target exists
-k::
Skip move or rename actions which would lead to an error
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index b5f26cee1..abfc6b6ea 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -8,7 +8,7 @@ git-prune-packed - Remove extra objects that are already in pack files
SYNOPSIS
--------
-'git prune-packed' [-n] [-q]
+'git prune-packed' [-n|--dry-run] [-q|--quiet]
DESCRIPTION
@@ -28,10 +28,12 @@ disk storage, etc.
OPTIONS
-------
-n::
+--dry-run::
Don't actually remove any objects, only show those that would have been
removed.
-q::
+--quiet::
Squelch the progress indicator.
Author
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 58d2bd5d4..ba6a8a2fb 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -9,7 +9,7 @@ git-push - Update remote refs along with associated objects
SYNOPSIS
--------
[verse]
-'git push' [--all | --mirror | --tags] [--dry-run] [--receive-pack=<git-receive-pack>]
+'git push' [--all | --mirror | --tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
[--repo=<repository>] [-f | --force] [-v | --verbose]
[<repository> <refspec>...]
@@ -82,6 +82,7 @@ nor in any Push line of the corresponding remotes file---see below).
if the configuration option `remote.<remote>.mirror` is
set.
+-n::
--dry-run::
Do everything except actually send the updates.
diff --git a/Documentation/git-quiltimport.txt b/Documentation/git-quiltimport.txt
index d4037de51..579e8d2f3 100644
--- a/Documentation/git-quiltimport.txt
+++ b/Documentation/git-quiltimport.txt
@@ -9,7 +9,7 @@ git-quiltimport - Applies a quilt patchset onto the current branch
SYNOPSIS
--------
[verse]
-'git quiltimport' [--dry-run] [--author <author>] [--patches <dir>]
+'git quiltimport' [--dry-run | -n] [--author <author>] [--patches <dir>]
DESCRIPTION
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 7160fa153..4a932b08c 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -8,7 +8,10 @@ git-read-tree - Reads tree information into the index
SYNOPSIS
--------
-'git read-tree' (<tree-ish> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
+ [-u [--exclude-per-directory=<gitignore>] | -i]]
+ [--index-output=<file>]
+ <tree-ish1> [<tree-ish2> [<tree-ish3>]]
DESCRIPTION
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index db1b71d24..0aefc34d0 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -268,8 +268,9 @@ OPTIONS
exit with the message "Current branch is up to date" in such a
situation.
+--ignore-whitespace::
--whitespace=<option>::
- This flag is passed to the 'git-apply' program
+ These flag are passed to the 'git-apply' program
(see linkgit:git-apply[1]) that applies the patch.
Incompatible with the --interactive option.
diff --git a/Documentation/git-remote-helpers.txt b/Documentation/git-remote-helpers.txt
new file mode 100644
index 000000000..173ee232f
--- /dev/null
+++ b/Documentation/git-remote-helpers.txt
@@ -0,0 +1,71 @@
+git-remote-helpers(1)
+=====================
+
+NAME
+----
+git-remote-helpers - Helper programs for interoperation with remote git
+
+SYNOPSIS
+--------
+'git remote-<transport>' <remote>
+
+DESCRIPTION
+-----------
+
+These programs are normally not used directly by end users, but are
+invoked by various git programs that interact with remote repositories
+when the repository they would operate on will be accessed using
+transport code not linked into the main git binary. Various particular
+helper programs will behave as documented here.
+
+COMMANDS
+--------
+
+Commands are given by the caller on the helper's standard input, one per line.
+
+'capabilities'::
+ Lists the capabilities of the helper, one per line, ending
+ with a blank line.
+
+'list'::
+ Lists the refs, one per line, in the format "<value> <name>
+ [<attr> ...]". The value may be a hex sha1 hash, "@<dest>" for
+ a symref, or "?" to indicate that the helper could not get the
+ value of the ref. A space-separated list of attributes follows
+ the name; unrecognized attributes are ignored. After the
+ complete list, outputs a blank line.
+
+'fetch' <sha1> <name>::
+ Fetches the given object, writing the necessary objects to the
+ database. Outputs a blank line when the fetch is
+ complete. Only objects which were reported in the ref list
+ with a sha1 may be fetched this way.
++
+Supported if the helper has the "fetch" capability.
+
+If a fatal error occurs, the program writes the error message to
+stderr and exits. The caller should expect that a suitable error
+message has been printed if the child closes the connection without
+completing a valid response for the current command.
+
+Additional commands may be supported, as may be determined from
+capabilities reported by the helper.
+
+CAPABILITIES
+------------
+
+'fetch'::
+ This helper supports the 'fetch' command.
+
+REF LIST ATTRIBUTES
+-------------------
+
+None are defined yet, but the caller must accept any which are supplied.
+
+Documentation
+-------------
+Documentation by Daniel Barkalow.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
new file mode 100644
index 000000000..915cb77b2
--- /dev/null
+++ b/Documentation/git-replace.txt
@@ -0,0 +1,71 @@
+git-replace(1)
+==============
+
+NAME
+----
+git-replace - Create, list, delete refs to replace objects
+
+SYNOPSIS
+--------
+[verse]
+'git replace' [-f] <object> <replacement>
+'git replace' -d <object>...
+'git replace' -l [<pattern>]
+
+DESCRIPTION
+-----------
+Adds a 'replace' reference in `.git/refs/replace/`
+
+The name of the 'replace' reference is the SHA1 of the object that is
+replaced. The content of the replace reference is the SHA1 of the
+replacement object.
+
+Unless `-f` is given, the replace reference must not yet exist in
+`.git/refs/replace/` directory.
+
+OPTIONS
+-------
+-f::
+ If an existing replace ref for the same object exists, it will
+ be overwritten (instead of failing).
+
+-d::
+ Delete existing replace refs for the given objects.
+
+-l <pattern>::
+ List replace refs for objects that match the given pattern (or
+ all if no pattern is given).
+ Typing "git replace" without arguments, also lists all replace
+ refs.
+
+BUGS
+----
+Comparing blobs or trees that have been replaced with those that
+replace them will not work properly. And using 'git reset --hard' to
+go back to a replaced commit will move the branch to the replacement
+commit instead of the replaced commit.
+
+There may be other problems when using 'git rev-list' related to
+pending objects. And of course things may break if an object of one
+type is replaced by an object of another type (for example a blob
+replaced by a commit).
+
+SEE ALSO
+--------
+linkgit:git-tag[1]
+linkgit:git-branch[1]
+
+Author
+------
+Written by Christian Couder <chriscool@tuxfamily.org> and Junio C
+Hamano <gitster@pobox.com>, based on 'git tag' by Kristian Hogsberg
+<krh@redhat.com> and Carlos Rica <jasampler@gmail.com>.
+
+Documentation
+--------------
+Documentation by Christian Couder <chriscool@tuxfamily.org> and the
+git-list <git@vger.kernel.org>, based on 'git tag' documentation.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index abb25d1c0..469cf6dba 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
+'git reset' --patch [<commit>] [--] [<paths>...]
DESCRIPTION
-----------
@@ -23,8 +24,9 @@ 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 form with 'paths' is used to revert selected paths in
-the index from a given commit, without moving HEAD.
+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.
OPTIONS
@@ -50,6 +52,15 @@ OPTIONS
and updates the files that are different between the named commit
and the current commit in the working tree.
+-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]).
+
-q::
Be quiet, only report errors.
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index 974d9f527..3341d1b62 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -51,20 +51,26 @@ SYNOPSIS
DESCRIPTION
-----------
-Lists commit objects in reverse chronological order starting at the
-given commit(s), taking ancestry relationship into account. This is
-useful to produce human-readable log output.
+List commits that are reachable by following the `parent` links from the
+given commit(s), but exclude commits that are reachable from the one(s)
+given with a '{caret}' in front of them. The output is given in reverse
+chronological order by default.
-Commits which are stated with a preceding '{caret}' cause listing to
-stop at that point. Their parents are implied. Thus the following
-command:
+You can think of this as a set operation. Commits given on the command
+line form a set of commits that are reachable from any of them, and then
+commits reachable from any of the ones given with '{caret}' in front are
+subtracted from that set. The remaining commits are what comes out in the
+command's output. Various other options and paths parameters can be used
+to further limit the result.
+
+Thus, the following command:
-----------------------------------------------------------------------
$ git rev-list foo bar ^baz
-----------------------------------------------------------------------
-means "list all the commits which are included in 'foo' and 'bar', but
-not in 'baz'".
+means "list all the commits which are reachable from 'foo' or 'bar', but
+not from 'baz'".
A special notation "'<commit1>'..'<commit2>'" can be used as a
short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index d6b192b7b..767cf4d4b 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -142,8 +142,9 @@ user is prompted for a password while the input is masked for privacy.
--smtp-server-port=<port>::
Specifies a port different from the default port (SMTP
- servers typically listen to smtp port 25 and ssmtp port
- 465); symbolic port names (e.g. "submission" instead of 465)
+ servers typically listen to smtp port 25, but may also listen to
+ submission port 587, or the common SSL smtp port 465);
+ symbolic port names (e.g. "submission" instead of 587)
are also accepted. The port can also be set with the
'sendemail.smtpserverport' configuration variable.
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 89ec5364e..734336119 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -8,11 +8,12 @@ git-show-branch - Show branches and their commits
SYNOPSIS
--------
[verse]
-'git show-branch' [--all] [--remotes] [--topo-order | --date-order]
- [--current] [--color | --no-color]
+'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order]
+ [--current] [--color | --no-color] [--sparse]
[--more=<n> | --list | --independent | --merge-base]
[--no-name | --sha1-name] [--topics]
[<rev> | <glob>]...
+
'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
DESCRIPTION
@@ -81,9 +82,11 @@ OPTIONS
Synonym to `--more=-1`
--merge-base::
- Instead of showing the commit list, just act like the
- 'git-merge-base -a' command, except that it can accept
- more than two heads.
+ Instead of showing the commit list, determine possible
+ merge bases for the specified commits. All merge bases
+ will be contained in all specified commits. This is
+ different from how linkgit:git-merge-base[1] handles
+ the case of three or more commits.
--independent::
Among the <reference>s given, display only the ones that
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 1c64a02fe..3f14b727b 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -13,7 +13,7 @@ SYNOPSIS
'git stash' drop [-q|--quiet] [<stash>]
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
'git stash' branch <branchname> [<stash>]
-'git stash' [save [--keep-index] [-q|--quiet] [<message>]]
+'git stash' [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
'git stash' clear
'git stash' create
@@ -42,15 +42,27 @@ is also possible).
OPTIONS
-------
-save [--keep-index] [-q|--quiet] [<message>]::
+save [--patch] [--[no-]keep-index] [-q|--quiet] [<message>]::
Save your local modifications to a new 'stash', and run `git reset
- --hard` to revert them. This is the default action when no
- subcommand is given. The <message> part is optional and gives
- the description along with the stashed state.
+ --hard` to revert them. The <message> part is optional and gives
+ the description along with the stashed state. For quickly making
+ a snapshot, you can omit _both_ "save" and <message>, but giving
+ only <message> does not trigger this action to prevent a misspelled
+ subcommand from making an unwanted stash.
+
If the `--keep-index` option is used, all changes already added to the
index are left intact.
++
+With `--patch`, you can interactively select hunks from in the diff
+between HEAD and the working tree to be stashed. The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively. The selected changes are then rolled back
+from your worktree.
++
+The `--patch` option implies `--keep-index`. You can use
+`--no-keep-index` to override this.
list [<options>]::
@@ -114,7 +126,8 @@ no conflicts.
clear::
Remove all the stashed states. Note that those states will then
- be subject to pruning, and may be difficult or impossible to recover.
+ be subject to pruning, and may be impossible to recover (see
+ 'Examples' below for a possible strategy).
drop [-q|--quiet] [<stash>]::
@@ -217,6 +230,20 @@ $ edit/build/test remaining parts
$ git commit foo -m 'Remaining parts'
----------------------------------------------------------------
+Recovering stashes that were cleared/dropped erroneously::
+
+If you mistakenly drop or clear stashes, they cannot be recovered
+through the normal safety mechanisms. However, you can try the
+following incantation to get a list of stashes that are still in your
+repository, but not reachable any more:
++
+----------------------------------------------------------------
+git fsck --unreachable |
+grep commit | cut -d\ -f3 |
+xargs git log --merges --no-walk --grep=WIP
+----------------------------------------------------------------
+
+
SEE ALSO
--------
linkgit:git-checkout[1],
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 7dd73ae14..5ccdd18c8 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -11,12 +11,12 @@ SYNOPSIS
[verse]
'git submodule' [--quiet] add [-b branch]
[--reference <repository>] [--] <repository> <path>
-'git submodule' [--quiet] status [--cached] [--] [<path>...]
+'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
- [--reference <repository>] [--merge] [--] [<path>...]
-'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
-'git submodule' [--quiet] foreach <command>
+ [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach [--recursive] <command>
'git submodule' [--quiet] sync [--] [<path>...]
@@ -100,6 +100,9 @@ status::
initialized and `+` if the currently checked out submodule commit
does not match the SHA-1 found in the index of the containing
repository. This command is the default command for 'git-submodule'.
++
+If '--recursive' is specified, this command will recurse into nested
+submodules, and show their status as well.
init::
Initialize the submodules, i.e. register each submodule name
@@ -122,21 +125,31 @@ update::
If the submodule is not yet initialized, and you just want to use the
setting as stored in .gitmodules, you can automatically initialize the
submodule with the --init option.
++
+If '--recursive' is specified, this command will recurse into the
+registered submodules, and update any nested submodules within.
summary::
Show commit summary between the given commit (defaults to HEAD) and
working tree/index. For a submodule in question, a series of commits
in the submodule between the given super project commit and the
- index or working tree (switched by --cached) are shown.
+ index or working tree (switched by --cached) are shown. If the option
+ --files is given, show the series of commits in the submodule between
+ the index of the super project and the working tree of the submodule
+ (this option doesn't allow to use the --cached option or to provide an
+ explicit commit).
foreach::
Evaluates an arbitrary shell command in each checked out submodule.
- The command has access to the variables $path and $sha1:
+ The command has access to the variables $name, $path and $sha1:
+ $name is the name of the relevant submodule section in .gitmodules,
$path is the name of the submodule directory relative to the
superproject, and $sha1 is the commit as recorded in the superproject.
Any submodules defined in the superproject but not checked out are
ignored by this command. Unless given --quiet, foreach prints the name
of each submodule before evaluating the command.
+ If --recursive is given, submodules are traversed recursively (i.e.
+ the given shell command is evaluated in nested submodules as well).
A non-zero return from the command in any submodule causes
the processing to terminate. This can be overridden by adding '|| :'
to the end of the command.
@@ -169,6 +182,11 @@ OPTIONS
commands typically use the commit found in the submodule HEAD, but
with this option, the commit stored in the index is used instead.
+--files::
+ This option is only valid for the summary command. This command
+ compares the commit in the index with that in the submodule HEAD
+ when this option is used.
+
-n::
--summary-limit::
This option is only valid for the summary command.
@@ -209,6 +227,12 @@ OPTIONS
*NOTE*: Do *not* use this option unless you have read the note
for linkgit:git-clone[1]'s --reference and --shared options carefully.
+--recursive::
+ This option is only valid for foreach, update and status commands.
+ Traverse submodules recursively. The operation is performed not
+ only in the submodules of the current repo, but also
+ in any nested submodules inside those submodules (and so on).
+
<path>...::
Paths to submodule(s). When specified this will restrict the command
to only operate on the submodules found at the specified paths.
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 22a0389f1..1812890a7 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -102,9 +102,6 @@ COMMANDS
Store Git commit times in the local timezone instead of UTC. This
makes 'git log' (even without --date=local) show the same times
that `svn log` would in the local timezone.
-
---parent;;
- Fetch only from the SVN parent of the current HEAD.
+
This doesn't interfere with interoperating with the Subversion
repository you cloned from, but if you wish for your local Git
@@ -112,6 +109,9 @@ repository to be able to interoperate with someone else's local Git
repository, either don't use this option or you should both use it in
the same local timezone.
+--parent;;
+ Fetch only from the SVN parent of the current HEAD.
+
--ignore-paths=<regex>;;
This allows one to specify a Perl regular expression that will
cause skipping of all matching paths from checkout from SVN.
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fa733214a..299b04f72 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,14 +10,15 @@ SYNOPSIS
--------
[verse]
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
- <name> [<commit> | <object>]
-'git tag' -d <name>...
+ <tagname> [<commit> | <object>]
+'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
-'git tag' -v <name>...
+'git tag' -v <tagname>...
DESCRIPTION
-----------
-Adds a 'tag' reference in `.git/refs/tags/`
+
+Adds a tag reference in `.git/refs/tags/`.
Unless `-f` is given, the tag must not yet exist in
`.git/refs/tags/` directory.
@@ -50,6 +51,7 @@ OPTIONS
Make a GPG-signed tag, using the given key
-f::
+--force::
Replace an existing tag with the given name (instead of failing)
-d::
@@ -85,6 +87,12 @@ OPTIONS
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+<tagname>::
+ The name of the tag to create, delete, or describe.
+ The new tag name must pass all checks defined by
+ linkgit:git-check-ref-format[1]. Some of these checks
+ may restrict the characters allowed in a tag name.
+
CONFIGURATION
-------------
By default, 'git-tag' in sign-with-default mode (-s) will use your
@@ -249,6 +257,10 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
------------
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1].
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>,
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index b8e49dce4..63f3b5c74 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -20,6 +20,8 @@ The UI for the protocol is on the 'git-fetch-pack' side, and the
program pair is meant to be used to pull updates from a remote
repository. For push operations, see 'git-send-pack'.
+After finishing the operation successfully, `post-upload-pack`
+hook is called (see linkgit:githooks[5]).
OPTIONS
-------
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index c8611632d..97f7f9165 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files
SYNOPSIS
--------
-'git verify-pack' [-v] [--] <pack>.idx ...
+'git verify-pack' [-v|--verbose] [--] <pack>.idx ...
DESCRIPTION
@@ -23,8 +23,15 @@ OPTIONS
The idx files to verify.
-v::
+--verbose::
After verifying the pack, show list of objects contained
- in the pack.
+ in the pack and a histogram of delta chain length.
+
+-s::
+--stat-only::
+ Do not verify the pack contents; only show the histogram of delta
+ chain length. With `--verbose`, list of objects is also shown.
+
\--::
Do not interpret any more arguments as options.
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index acc408d7e..06e0f315c 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -245,7 +245,7 @@ Both standard output and standard error output are forwarded to
for the user.
The default 'update' hook, when enabled--and with
-`hooks.allowunannotated` config option turned on--prevents
+`hooks.allowunannotated` config option unset or set to false--prevents
unannotated tags to be pushed.
[[post-receive]]
@@ -310,6 +310,35 @@ Both standard output and standard error output are forwarded to
'git-send-pack' on the other end, so you can simply `echo` messages
for the user.
+post-upload-pack
+----------------
+
+After upload-pack successfully finishes its operation, this hook is called
+for logging purposes.
+
+The hook is passed various pieces of information, one per line, from its
+standard input. Currently the following items can be fed to the hook, but
+more types of information may be added in the future:
+
+want SHA-1::
+ 40-byte hexadecimal object name the client asked to include in the
+ resulting pack. Can occur one or more times in the input.
+
+have SHA-1::
+ 40-byte hexadecimal object name the client asked to exclude from
+ the resulting pack, claiming to have them already. Can occur zero
+ or more times in the input.
+
+time float::
+ Number of seconds spent for creating the packfile.
+
+size decimal::
+ Size of the resulting packfile in bytes.
+
+kind string:
+ Either "clone" (when the client did not give us any "have", and asked
+ for all our refs with "want"), or "fetch" (otherwise).
+
pre-auto-gc
~~~~~~~~~~~
diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt
new file mode 100644
index 000000000..81e7ad7df
--- /dev/null
+++ b/Documentation/pt_BR/gittutorial.txt
@@ -0,0 +1,675 @@
+gittutorial(7)
+==============
+
+NAME
+----
+gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova)
+
+SYNOPSIS
+--------
+git *
+
+DESCRIPTION
+-----------
+
+Este tutorial explica como importar um novo projeto para o git,
+adicionar mudanças a ele, e compartilhar mudanças com outros
+desenvolvedores.
+
+Se, ao invés disso, você está interessado primariamente em usar git para
+obter um projeto, por exemplo, para testar a última versão, você pode
+preferir começar com os primeiros dois capítulos de
+link:user-manual.html[O Manual do Usuário Git].
+
+Primeiro, note que você pode obter documentação para um comando como
+`git log --graph` com:
+
+------------------------------------------------
+$ man git-log
+------------------------------------------------
+
+ou:
+
+------------------------------------------------
+$ git help log
+------------------------------------------------
+
+Com a última forma, você pode usar o visualizador de manual de sua
+escolha; veja linkgit:git-help[1] para maior informação.
+
+É uma boa idéia informar ao git seu nome e endereço público de email
+antes de fazer qualquer operação. A maneira mais fácil de fazê-lo é:
+
+------------------------------------------------
+$ git config --global user.name "Seu Nome Vem Aqui"
+$ git config --global user.email voce@seudominio.exemplo.com
+------------------------------------------------
+
+
+Importando um novo projeto
+-----------------------
+
+Assuma que você tem um tarball project.tar.gz com seu trabalho inicial.
+Você pode colocá-lo sob controle de revisão git da seguinte forma:
+
+------------------------------------------------
+$ tar xzf project.tar.gz
+$ cd project
+$ git init
+------------------------------------------------
+
+Git irá responder
+
+------------------------------------------------
+Initialized empty Git repository in .git/
+------------------------------------------------
+
+Você agora iniciou seu diretório de trabalho--você deve ter notado um
+novo diretório criado, com o nome de ".git".
+
+A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os
+arquivos sob o diretório corrente (note o '.'), com 'git-add':
+
+------------------------------------------------
+$ git add .
+------------------------------------------------
+
+Este instantâneo está agora armazenado em uma área temporária que o git
+chama de "index" ou índice. Você pode armazenar permanentemente o
+conteúdo do índice no repositório com 'git-commit':
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+Isto vai te pedir por uma mensagem de commit. Você agora gravou sua
+primeira versão de seu projeto no git.
+
+Fazendo mudanças
+--------------
+
+Modifique alguns arquivos, e, então, adicione seu conteúdo atualizado ao
+índice:
+
+------------------------------------------------
+$ git add file1 file2 file3
+------------------------------------------------
+
+Você está agora pronto para fazer o commit. Você pode ver o que está
+para ser gravado usando 'git-diff' com a opção --cached:
+
+------------------------------------------------
+$ git diff --cached
+------------------------------------------------
+
+(Sem --cached, o comando 'git-diff' irá te mostrar quaisquer mudanças
+que você tenha feito mas ainda não adicionou ao índice.) Você também
+pode obter um breve sumário da situação com 'git-status':
+
+------------------------------------------------
+$ git status
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: file1
+# modified: file2
+# modified: file3
+#
+------------------------------------------------
+
+Se você precisar fazer qualquer outro ajuste, faça-o agora, e, então,
+adicione qualquer conteúdo modificado ao índice. Finalmente, grave suas
+mudanças com:
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+Isto irá novamente te pedir por uma mensagem descrevendo a mudança, e,
+então, gravar a nova versão do projeto.
+
+Alternativamente, ao invés de executar 'git-add' antes, você pode usar
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
+
+o que irá automaticamente notar quaisquer arquivos modificados (mas não
+novos), adicioná-los ao índices, e gravar, tudo em um único passo.
+
+Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa
+idéia começar a mensagem com uma simples e curta (menos de 50
+caracteres) linha sumarizando a mudança, seguida de uma linha em branco
+e, então, uma descrição mais detalhada. Ferramentas que transformam
+commits em email, por exemplo, usam a primeira linha no campo de
+cabeçalho Subject: e o resto no corpo.
+
+Git rastreia conteúdo, não arquivos
+----------------------------
+
+Muitos sistemas de controle de revisão provêem um comando `add` que diz
+ao sistema para começar a rastrear mudanças em um novo arquivo. O
+comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é
+usado tanto para arquivos novos e arquivos recentemente modificados, e
+em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena
+o conteúdo no índice, pronto para inclusão do próximo commit.
+
+Visualizando história do projeto
+-----------------------
+
+Em qualquer ponto você pode visualizar a história das suas mudanças
+usando
+
+------------------------------------------------
+$ git log
+------------------------------------------------
+
+Se você também quer ver a diferença completa a cada passo, use
+
+------------------------------------------------
+$ git log -p
+------------------------------------------------
+
+Geralmente, uma visão geral da mudança é útil para ter a sensação de
+cada passo
+
+------------------------------------------------
+$ git log --stat --summary
+------------------------------------------------
+
+Gerenciando "branches"/ramos
+-----------------
+
+Um simples repositório git pode manter múltiplos ramos de
+desenvolvimento. Para criar um novo ramo chamado "experimental", use
+
+------------------------------------------------
+$ git branch experimental
+------------------------------------------------
+
+Se você executar agora
+
+------------------------------------------------
+$ git branch
+------------------------------------------------
+
+você vai obter uma lista de todos os ramos existentes:
+
+------------------------------------------------
+ experimental
+* master
+------------------------------------------------
+
+O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o
+ramo padrão que foi criado pra você automaticamente. O asterisco marca
+o ramo em que você está atualmente; digite
+
+------------------------------------------------
+$ git checkout experimental
+------------------------------------------------
+
+para mudar para o ramo experimental. Agora edite um arquivo, grave a
+mudança, e mude de volta para o ramo master:
+
+------------------------------------------------
+(edita arquivo)
+$ git commit -a
+$ git checkout master
+------------------------------------------------
+
+Verifique que a mudança que você fez não está mais visível, já que ela
+foi feita no ramo experimental e você está de volta ao ramo master.
+
+Você pode fazer uma mudança diferente no ramo master:
+
+------------------------------------------------
+(edit file)
+$ git commit -a
+------------------------------------------------
+
+neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em
+cada um. Para unificar as mudanças feitas no experimental para o
+master, execute
+
+------------------------------------------------
+$ git merge experimental
+------------------------------------------------
+
+Se as mudanças não conflitarem, estará pronto. Se existirem conflitos,
+marcadores serão deixados nos arquivos problemáticos exibindo o
+conflito;
+
+------------------------------------------------
+$ git diff
+------------------------------------------------
+
+vai exibir isto. Após você editar os arquivos para resolver os
+conflitos,
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
+
+irá gravar o resultado da unificação. Finalmente,
+
+------------------------------------------------
+$ gitk
+------------------------------------------------
+
+vai mostrar uma bela representação gráfica da história resultante.
+
+Neste ponto você pode remover seu ramo experimental com
+
+------------------------------------------------
+$ git branch -d experimental
+------------------------------------------------
+
+Este comando garante que as mudanças no ramo experimental já estão no
+ramo atual.
+
+Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode
+sempre remover o ramo com
+
+-------------------------------------
+$ git branch -D ideia-louca
+-------------------------------------
+
+Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar
+alguma coisa.
+
+Usando git para colaboração
+---------------------------
+
+Suponha que Alice começou um novo projeto com um repositório git em
+/home/alice/project, e que Bob, que tem um diretório home na mesma
+máquina, quer contribuir.
+
+Bob começa com:
+
+------------------------------------------------
+bob$ git clone /home/alice/project myrepo
+------------------------------------------------
+
+Isso cria um novo diretório "myrepo" contendo um clone do repositório de
+Alice. O clone está no mesmo pé que o projeto original, possuindo sua
+própria cópia da história do projeto original.
+
+Bob então faz algumas mudanças e as grava:
+
+------------------------------------------------
+(editar arquivos)
+bob$ git commit -a
+(repetir conforme necessário)
+------------------------------------------------
+
+Quanto está pronto, ele diz a Alice para puxar as mudanças do
+repositório em /home/bob/myrepo. Ela o faz com:
+
+------------------------------------------------
+alice$ cd /home/alice/project
+alice$ git pull /home/bob/myrepo master
+------------------------------------------------
+
+Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice.
+Se Alice fez suas próprias mudanças no intervalo, ela, então, pode
+precisar corrigir manualmente quaisquer conflitos. (Note que o argumento
+"master" no comando acima é, de fato, desnecessário, já que é o padrão.)
+
+O comando "pull" executa, então, duas operações: ele obtém mudanças de
+um ramo remoto, e, então, as unifica no ramo atual.
+
+Note que, em geral, Alice gostaria que suas mudanças locais fossem
+gravadas antes de iniciar este "pull". Se o trabalho de Bob conflita
+com o que Alice fez desde que suas histórias se ramificaram, Alice irá
+usar seu diretório de trabalho e o índice para resolver conflitos, e
+mudanças locais existentes irão interferir com o processo de resolução
+de conflitos (git ainda irá realizar a obtenção mas irá se recusar a
+unificar --- Alice terá que se livrar de suas mudanças locais de alguma
+forma e puxar de novo quando isso acontecer).
+
+Alice pode espiar o que Bob fez sem unificar primeiro, usando o comando
+"fetch"; isto permite Alice inspecionar o que Bob fez, usando um símbolo
+especial "FETCH_HEAD", com o fim de determinar se ele tem alguma coisa
+que vale puxar, assim:
+
+------------------------------------------------
+alice$ git fetch /home/bob/myrepo master
+alice$ git log -p HEAD..FETCH_HEAD
+------------------------------------------------
+
+Esta operação é segura mesmo se Alice tem mudanças locais não gravadas.
+A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é
+alcançável de FETCH_HEAD mas exclua tudo o que é alcançável de HEAD.
+Alice já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob
+tem em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando.
+
+Se Alice quer visualizar o que Bob fez desde que suas histórias se
+ramificaram, ela pode disparar o seguinte comando:
+
+------------------------------------------------
+$ gitk HEAD..FETCH_HEAD
+------------------------------------------------
+
+Isto usa a mesma notação de intervalo que vimos antes com 'git log'.
+
+Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela
+pode usar a forma com três pontos ao invés da forma com dois pontos:
+
+------------------------------------------------
+$ gitk HEAD...FETCH_HEAD
+------------------------------------------------
+
+Isto significa "mostre tudo que é alcançável de qualquer um deles, mas
+exclua tudo que é alcançável a partir de ambos".
+
+Por favor, note que essas notações de intervalo podem ser usadas tanto
+com gitk quanto com "git log".
+
+Após inspecionar o que Bob fez, se não há nada urgente, Alice pode
+decidir continuar trabalhando sem puxar de Bob. Se a história de Bob
+tem alguma coisa que Alice precisa imediatamente, Alice pode optar por
+separar seu trabalho em progresso primeiro, fazer um "pull", e, então,
+finalmente, retomar seu trabalho em progresso em cima da história
+resultante.
+
+Quando você está trabalhando em um pequeno grupo unido, não é incomum
+interagir com o mesmo repositório várias e várias vezes. Definindo um
+repositório remoto antes de tudo, você pode fazê-lo mais facilmente:
+
+------------------------------------------------
+alice$ git remote add bob /home/bob/myrepo
+------------------------------------------------
+
+Com isso, Alice pode executar a primeira parte da operação "pull" usando
+o comando 'git-fetch' sem unificar suas mudanças com seu próprio ramo,
+usando:
+
+-------------------------------------
+alice$ git fetch bob
+-------------------------------------
+
+Diferente da forma longa, quando Alice obteve de Bob usando um
+repositório remoto antes definido com 'git-remote', o que foi obtido é
+armazenado em um ramo remoto, neste caso `bob/master`. Então, após isso:
+
+-------------------------------------
+alice$ git log -p master..bob/master
+-------------------------------------
+
+mostra uma lista de todas as mudanças que Bob fez desde que ramificou do
+ramo master de Alice.
+
+Após examinar essas mudanças, Alice pode unificá-las em seu ramo master:
+
+-------------------------------------
+alice$ git merge bob/master
+-------------------------------------
+
+Esse `merge` pode também ser feito puxando de seu próprio ramo remoto,
+assim:
+
+-------------------------------------
+alice$ git pull . remotes/bob/master
+-------------------------------------
+
+Note que 'git pull' sempre unifica ao ramo atual, independente do que
+mais foi passado na linha de comando.
+
+Depois, Bob pode atualizar seu repositório com as últimas mudanças de
+Alice, usando
+
+-------------------------------------
+bob$ git pull
+-------------------------------------
+
+Note que ele não precisa dar o caminho do repositório de Alice; quando
+Bob clonou seu repositório, o git armazenou a localização de seu
+repositório na configuração do mesmo, e essa localização é usada
+para puxar:
+
+-------------------------------------
+bob$ git config --get remote.origin.url
+/home/alice/project
+-------------------------------------
+
+(A configuração completa criada por 'git-clone' é visível usando `git
+config -l`, e a página de manual linkgit:git-config[1] explica o
+significado de cada opção.)
+
+Git também mantém uma cópia limpa do ramo master de Alice sob o nome
+"origin/master":
+
+-------------------------------------
+bob$ git branch -r
+ origin/master
+-------------------------------------
+
+Se Bob decidir depois em trabalhar em um host diferente, ele ainda pode
+executar clones e puxar usando o protocolo ssh:
+
+-------------------------------------
+bob$ git clone alice.org:/home/alice/project myrepo
+-------------------------------------
+
+Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou
+http; veja linkgit:git-pull[1] para detalhes.
+
+Git pode também ser usado em um modo parecido com CVS, com um
+repositório central para o qual vários usuários empurram modificações;
+veja linkgit:git-push[1] e linkgit:gitcvs-migration[7].
+
+Explorando história
+-----------------
+
+A história no git é representada como uma série de commits
+interrelacionados. Nós já vimos que o comando 'git-log' pode listar
+esses commits. Note que a primeira linha de cada entrada no log também
+dá o nome para o commit:
+
+-------------------------------------
+$ git log
+commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
+Author: Junio C Hamano <junkio@cox.net>
+Date: Tue May 16 17:18:22 2006 -0700
+
+ merge-base: Clarify the comments on post processing.
+-------------------------------------
+
+Nós podemos dar este nome ao 'git-show' para ver os detalhes sobre este
+commit.
+
+-------------------------------------
+$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
+-------------------------------------
+
+Mas há outras formas de se referir aos commits. Você pode usar qualquer
+parte inicial do nome que seja longo o bastante para identificar
+unicamente o commit:
+
+-------------------------------------
+$ git show c82a22c39c # os primeiros caracteres do nome são o bastante
+ # usualmente
+$ git show HEAD # a ponta do ramo atual
+$ git show experimental # a ponta do ramo "experimental"
+-------------------------------------
+
+Todo commit normalmente tem um commit "pai" que aponta para o estado
+anterior do projeto:
+
+-------------------------------------
+$ git show HEAD^ # para ver o pai de HEAD
+$ git show HEAD^^ # para ver o avô de HEAD
+$ git show HEAD~4 # para ver o trisavô de HEAD
+-------------------------------------
+
+Note que commits de unificação podem ter mais de um pai:
+
+-------------------------------------
+$ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^)
+$ git show HEAD^2 # mostra o segundo pai de HEAD
+-------------------------------------
+
+Você também pode dar aos commits nomes à sua escolha; após executar
+
+-------------------------------------
+$ git tag v2.5 1b2e1d63ff
+-------------------------------------
+
+você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende
+compartilhar esse nome com outras pessoas (por exemplo, para identificar
+uma versão de lançamento), você deveria criar um objeto "tag", e talvez
+assiná-lo; veja linkgit:git-tag[1] para detalhes.
+
+Qualquer comando git que precise conhecer um commit pode receber
+quaisquer desses nomes. Por exemplo:
+
+-------------------------------------
+$ git diff v2.5 HEAD # compara o HEAD atual com v2.5
+$ git branch stable v2.5 # inicia um novo ramo chamado "stable" baseado
+ # em v2.5
+$ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de
+ # trabalho a seu estado em HEAD^
+-------------------------------------
+
+Seja cuidadoso com o último comando: além de perder quaisquer mudanças
+em seu diretório de trabalho, ele também remove todos os commits
+posteriores desse ramo. Se esse ramo é o único ramo contendo esses
+commits, eles serão perdidos. Também, não use 'git-reset' num ramo
+publicamente visível de onde outros desenvolvedores puxam, já que vai
+forçar unificações desnecessárias para que outros desenvolvedores limpem
+a história. Se você precisa desfazer mudanças que você empurrou, use
+'git-revert' no lugar.
+
+O comando 'git-grep' pode buscar strings em qualquer versão de seu
+projeto, então
+
+-------------------------------------
+$ git grep "hello" v2.5
+-------------------------------------
+
+procura por todas as ocorrências de "hello" em v2.5.
+
+Se você deixar de fora o nome do commit, 'git-grep' irá procurar
+quaisquer dos arquivos que ele gerencia no diretório corrente. Então
+
+-------------------------------------
+$ git grep "hello"
+-------------------------------------
+
+é uma forma rápida de buscar somente os arquivos que são rastreados pelo
+git.
+
+Muitos comandos git também recebem um conjunto de commits, o que pode
+ser especificado de várias formas. Aqui estão alguns exemplos com 'git-log':
+
+-------------------------------------
+$ git log v2.5..v2.6 # commits entre v2.5 e v2.6
+$ git log v2.5.. # commits desde v2.5
+$ git log --since="2 weeks ago" # commits das últimas 2 semanas
+$ git log v2.5.. Makefile # commits desde v2.5 que modificam
+ # Makefile
+-------------------------------------
+
+Você também pode dar ao 'git-log' um "intervalo" de commits onde o
+primeiro não é necessariamente um ancestral do segundo; por exemplo, se
+as pontas dos ramos "stable" e "master" divergiram de um commit
+comum algum tempo atrás, então
+
+-------------------------------------
+$ git log stable..master
+-------------------------------------
+
+irá listar os commits feitos no ramo "master" mas não no ramo
+"stable", enquanto
+
+-------------------------------------
+$ git log master..stable
+-------------------------------------
+
+irá listar a lista de commits feitos no ramo "stable" mas não no ramo
+"master".
+
+O comando 'git-log' tem uma fraqueza: ele precisa mostrar os commits em
+uma lista. Quando a história tem linhas de desenvolvimento que
+divergiram e então foram unificadas novamente, a ordem em que 'git-log'
+apresenta essas mudanças é irrelevante.
+
+A maioria dos projetos com múltiplos contribuidores (como o kernel
+Linux, ou o próprio git) tem unificações frequentes, e 'gitk' faz um
+trabalho melhor de visualizar sua história. Por exemplo,
+
+-------------------------------------
+$ gitk --since="2 weeks ago" drivers/
+-------------------------------------
+
+permite a você navegar em quaisquer commits desde as últimas duas semanas
+de commits que modificaram arquivos sob o diretório "drivers". (Nota:
+você pode ajustar as fontes do gitk segurando a tecla control enquanto
+pressiona "-" ou "+".)
+
+Finalmente, a maioria dos comandos que recebem nomes de arquivo permitirão
+também, opcionalmente, preceder qualquer nome de arquivo por um
+commit, para especificar uma versão particular do arquivo:
+
+-------------------------------------
+$ git diff v2.5:Makefile HEAD:Makefile.in
+-------------------------------------
+
+Você pode usar 'git-show' para ver tal arquivo:
+
+-------------------------------------
+$ git show v2.5:Makefile
+-------------------------------------
+
+Próximos passos
+----------
+
+Este tutorial deve ser o bastante para operar controle de revisão
+distribuído básico para seus projetos. No entanto, para entender
+plenamente a profundidade e o poder do git você precisa entender duas
+idéias simples nas quais ele se baseia:
+
+ * A base de objetos é um sistema bem elegante usado para armazenar a
+ história de seu projeto--arquivos, diretórios, e commits.
+
+ * O arquivo de índice é um cache do estado de uma árvore de diretório,
+ usado para criar commits, restaurar diretórios de trabalho, e
+ armazenar as várias árvores envolvidas em uma unificação.
+
+A parte dois deste tutorial explica a base de objetos, o arquivo de
+índice, e algumas outras coisinhas que você vai precisar pra usar o
+máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7].
+
+Se você não quiser continuar com o tutorial agora nesse momento, algumas
+outras digressões que podem ser interessantes neste ponto são:
+
+ * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem
+ séries de commits em patches para email, e vice-versa, úteis para
+ projetos como o kernel Linux que dependem fortemente de patches
+ enviados por email.
+
+ * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma
+ forma de rastrear um bug é procurando pela história para encontrar o
+ commit culpado. Git bisect pode ajudar a executar uma busca binária
+ por esse commit. Ele é inteligente o bastante para executar uma
+ busca próxima da ótima mesmo no caso de uma história complexa
+ não-linear com muitos ramos unificados.
+
+ * link:everyday.html[GIT diariamente com 20 e tantos comandos]
+
+ * linkgit:gitcvs-migration[7]: Git para usuários de CVS.
+
+VEJA TAMBÉM
+--------
+linkgit:gittutorial-2[7],
+linkgit:gitcvs-migration[7],
+linkgit:gitcore-tutorial[7],
+linkgit:gitglossary[7],
+linkgit:git-help[1],
+link:everyday.html[git diariamente],
+link:user-manual.html[O Manual do Usuário git]
+
+GIT
+---
+Parte da suite linkgit:git[1].
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 2efe7a40b..b26c28133 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -35,12 +35,32 @@ Functions
Convenience functions that encapsulate a sequence of
start_command() followed by finish_command(). The argument argv
specifies the program and its arguments. The argument opt is zero
- or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
- `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
- .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
+ or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
+ `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
+ that correspond to the members .no_stdin, .git_cmd,
+ .stdout_to_stderr, .silent_exec_failure of `struct child_process`.
The argument dir corresponds the member .dir. The argument env
corresponds to the member .env.
+The functions above do the following:
+
+. If a system call failed, errno is set and -1 is returned. A diagnostic
+ is printed.
+
+. If the program was not found, then -1 is returned and errno is set to
+ ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.
+
+. Otherwise, the program is run. If it terminates regularly, its exit
+ code is returned. No diagnistic is printed, even if the exit code is
+ non-zero.
+
+. If the program terminated due to a signal, then the return value is the
+ signal number - 128, ie. it is negative and so indicates an unusual
+ condition; a diagnostic is printed. This return value can be passed to
+ exit(2), which will report the same code to the parent process that a
+ POSIX shell's $? would report for a program that died from the signal.
+
+
`start_async`::
Run a function asynchronously. Takes a pointer to a `struct
@@ -143,6 +163,11 @@ string pointers (NULL terminated) in .env:
To specify a new initial working directory for the sub-process,
specify it in the .dir member.
+If the program cannot be found, the functions return -1 and set
+errno to ENOENT. Normally, an error message is printed, but if
+.silent_exec_failure is set to 1, no message is printed for this
+special error condition.
+
* `struct async`
diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt
index e3ddf9128..55b728632 100644
--- a/Documentation/technical/api-tree-walking.txt
+++ b/Documentation/technical/api-tree-walking.txt
@@ -1,12 +1,145 @@
tree walking API
================
-Talk about <tree-walk.h>, things like
+The tree walking API is used to traverse and inspect trees.
-* struct tree_desc
-* init_tree_desc
-* tree_entry_extract
-* update_tree_entry
-* get_tree_entry
+Data Structures
+---------------
-(JC, Linus)
+`struct name_entry`::
+
+ An entry in a tree. Each entry has a sha1 identifier, pathname, and
+ mode.
+
+`struct tree_desc`::
+
+ A semi-opaque data structure used to maintain the current state of the
+ walk.
++
+* `buffer` is a pointer into the memory representation of the tree. It always
+points at the current entry being visited.
+
+* `size` counts the number of bytes left in the `buffer`.
+
+* `entry` points to the current entry being visited.
+
+`struct traverse_info`::
+
+ A structure used to maintain the state of a traversal.
++
+* `prev` points to the traverse_info which was used to descend into the
+current tree. If this is the top-level tree `prev` will point to
+a dummy traverse_info.
+
+* `name` is the entry for the current tree (if the tree is a subtree).
+
+* `pathlen` is the length of the full path for the current tree.
+
+* `conflicts` can be used by callbacks to maintain directory-file conflicts.
+
+* `fn` is a callback called for each entry in the tree. See Traversing for more
+information.
+
+* `data` can be anything the `fn` callback would want to use.
+
+Initializing
+------------
+
+`init_tree_desc`::
+
+ Initialize a `tree_desc` and decode its first entry. The buffer and
+ size parameters are assumed to be the same as the buffer and size
+ members of `struct tree`.
+
+`fill_tree_descriptor`::
+
+ Initialize a `tree_desc` and decode its first entry given the sha1 of
+ a tree. Returns the `buffer` member if the sha1 is a valid tree
+ identifier and NULL otherwise.
+
+`setup_traverse_info`::
+
+ Initialize a `traverse_info` given the pathname of the tree to start
+ traversing from. The `base` argument is assumed to be the `path`
+ member of the `name_entry` being recursed into unless the tree is a
+ top-level tree in which case the empty string ("") is used.
+
+Walking
+-------
+
+`tree_entry`::
+
+ Visit the next entry in a tree. Returns 1 when there are more entries
+ left to visit and 0 when all entries have been visited. This is
+ commonly used in the test of a while loop.
+
+`tree_entry_len`::
+
+ Calculate the length of a tree entry's pathname. This utilizes the
+ memory structure of a tree entry to avoid the overhead of using a
+ generic strlen().
+
+`update_tree_entry`::
+
+ Walk to the next entry in a tree. This is commonly used in conjunction
+ with `tree_entry_extract` to inspect the current entry.
+
+`tree_entry_extract`::
+
+ Decode the entry currently being visited (the one pointed to by
+ `tree_desc's` `entry` member) and return the sha1 of the entry. The
+ `pathp` and `modep` arguments are set to the entry's pathname and mode
+ respectively.
+
+`get_tree_entry`::
+
+ Find an entry in a tree given a pathname and the sha1 of a tree to
+ search. Returns 0 if the entry is found and -1 otherwise. The third
+ and fourth parameters are set to the entry's sha1 and mode
+ respectively.
+
+Traversing
+----------
+
+`traverse_trees`::
+
+ Traverse `n` number of trees in parallel. The `fn` callback member of
+ `traverse_info` is called once for each tree entry.
+
+`traverse_callback_t`::
+ The arguments passed to the traverse callback are as follows:
++
+* `n` counts the number of trees being traversed.
+
+* `mask` has its nth bit set if something exists in the nth entry.
+
+* `dirmask` has its nth bit set if the nth tree's entry is a directory.
+
+* `entry` is an array of size `n` where the nth entry is from the nth tree.
+
+* `info` maintains the state of the traversal.
+
++
+Returning a negative value will terminate the traversal. Otherwise the
+return value is treated as an update mask. If the nth bit is set the nth tree
+will be updated and if the bit is not set the nth tree entry will be the
+same in the next callback invocation.
+
+`make_traverse_path`::
+
+ Generate the full pathname of a tree entry based from the root of the
+ traversal. For example, if the traversal has recursed into another
+ tree named "bar" the pathname of an entry "baz" in the "bar"
+ tree would be "bar/baz".
+
+`traverse_path_len`::
+
+ Calculate the length of a pathname returned by `make_traverse_path`.
+ This utilizes the memory structure of a tree entry to avoid the
+ overhead of using a generic strlen().
+
+Authors
+-------
+
+Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds
+<torvalds@linux-foundation.org>
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 5355ebc0f..d813ceb72 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -67,3 +67,21 @@ For example, with this:
a URL like "work:repo.git" or like "host.xz:/path/to/repo.git" will be
rewritten in any context that takes a URL to be "git://git.host.xz/repo.git".
+If you want to rewrite URLs for push only, you can create a
+configuration section of the form:
+
+------------
+ [url "<actual url base>"]
+ pushInsteadOf = <other url base>
+------------
+
+For example, with this:
+
+------------
+ [url "ssh://example.org/"]
+ pushInsteadOf = git://example.org/
+------------
+
+a URL like "git://example.org/path/to/repo.git" will be rewritten to
+"ssh://example.org/path/to/repo.git" for pushes, but pulls will still
+use the original URL.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 68b95733c..d7d9a9a06 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.4.4
+DEF_VER=v1.6.4.GIT
LF='
'
diff --git a/INSTALL b/INSTALL
index ae7f7508f..be504c95e 100644
--- a/INSTALL
+++ b/INSTALL
@@ -13,6 +13,10 @@ that uses $prefix, the built results have some paths encoded,
which are derived from $prefix, so "make all; make prefix=/usr
install" would not work.
+The beginning of the Makefile documents many variables that affect the way
+git is built. You can override them either from the command line, or in a
+config.mak file.
+
Alternatively you can use autoconf generated ./configure script to
set up install paths (via config.mak.autogen), so you can write instead
@@ -48,32 +52,42 @@ Issues of note:
export GIT_EXEC_PATH PATH GITPERLLIB
- Git is reasonably self-sufficient, but does depend on a few external
- programs and libraries:
+ programs and libraries. Git can be used without most of them by adding
+ the approriate "NO_<LIBRARY>=YesPlease" to the make command line or
+ config.mak file.
- "zlib", the compression library. Git won't build without it.
- - "openssl". Unless you specify otherwise, you'll get the SHA1
- library from here.
+ - "ssh" is used to push and pull over the net.
- If you don't have openssl, you can use one of the SHA1 libraries
- that come with git (git includes the one from Mozilla, and has
- its own PowerPC and ARM optimized ones too - see the Makefile).
+ - A POSIX-compliant shell is required to run many scripts needed
+ for everyday use (e.g. "bisect", "pull").
- - libcurl library; git-http-fetch and git-fetch use them. You
- might also want the "curl" executable for debugging purposes.
- If you do not use http transfer, you are probably OK if you
- do not have them.
+ - "Perl" is needed to use some of the features (e.g. preparing a
+ partial commit using "git add -i/-p", interacting with svn
+ repositories with "git svn"). If you can live without these, use
+ NO_PERL.
- - expat library; git-http-push uses it for remote lock
- management over DAV. Similar to "curl" above, this is optional.
+ - "openssl" library is used by git-imap-send to use IMAP over SSL.
+ If you don't need it, use NO_OPENSSL.
- - "wish", the Tcl/Tk windowing shell is used in gitk to show the
- history graphically, and in git-gui.
+ By default, git uses OpenSSL for SHA1 but it will use it's own
+ library (inspired by Mozilla's) with either NO_OPENSSL or
+ BLK_SHA1. Also included is a version optimized for PowerPC
+ (PPC_SHA1).
+
+ - "libcurl" library is used by git-http-fetch and git-fetch. You
+ might also want the "curl" executable for debugging purposes.
+ If you do not use http:// or https:// repositories, you do not
+ have to have them (use NO_CURL).
- - "ssh" is used to push and pull over the net
+ - "expat" library; git-http-push uses it for remote lock
+ management over DAV. Similar to "curl" above, this is optional
+ (with NO_EXPAT).
- - "perl" and POSIX-compliant shells are needed to use most of
- the bare-bones Porcelainish scripts.
+ - "wish", the Tcl/Tk windowing shell is used in gitk to show the
+ history graphically, and in git-gui. If you don't want gitk or
+ git-gui, you can use NO_TCLTK.
- Some platform specific issues are dealt with Makefile rules,
but depending on your specific installation, you may not
diff --git a/Makefile b/Makefile
index daf429670..d4958b832 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ all::
# when attempting to read from an fopen'ed directory.
#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
#
# Define NO_CURL if you do not have libcurl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
@@ -84,18 +84,16 @@ all::
# specify your own (or DarwinPort's) include directories and
# library directories by defining CFLAGS and LDFLAGS appropriately.
#
+# Define BLK_SHA1 environment variable if you want the C version
+# of the SHA1 that assumes you can do unaligned 32-bit loads and
+# have a fast htonl() function.
+#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
+# Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
#
-# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
#
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
#
@@ -361,7 +359,6 @@ PROGRAMS += git-patch-id$X
PROGRAMS += git-shell$X
PROGRAMS += git-show-index$X
PROGRAMS += git-unpack-file$X
-PROGRAMS += git-update-server-info$X
PROGRAMS += git-upload-pack$X
PROGRAMS += git-var$X
@@ -383,7 +380,8 @@ BUILT_INS += git-stage$X
BUILT_INS += git-status$X
BUILT_INS += git-whatchanged$X
-# what 'all' will build and 'install' will install, in gitexecdir
+# what 'all' will build and 'install' will install in gitexecdir,
+# excluding programs for built-in commands
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
# what 'all' will build but not install in gitexecdir
@@ -402,6 +400,7 @@ export PERL_PATH
LIB_FILE=libgit.a
XDIFF_LIB=xdiff/lib.a
+LIB_H += advice.h
LIB_H += archive.h
LIB_H += attr.h
LIB_H += blob.h
@@ -459,6 +458,7 @@ LIB_H += utf8.h
LIB_H += wt-status.h
LIB_OBJS += abspath.o
+LIB_OBJS += advice.o
LIB_OBJS += alias.o
LIB_OBJS += alloc.o
LIB_OBJS += archive.o
@@ -532,6 +532,7 @@ LIB_OBJS += read-cache.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += remote.o
+LIB_OBJS += replace_object.o
LIB_OBJS += rerere.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
@@ -549,6 +550,7 @@ LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
LIB_OBJS += trace.o
LIB_OBJS += transport.o
+LIB_OBJS += transport-helper.o
LIB_OBJS += tree-diff.o
LIB_OBJS += tree.o
LIB_OBJS += tree-walk.o
@@ -621,6 +623,7 @@ BUILTIN_OBJS += builtin-read-tree.o
BUILTIN_OBJS += builtin-receive-pack.o
BUILTIN_OBJS += builtin-reflog.o
BUILTIN_OBJS += builtin-remote.o
+BUILTIN_OBJS += builtin-replace.o
BUILTIN_OBJS += builtin-rerere.o
BUILTIN_OBJS += builtin-reset.o
BUILTIN_OBJS += builtin-rev-list.o
@@ -638,6 +641,7 @@ BUILTIN_OBJS += builtin-tar-tree.o
BUILTIN_OBJS += builtin-unpack-objects.o
BUILTIN_OBJS += builtin-update-index.o
BUILTIN_OBJS += builtin-update-ref.o
+BUILTIN_OBJS += builtin-update-server-info.o
BUILTIN_OBJS += builtin-upload-archive.o
BUILTIN_OBJS += builtin-verify-pack.o
BUILTIN_OBJS += builtin-verify-tag.o
@@ -706,6 +710,7 @@ ifeq ($(uname_S),SCO_SV)
TAR = gtar
endif
ifeq ($(uname_S),Darwin)
+ NEEDS_CRYPTO_WITH_SSL = YesPlease
NEEDS_SSL_WITH_CRYPTO = YesPlease
NEEDS_LIBICONV = YesPlease
ifeq ($(shell expr "$(uname_R)" : '[15678]\.'),2)
@@ -751,9 +756,6 @@ ifeq ($(uname_S),SunOS)
NO_C99_FORMAT = YesPlease
NO_STRTOUMAX = YesPlease
endif
- ifdef NO_IPV6
- NEEDS_RESOLV = YesPlease
- endif
INSTALL = /usr/ucb/install
TAR = gtar
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
@@ -922,10 +924,6 @@ else
NO_PTHREADS = YesPlease
endif
endif
-ifneq (,$(findstring arm,$(uname_M)))
- ARM_SHA1 = YesPlease
- NO_MKSTEMPS = YesPlease
-endif
-include config.mak.autogen
-include config.mak
@@ -979,9 +977,7 @@ else
else
CURL_LIBCURL = -lcurl
endif
- BUILTIN_OBJS += builtin-http-fetch.o
- EXTLIBS += $(CURL_LIBCURL)
- LIB_OBJS += http.o http-walker.o
+ PROGRAMS += git-remote-curl$X git-http-fetch$X
curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
ifeq "$(curl_check)" "070908"
ifndef NO_EXPAT
@@ -1016,9 +1012,12 @@ ifndef NO_OPENSSL
else
OPENSSL_LINK =
endif
+ ifdef NEEDS_CRYPTO_WITH_SSL
+ OPENSSL_LINK += -lcrypto
+ endif
else
BASIC_CFLAGS += -DNO_OPENSSL
- MOZILLA_SHA1 = 1
+ BLK_SHA1 = 1
OPENSSL_LIBSSL =
endif
ifdef NEEDS_SSL_WITH_CRYPTO
@@ -1167,23 +1166,18 @@ ifdef NO_DEFLATE_BOUND
BASIC_CFLAGS += -DNO_DEFLATE_BOUND
endif
+ifdef BLK_SHA1
+ SHA1_HEADER = "block-sha1/sha1.h"
+ LIB_OBJS += block-sha1/sha1.o
+else
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
else
-ifdef ARM_SHA1
- SHA1_HEADER = "arm/sha1.h"
- LIB_OBJS += arm/sha1.o arm/sha1_arm.o
-else
-ifdef MOZILLA_SHA1
- SHA1_HEADER = "mozilla-sha1/sha1.h"
- LIB_OBJS += mozilla-sha1/sha1.o
-else
SHA1_HEADER = <openssl/sha.h>
EXTLIBS += $(LIB_4_CRYPTO)
endif
endif
-endif
ifdef NO_PERL_MAKEMAKER
export NO_PERL_MAKEMAKER
endif
@@ -1257,6 +1251,7 @@ ifndef V
QUIET_LINK = @echo ' ' LINK $@;
QUIET_BUILT_IN = @echo ' ' BUILTIN $@;
QUIET_GEN = @echo ' ' GEN $@;
+ QUIET_LNCP = @echo ' ' LN/CP $@;
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
$(MAKE) $(PRINT_DIR) -C $$subdir
@@ -1484,12 +1479,21 @@ git-imap-send$X: imap-send.o $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
-http.o http-walker.o http-push.o transport.o: http.h
+http.o http-walker.o http-push.o: http.h
+
+http.o http-walker.o: $(LIB_H)
+git-http-fetch$X: revision.o http.o http-walker.o http-fetch.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS) $(CURL_LIBCURL)
git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
+ $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
+
$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
$(patsubst git-%$X,%.o,$(PROGRAMS)) git.o: $(LIB_H) $(wildcard */*.h)
builtin-revert.o wt-status.o: wt-status.h
diff --git a/RelNotes b/RelNotes
index 2dad8b277..b62449d2e 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.4.4.txt \ No newline at end of file
+Documentation/RelNotes-1.6.5.txt \ No newline at end of file
diff --git a/abspath.c b/abspath.c
index 4bee0ba1e..b88122cbe 100644
--- a/abspath.c
+++ b/abspath.c
@@ -18,7 +18,7 @@ const char *make_absolute_path(const char *path)
{
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
char cwd[1024] = "";
- int buf_index = 1, len;
+ int buf_index = 1;
int depth = MAXDEPTH;
char *last_elem = NULL;
@@ -50,7 +50,7 @@ const char *make_absolute_path(const char *path)
die_errno ("Could not get current working directory");
if (last_elem) {
- int len = strlen(buf);
+ size_t len = strlen(buf);
if (len + strlen(last_elem) + 2 > PATH_MAX)
die ("Too long path name: '%s/%s'",
buf, last_elem);
@@ -61,7 +61,7 @@ const char *make_absolute_path(const char *path)
}
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
- len = readlink(buf, next_buf, PATH_MAX);
+ ssize_t len = readlink(buf, next_buf, PATH_MAX);
if (len < 0)
die_errno ("Invalid symlink '%s'", buf);
if (PATH_MAX <= len)
diff --git a/advice.c b/advice.c
new file mode 100644
index 000000000..ae4b1e81d
--- /dev/null
+++ b/advice.c
@@ -0,0 +1,27 @@
+#include "cache.h"
+
+int advice_push_nonfastforward = 1;
+int advice_status_hints = 1;
+
+static struct {
+ const char *name;
+ int *preference;
+} advice_config[] = {
+ { "pushnonfastforward", &advice_push_nonfastforward },
+ { "statushints", &advice_status_hints },
+};
+
+int git_default_advice_config(const char *var, const char *value)
+{
+ const char *k = skip_prefix(var, "advice.");
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(advice_config); i++) {
+ if (strcmp(k, advice_config[i].name))
+ continue;
+ *advice_config[i].preference = git_config_bool(var, value);
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/advice.h b/advice.h
new file mode 100644
index 000000000..e9df8e026
--- /dev/null
+++ b/advice.h
@@ -0,0 +1,9 @@
+#ifndef ADVICE_H
+#define ADVICE_H
+
+extern int advice_push_nonfastforward;
+extern int advice_status_hints;
+
+int git_default_advice_config(const char *var, const char *value);
+
+#endif /* ADVICE_H */
diff --git a/archive.c b/archive.c
index 0bca9ca40..73b8e8a56 100644
--- a/archive.c
+++ b/archive.c
@@ -283,7 +283,7 @@ static int parse_archive_args(int argc, const char **argv,
OPT_STRING(0, "format", &format, "fmt", "archive format"),
OPT_STRING(0, "prefix", &base, "prefix",
"prepend prefix to each pathname in the archive"),
- OPT_STRING(0, "output", &output, "file",
+ OPT_STRING('o', "output", &output, "file",
"write the archive to this file"),
OPT_BOOLEAN(0, "worktree-attributes", &worktree_attributes,
"read .gitattributes in working directory"),
diff --git a/arm/sha1.c b/arm/sha1.c
deleted file mode 100644
index c61ad4aff..000000000
--- a/arm/sha1.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- */
-
-#include <string.h>
-#include "sha1.h"
-
-extern void arm_sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
-
-void arm_SHA1_Init(arm_SHA_CTX *c)
-{
- c->len = 0;
- c->hash[0] = 0x67452301;
- c->hash[1] = 0xefcdab89;
- c->hash[2] = 0x98badcfe;
- c->hash[3] = 0x10325476;
- c->hash[4] = 0xc3d2e1f0;
-}
-
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n)
-{
- uint32_t workspace[80];
- unsigned int partial;
- unsigned long done;
-
- partial = c->len & 0x3f;
- c->len += n;
- if ((partial + n) >= 64) {
- if (partial) {
- done = 64 - partial;
- memcpy(c->buffer + partial, p, done);
- arm_sha_transform(c->hash, c->buffer, workspace);
- partial = 0;
- } else
- done = 0;
- while (n >= done + 64) {
- arm_sha_transform(c->hash, p + done, workspace);
- done += 64;
- }
- } else
- done = 0;
- if (n - done)
- memcpy(c->buffer + partial, p + done, n - done);
-}
-
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c)
-{
- uint64_t bitlen;
- uint32_t bitlen_hi, bitlen_lo;
- unsigned int i, offset, padlen;
- unsigned char bits[8];
- static const unsigned char padding[64] = { 0x80, };
-
- bitlen = c->len << 3;
- offset = c->len & 0x3f;
- padlen = ((offset < 56) ? 56 : (64 + 56)) - offset;
- arm_SHA1_Update(c, padding, padlen);
-
- bitlen_hi = bitlen >> 32;
- bitlen_lo = bitlen & 0xffffffff;
- bits[0] = bitlen_hi >> 24;
- bits[1] = bitlen_hi >> 16;
- bits[2] = bitlen_hi >> 8;
- bits[3] = bitlen_hi;
- bits[4] = bitlen_lo >> 24;
- bits[5] = bitlen_lo >> 16;
- bits[6] = bitlen_lo >> 8;
- bits[7] = bitlen_lo;
- arm_SHA1_Update(c, bits, 8);
-
- for (i = 0; i < 5; i++) {
- uint32_t v = c->hash[i];
- hash[0] = v >> 24;
- hash[1] = v >> 16;
- hash[2] = v >> 8;
- hash[3] = v;
- hash += 4;
- }
-}
diff --git a/arm/sha1.h b/arm/sha1.h
deleted file mode 100644
index b61b61848..000000000
--- a/arm/sha1.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- */
-
-#include <stdint.h>
-
-typedef struct {
- uint64_t len;
- uint32_t hash[5];
- unsigned char buffer[64];
-} arm_SHA_CTX;
-
-void arm_SHA1_Init(arm_SHA_CTX *c);
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n);
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c);
-
-#define git_SHA_CTX arm_SHA_CTX
-#define git_SHA1_Init arm_SHA1_Init
-#define git_SHA1_Update arm_SHA1_Update
-#define git_SHA1_Final arm_SHA1_Final
diff --git a/arm/sha1_arm.S b/arm/sha1_arm.S
deleted file mode 100644
index 41e92636e..000000000
--- a/arm/sha1_arm.S
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * SHA transform optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
- .text
- .globl arm_sha_transform
-
-/*
- * void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
- *
- * note: the "data" pointer may be unaligned.
- */
-
-arm_sha_transform:
-
- stmfd sp!, {r4 - r8, lr}
-
- @ for (i = 0; i < 16; i++)
- @ W[i] = ntohl(((uint32_t *)data)[i]);
-
-#ifdef __ARMEB__
- mov r4, r0
- mov r0, r2
- mov r2, #64
- bl memcpy
- mov r2, r0
- mov r0, r4
-#else
- mov r3, r2
- mov lr, #16
-1: ldrb r4, [r1], #1
- ldrb r5, [r1], #1
- ldrb r6, [r1], #1
- ldrb r7, [r1], #1
- subs lr, lr, #1
- orr r5, r5, r4, lsl #8
- orr r6, r6, r5, lsl #8
- orr r7, r7, r6, lsl #8
- str r7, [r3], #4
- bne 1b
-#endif
-
- @ for (i = 0; i < 64; i++)
- @ W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31);
-
- sub r3, r2, #4
- mov lr, #64
-2: ldr r4, [r3, #4]!
- subs lr, lr, #1
- ldr r5, [r3, #8]
- ldr r6, [r3, #32]
- ldr r7, [r3, #52]
- eor r4, r4, r5
- eor r4, r4, r6
- eor r4, r4, r7
- mov r4, r4, ror #31
- str r4, [r3, #64]
- bne 2b
-
- /*
- * The SHA functions are:
- *
- * f1(B,C,D) = (D ^ (B & (C ^ D)))
- * f2(B,C,D) = (B ^ C ^ D)
- * f3(B,C,D) = ((B & C) | (D & (B | C)))
- *
- * Then the sub-blocks are processed as follows:
- *
- * A' = ror(A, 27) + f(B,C,D) + E + K + *W++
- * B' = A
- * C' = ror(B, 2)
- * D' = C
- * E' = D
- *
- * We therefore unroll each loop 5 times to avoid register shuffling.
- * Also the ror for C (and also D and E which are successivelyderived
- * from it) is applied in place to cut on an additional mov insn for
- * each round.
- */
-
- .macro sha_f1, A, B, C, D, E
- ldr r3, [r2], #4
- eor ip, \C, \D
- add \E, r1, \E, ror #2
- and ip, \B, ip, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f2, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- eor ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f3, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- orr ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- and ip, ip, \D, ror #2
- add \E, \E, r3
- and r3, \B, \C, ror #2
- orr ip, ip, r3
- add \E, \E, ip
- .endm
-
- ldmia r0, {r4 - r8}
-
- mov lr, #4
- ldr r1, .L_sha_K + 0
-
- /* adjust initial values */
- mov r6, r6, ror #30
- mov r7, r7, ror #30
- mov r8, r8, ror #30
-
-3: subs lr, lr, #1
- sha_f1 r4, r5, r6, r7, r8
- sha_f1 r8, r4, r5, r6, r7
- sha_f1 r7, r8, r4, r5, r6
- sha_f1 r6, r7, r8, r4, r5
- sha_f1 r5, r6, r7, r8, r4
- bne 3b
-
- ldr r1, .L_sha_K + 4
- mov lr, #4
-
-4: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 4b
-
- ldr r1, .L_sha_K + 8
- mov lr, #4
-
-5: subs lr, lr, #1
- sha_f3 r4, r5, r6, r7, r8
- sha_f3 r8, r4, r5, r6, r7
- sha_f3 r7, r8, r4, r5, r6
- sha_f3 r6, r7, r8, r4, r5
- sha_f3 r5, r6, r7, r8, r4
- bne 5b
-
- ldr r1, .L_sha_K + 12
- mov lr, #4
-
-6: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 6b
-
- ldmia r0, {r1, r2, r3, ip, lr}
- add r4, r1, r4
- add r5, r2, r5
- add r6, r3, r6, ror #2
- add r7, ip, r7, ror #2
- add r8, lr, r8, ror #2
- stmia r0, {r4 - r8}
-
- ldmfd sp!, {r4 - r8, pc}
-
-.L_sha_K:
- .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
diff --git a/block-sha1/sha1.c b/block-sha1/sha1.c
new file mode 100644
index 000000000..d8934757a
--- /dev/null
+++ b/block-sha1/sha1.c
@@ -0,0 +1,282 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+/* this is only to get definitions for memcpy(), ntohl() and htonl() */
+#include "../git-compat-util.h"
+
+#include "sha1.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+/*
+ * Force usage of rol or ror by selecting the one with the smaller constant.
+ * It _can_ generate slightly smaller code (a constant of 1 is special), but
+ * perhaps more importantly it's possibly faster on any uarch that does a
+ * rotate with a loop.
+ */
+
+#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
+#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
+#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
+
+#else
+
+#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
+#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
+#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
+
+#endif
+
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+ #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
+#elif defined(__GNUC__) && defined(__arm__)
+ #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+ #define setW(x, val) (W(x) = (val))
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+ defined(__ppc__) || defined(__ppc64__) || \
+ defined(__powerpc__) || defined(__powerpc64__) || \
+ defined(__s390__) || defined(__s390x__)
+
+#define get_be32(p) ntohl(*(unsigned int *)(p))
+#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be32(p) ( \
+ (*((unsigned char *)(p) + 0) << 24) | \
+ (*((unsigned char *)(p) + 1) << 16) | \
+ (*((unsigned char *)(p) + 2) << 8) | \
+ (*((unsigned char *)(p) + 3) << 0) )
+#define put_be32(p, v) do { \
+ unsigned int __v = (v); \
+ *((unsigned char *)(p) + 0) = __v >> 24; \
+ *((unsigned char *)(p) + 1) = __v >> 16; \
+ *((unsigned char *)(p) + 2) = __v >> 8; \
+ *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
+
+#endif
+
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
+
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_be32(data + t)
+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+ unsigned int TEMP = input(t); setW(t, TEMP); \
+ E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
+ B = SHA_ROR(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
+
+static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
+{
+ unsigned int A,B,C,D,E;
+ unsigned int array[16];
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ /* Round 1 - iterations 0-16 take their input from 'data' */
+ T_0_15( 0, A, B, C, D, E);
+ T_0_15( 1, E, A, B, C, D);
+ T_0_15( 2, D, E, A, B, C);
+ T_0_15( 3, C, D, E, A, B);
+ T_0_15( 4, B, C, D, E, A);
+ T_0_15( 5, A, B, C, D, E);
+ T_0_15( 6, E, A, B, C, D);
+ T_0_15( 7, D, E, A, B, C);
+ T_0_15( 8, C, D, E, A, B);
+ T_0_15( 9, B, C, D, E, A);
+ T_0_15(10, A, B, C, D, E);
+ T_0_15(11, E, A, B, C, D);
+ T_0_15(12, D, E, A, B, C);
+ T_0_15(13, C, D, E, A, B);
+ T_0_15(14, B, C, D, E, A);
+ T_0_15(15, A, B, C, D, E);
+
+ /* Round 1 - tail. Input from 512-bit mixing array */
+ T_16_19(16, E, A, B, C, D);
+ T_16_19(17, D, E, A, B, C);
+ T_16_19(18, C, D, E, A, B);
+ T_16_19(19, B, C, D, E, A);
+
+ /* Round 2 */
+ T_20_39(20, A, B, C, D, E);
+ T_20_39(21, E, A, B, C, D);
+ T_20_39(22, D, E, A, B, C);
+ T_20_39(23, C, D, E, A, B);
+ T_20_39(24, B, C, D, E, A);
+ T_20_39(25, A, B, C, D, E);
+ T_20_39(26, E, A, B, C, D);
+ T_20_39(27, D, E, A, B, C);
+ T_20_39(28, C, D, E, A, B);
+ T_20_39(29, B, C, D, E, A);
+ T_20_39(30, A, B, C, D, E);
+ T_20_39(31, E, A, B, C, D);
+ T_20_39(32, D, E, A, B, C);
+ T_20_39(33, C, D, E, A, B);
+ T_20_39(34, B, C, D, E, A);
+ T_20_39(35, A, B, C, D, E);
+ T_20_39(36, E, A, B, C, D);
+ T_20_39(37, D, E, A, B, C);
+ T_20_39(38, C, D, E, A, B);
+ T_20_39(39, B, C, D, E, A);
+
+ /* Round 3 */
+ T_40_59(40, A, B, C, D, E);
+ T_40_59(41, E, A, B, C, D);
+ T_40_59(42, D, E, A, B, C);
+ T_40_59(43, C, D, E, A, B);
+ T_40_59(44, B, C, D, E, A);
+ T_40_59(45, A, B, C, D, E);
+ T_40_59(46, E, A, B, C, D);
+ T_40_59(47, D, E, A, B, C);
+ T_40_59(48, C, D, E, A, B);
+ T_40_59(49, B, C, D, E, A);
+ T_40_59(50, A, B, C, D, E);
+ T_40_59(51, E, A, B, C, D);
+ T_40_59(52, D, E, A, B, C);
+ T_40_59(53, C, D, E, A, B);
+ T_40_59(54, B, C, D, E, A);
+ T_40_59(55, A, B, C, D, E);
+ T_40_59(56, E, A, B, C, D);
+ T_40_59(57, D, E, A, B, C);
+ T_40_59(58, C, D, E, A, B);
+ T_40_59(59, B, C, D, E, A);
+
+ /* Round 4 */
+ T_60_79(60, A, B, C, D, E);
+ T_60_79(61, E, A, B, C, D);
+ T_60_79(62, D, E, A, B, C);
+ T_60_79(63, C, D, E, A, B);
+ T_60_79(64, B, C, D, E, A);
+ T_60_79(65, A, B, C, D, E);
+ T_60_79(66, E, A, B, C, D);
+ T_60_79(67, D, E, A, B, C);
+ T_60_79(68, C, D, E, A, B);
+ T_60_79(69, B, C, D, E, A);
+ T_60_79(70, A, B, C, D, E);
+ T_60_79(71, E, A, B, C, D);
+ T_60_79(72, D, E, A, B, C);
+ T_60_79(73, C, D, E, A, B);
+ T_60_79(74, B, C, D, E, A);
+ T_60_79(75, A, B, C, D, E);
+ T_60_79(76, E, A, B, C, D);
+ T_60_79(77, D, E, A, B, C);
+ T_60_79(78, C, D, E, A, B);
+ T_60_79(79, B, C, D, E, A);
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx)
+{
+ ctx->size = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants) */
+ ctx->H[0] = 0x67452301;
+ ctx->H[1] = 0xefcdab89;
+ ctx->H[2] = 0x98badcfe;
+ ctx->H[3] = 0x10325476;
+ ctx->H[4] = 0xc3d2e1f0;
+}
+
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+{
+ int lenW = ctx->size & 63;
+
+ ctx->size += len;
+
+ /* Read the data into W and process blocks as they get full */
+ if (lenW) {
+ int left = 64 - lenW;
+ if (len < left)
+ left = len;
+ memcpy(lenW + (char *)ctx->W, data, left);
+ lenW = (lenW + left) & 63;
+ len -= left;
+ data = ((const char *)data + left);
+ if (lenW)
+ return;
+ blk_SHA1_Block(ctx, ctx->W);
+ }
+ while (len >= 64) {
+ blk_SHA1_Block(ctx, data);
+ data = ((const char *)data + 64);
+ len -= 64;
+ }
+ if (len)
+ memcpy(ctx->W, data, len);
+}
+
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+{
+ static const unsigned char pad[64] = { 0x80 };
+ unsigned int padlen[2];
+ int i;
+
+ /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+ padlen[0] = htonl(ctx->size >> 29);
+ padlen[1] = htonl(ctx->size << 3);
+
+ i = ctx->size & 63;
+ blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
+ blk_SHA1_Update(ctx, padlen, 8);
+
+ /* Output hash */
+ for (i = 0; i < 5; i++)
+ put_be32(hashout + i*4, ctx->H[i]);
+}
diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h
new file mode 100644
index 000000000..b864df623
--- /dev/null
+++ b/block-sha1/sha1.h
@@ -0,0 +1,22 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+typedef struct {
+ unsigned long long size;
+ unsigned int H[5];
+ unsigned int W[16];
+} blk_SHA_CTX;
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+
+#define git_SHA_CTX blk_SHA_CTX
+#define git_SHA1_Init blk_SHA1_Init
+#define git_SHA1_Update blk_SHA1_Update
+#define git_SHA1_Final blk_SHA1_Final
diff --git a/builtin-add.c b/builtin-add.c
index 581a2a174..cb6e5906f 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -105,8 +105,8 @@ static void refresh(int verbose, const char **pathspec)
for (specs = 0; pathspec[specs]; specs++)
/* nothing */;
seen = xcalloc(specs, 1);
- refresh_index(&the_index, verbose ? REFRESH_SAY_CHANGED : REFRESH_QUIET,
- pathspec, seen);
+ refresh_index(&the_index, verbose ? REFRESH_IN_PORCELAIN : REFRESH_QUIET,
+ pathspec, seen, "Unstaged changes after refreshing the index:");
for (i = 0; i < specs; i++) {
if (!seen[i])
die("pathspec '%s' did not match any files", pathspec[i]);
@@ -131,27 +131,27 @@ static const char **validate_pathspec(int argc, const char **argv, const char *p
return pathspec;
}
-int interactive_add(int argc, const char **argv, const char *prefix)
+int run_add_interactive(const char *revision, const char *patch_mode,
+ const char **pathspec)
{
- int status, ac;
+ int status, ac, pc = 0;
const char **args;
- const char **pathspec = NULL;
- if (argc) {
- pathspec = validate_pathspec(argc, argv, prefix);
- if (!pathspec)
- return -1;
- }
+ if (pathspec)
+ while (pathspec[pc])
+ pc++;
- args = xcalloc(sizeof(const char *), (argc + 4));
+ args = xcalloc(sizeof(const char *), (pc + 5));
ac = 0;
args[ac++] = "add--interactive";
- if (patch_interactive)
- args[ac++] = "--patch";
+ if (patch_mode)
+ args[ac++] = patch_mode;
+ if (revision)
+ args[ac++] = revision;
args[ac++] = "--";
- if (argc) {
- memcpy(&(args[ac]), pathspec, sizeof(const char *) * argc);
- ac += argc;
+ if (pc) {
+ memcpy(&(args[ac]), pathspec, sizeof(const char *) * pc);
+ ac += pc;
}
args[ac] = NULL;
@@ -160,6 +160,21 @@ int interactive_add(int argc, const char **argv, const char *prefix)
return status;
}
+int interactive_add(int argc, const char **argv, const char *prefix)
+{
+ const char **pathspec = NULL;
+
+ if (argc) {
+ pathspec = validate_pathspec(argc, argv, prefix);
+ if (!pathspec)
+ return -1;
+ }
+
+ return run_add_interactive(NULL,
+ patch_interactive ? "--patch" : NULL,
+ pathspec);
+}
+
static int edit_patch(int argc, const char **argv, const char *prefix)
{
char *file = xstrdup(git_path("ADD_EDIT.patch"));
@@ -183,7 +198,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
out = open(file, O_CREAT | O_WRONLY, 0644);
if (out < 0)
die ("Could not open '%s' for writing.", file);
- rev.diffopt.file = fdopen(out, "w");
+ rev.diffopt.file = xfdopen(out, "w");
rev.diffopt.close_file = 1;
if (run_diff_files(&rev, 0))
die ("Could not write patch");
diff --git a/builtin-apply.c b/builtin-apply.c
index 39dc96ae0..c8372a0a8 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -61,6 +61,13 @@ static enum ws_error_action {
static int whitespace_error;
static int squelch_whitespace_errors = 5;
static int applied_after_fixing_ws;
+
+static enum ws_ignore {
+ ignore_ws_none,
+ ignore_ws_change,
+} ws_ignore_action = ignore_ws_none;
+
+
static const char *patch_input_file;
static const char *root;
static int root_len;
@@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option)
die("unrecognized whitespace option '%s'", option);
}
+static void parse_ignorewhitespace_option(const char *option)
+{
+ if (!option || !strcmp(option, "no") ||
+ !strcmp(option, "false") || !strcmp(option, "never") ||
+ !strcmp(option, "none")) {
+ ws_ignore_action = ignore_ws_none;
+ return;
+ }
+ if (!strcmp(option, "change")) {
+ ws_ignore_action = ignore_ws_change;
+ return;
+ }
+ die("unrecognized whitespace ignore option '%s'", option);
+}
+
static void set_default_whitespace_mode(const char *whitespace_option)
{
if (!whitespace_option && !apply_default_whitespace)
@@ -214,6 +236,62 @@ static uint32_t hash_line(const char *cp, size_t len)
return h;
}
+/*
+ * Compare lines s1 of length n1 and s2 of length n2, ignoring
+ * whitespace difference. Returns 1 if they match, 0 otherwise
+ */
+static int fuzzy_matchlines(const char *s1, size_t n1,
+ const char *s2, size_t n2)
+{
+ const char *last1 = s1 + n1 - 1;
+ const char *last2 = s2 + n2 - 1;
+ int result = 0;
+
+ if (n1 < 0 || n2 < 0)
+ return 0;
+
+ /* ignore line endings */
+ while ((*last1 == '\r') || (*last1 == '\n'))
+ last1--;
+ while ((*last2 == '\r') || (*last2 == '\n'))
+ last2--;
+
+ /* skip leading whitespace */
+ while (isspace(*s1) && (s1 <= last1))
+ s1++;
+ while (isspace(*s2) && (s2 <= last2))
+ s2++;
+ /* early return if both lines are empty */
+ if ((s1 > last1) && (s2 > last2))
+ return 1;
+ while (!result) {
+ result = *s1++ - *s2++;
+ /*
+ * Skip whitespace inside. We check for whitespace on
+ * both buffers because we don't want "a b" to match
+ * "ab"
+ */
+ if (isspace(*s1) && isspace(*s2)) {
+ while (isspace(*s1) && s1 <= last1)
+ s1++;
+ while (isspace(*s2) && s2 <= last2)
+ s2++;
+ }
+ /*
+ * If we reached the end on one side only,
+ * lines don't match
+ */
+ if (
+ ((s2 > last2) && (s1 <= last1)) ||
+ ((s1 > last1) && (s2 <= last2)))
+ return 0;
+ if ((s1 > last1) && (s2 > last2))
+ break;
+ }
+
+ return !result;
+}
+
static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
{
ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
@@ -1672,10 +1750,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
}
}
+/*
+ * Update the preimage, and the common lines in postimage,
+ * from buffer buf of length len. If postlen is 0 the postimage
+ * is updated in place, otherwise it's updated on a new buffer
+ * of length postlen
+ */
+
static void update_pre_post_images(struct image *preimage,
struct image *postimage,
char *buf,
- size_t len)
+ size_t len, size_t postlen)
{
int i, ctx;
char *new, *old, *fixed;
@@ -1694,11 +1779,19 @@ static void update_pre_post_images(struct image *preimage,
*preimage = fixed_preimage;
/*
- * Adjust the common context lines in postimage, in place.
- * This is possible because whitespace fixing does not make
- * the string grow.
+ * Adjust the common context lines in postimage. This can be
+ * done in-place when we are just doing whitespace fixing,
+ * which does not make the string grow, but needs a new buffer
+ * when ignoring whitespace causes the update, since in this case
+ * we could have e.g. tabs converted to multiple spaces.
+ * We trust the caller to tell us if the update can be done
+ * in place (postlen==0) or not.
*/
- new = old = postimage->buf;
+ old = postimage->buf;
+ if (postlen)
+ new = postimage->buf = xmalloc(postlen);
+ else
+ new = old;
fixed = preimage->buf;
for (i = ctx = 0; i < postimage->nr; i++) {
size_t len = postimage->line[i].len;
@@ -1773,12 +1866,56 @@ static int match_fragment(struct image *img,
!memcmp(img->buf + try, preimage->buf, preimage->len))
return 1;
+ /*
+ * No exact match. If we are ignoring whitespace, run a line-by-line
+ * fuzzy matching. We collect all the line length information because
+ * we need it to adjust whitespace if we match.
+ */
+ if (ws_ignore_action == ignore_ws_change) {
+ size_t imgoff = 0;
+ size_t preoff = 0;
+ size_t postlen = postimage->len;
+ for (i = 0; i < preimage->nr; i++) {
+ size_t prelen = preimage->line[i].len;
+ size_t imglen = img->line[try_lno+i].len;
+
+ if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
+ preimage->buf + preoff, prelen))
+ return 0;
+ if (preimage->line[i].flag & LINE_COMMON)
+ postlen += imglen - prelen;
+ imgoff += imglen;
+ preoff += prelen;
+ }
+
+ /*
+ * Ok, the preimage matches with whitespace fuzz. Update it and
+ * the common postimage lines to use the same whitespace as the
+ * target. imgoff now holds the true length of the target that
+ * matches the preimage, and we need to update the line lengths
+ * of the preimage to match the target ones.
+ */
+ fixed_buf = xmalloc(imgoff);
+ memcpy(fixed_buf, img->buf + try, imgoff);
+ for (i = 0; i < preimage->nr; i++)
+ preimage->line[i].len = img->line[try_lno+i].len;
+
+ /*
+ * Update the preimage buffer and the postimage context lines.
+ */
+ update_pre_post_images(preimage, postimage,
+ fixed_buf, imgoff, postlen);
+ return 1;
+ }
+
if (ws_error_action != correct_ws_error)
return 0;
/*
* The hunk does not apply byte-by-byte, but the hash says
- * it might with whitespace fuzz.
+ * it might with whitespace fuzz. We haven't been asked to
+ * ignore whitespace, we were asked to correct whitespace
+ * errors, so let's try matching after whitespace correction.
*/
fixed_buf = xmalloc(preimage->len + 1);
buf = fixed_buf;
@@ -1830,7 +1967,7 @@ static int match_fragment(struct image *img,
* hunk match. Update the context lines in the postimage.
*/
update_pre_post_images(preimage, postimage,
- fixed_buf, buf - fixed_buf);
+ fixed_buf, buf - fixed_buf, 0);
return 1;
unmatch_exit:
@@ -3272,6 +3409,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "apply.whitespace"))
return git_config_string(&apply_default_whitespace, var, value);
+ else if (!strcmp(var, "apply.ignorewhitespace"))
+ return git_config_string(&apply_default_ignorewhitespace, var, value);
return git_default_config(var, value, cb);
}
@@ -3308,6 +3447,16 @@ static int option_parse_z(const struct option *opt,
return 0;
}
+static int option_parse_space_change(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset)
+ ws_ignore_action = ignore_ws_none;
+ else
+ ws_ignore_action = ignore_ws_change;
+ return 0;
+}
+
static int option_parse_whitespace(const struct option *opt,
const char *arg, int unset)
{
@@ -3384,6 +3533,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
"detect new or modified lines that have whitespace errors",
0, option_parse_whitespace },
+ { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+ "ignore changes in whitespace when finding context",
+ PARSE_OPT_NOARG, option_parse_space_change },
+ { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+ "ignore changes in whitespace when finding context",
+ PARSE_OPT_NOARG, option_parse_space_change },
OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
"apply the patch in reverse"),
OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
@@ -3408,6 +3563,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
git_config(git_apply_config, NULL);
if (apply_default_whitespace)
parse_whitespace_option(apply_default_whitespace);
+ if (apply_default_ignorewhitespace)
+ parse_ignorewhitespace_option(apply_default_ignorewhitespace);
argc = parse_options(argc, argv, prefix, builtin_apply_options,
apply_usage, 0);
diff --git a/builtin-archive.c b/builtin-archive.c
index f9a4bea41..12351e9dd 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -60,6 +60,17 @@ static int run_remote_archiver(int argc, const char **argv,
return !!rv;
}
+static const char *format_from_name(const char *filename)
+{
+ const char *ext = strrchr(filename, '.');
+ if (!ext)
+ return NULL;
+ ext++;
+ if (!strcasecmp(ext, "zip"))
+ return "zip";
+ return NULL;
+}
+
#define PARSE_OPT_KEEP_ALL ( PARSE_OPT_KEEP_DASHDASH | \
PARSE_OPT_KEEP_ARGV0 | \
PARSE_OPT_KEEP_UNKNOWN | \
@@ -70,21 +81,39 @@ int cmd_archive(int argc, const char **argv, const char *prefix)
const char *exec = "git-upload-archive";
const char *output = NULL;
const char *remote = NULL;
+ const char *format = NULL;
struct option local_opts[] = {
- OPT_STRING(0, "output", &output, "file",
+ OPT_STRING('o', "output", &output, "file",
"write the archive to this file"),
OPT_STRING(0, "remote", &remote, "repo",
"retrieve the archive from remote repository <repo>"),
OPT_STRING(0, "exec", &exec, "cmd",
"path to the remote git-upload-archive command"),
+ OPT_STRING(0, "format", &format, "fmt", "archive format"),
OPT_END()
};
+ char fmt_opt[32];
argc = parse_options(argc, argv, prefix, local_opts, NULL,
PARSE_OPT_KEEP_ALL);
- if (output)
+ if (output) {
create_output_file(output);
+ if (!format)
+ format = format_from_name(output);
+ }
+
+ if (format) {
+ sprintf(fmt_opt, "--format=%s", format);
+ /*
+ * This is safe because either --format and/or --output must
+ * have been given on the original command line if we get to
+ * this point, and parse_options() must have eaten at least
+ * one argument, i.e. we have enough room to append to argv[].
+ */
+ argv[argc++] = fmt_opt;
+ argv[argc] = NULL;
+ }
if (remote)
return run_remote_archiver(argc, argv, remote, exec);
diff --git a/builtin-blame.c b/builtin-blame.c
index fd6ca51ee..7512773b4 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1348,7 +1348,7 @@ static void get_ac_line(const char *inbuf, const char *what,
/*
* Now, convert both name and e-mail using mailmap
*/
- if(map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
+ if (map_user(&mailmap, mail+1, mail_len-1, person, tmp-person-1)) {
/* Add a trailing '>' to email, since map_user returns plain emails
Note: It already has '<', since we replace from mail+1 */
mailpos = memchr(mail, '\0', mail_len);
diff --git a/builtin-branch.c b/builtin-branch.c
index 1a03d5f35..9f5799206 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -586,7 +586,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
- OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+ OPT_BOOLEAN('f', "force", &force_create, "force creation (when already exists)"),
{
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
"commit", "print only not merged branches",
diff --git a/builtin-checkout.c b/builtin-checkout.c
index c6d6ac99f..d050c3789 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -566,6 +566,13 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
return git_xmerge_config(var, value, cb);
}
+static int interactive_checkout(const char *revision, const char **pathspec,
+ struct checkout_opts *opts)
+{
+ return run_add_interactive(revision, "--patch=checkout", pathspec);
+}
+
+
int cmd_checkout(int argc, const char **argv, const char *prefix)
{
struct checkout_opts opts;
@@ -574,6 +581,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
struct branch_info new;
struct tree *source_tree = NULL;
char *conflict_style = NULL;
+ int patch_mode = 0;
struct option options[] = {
OPT__QUIET(&opts.quiet),
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
@@ -584,10 +592,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
2),
OPT_SET_INT('3', "theirs", &opts.writeout_stage, "stage",
3),
- OPT_BOOLEAN('f', NULL, &opts.force, "force"),
+ OPT_BOOLEAN('f', "force", &opts.force, "force"),
OPT_BOOLEAN('m', "merge", &opts.merge, "merge"),
OPT_STRING(0, "conflict", &conflict_style, "style",
"conflict style (merge or diff3)"),
+ OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
OPT_END(),
};
int has_dash_dash;
@@ -602,6 +611,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ 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");
+
/* --track without -b should DWIM */
if (0 < opts.track && !opts.new_branch) {
const char *argv0 = argv[0];
@@ -708,6 +721,9 @@ no_reference:
if (!pathspec)
die("invalid path specification");
+ if (patch_mode)
+ return interactive_checkout(new.name, pathspec, &opts);
+
/* Checkout paths */
if (opts.new_branch) {
if (argc == 1) {
@@ -723,6 +739,9 @@ no_reference:
return checkout_paths(source_tree, pathspec, &opts);
}
+ if (patch_mode)
+ return interactive_checkout(new.name, NULL, &opts);
+
if (opts.new_branch) {
struct strbuf buf = STRBUF_INIT;
if (strbuf_check_branch_ref(&buf, opts.new_branch))
diff --git a/builtin-clean.c b/builtin-clean.c
index 05c763cbe..28cdcd027 100644
--- a/builtin-clean.c
+++ b/builtin-clean.c
@@ -41,7 +41,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct option options[] = {
OPT__QUIET(&quiet),
OPT__DRY_RUN(&show_only),
- OPT_BOOLEAN('f', NULL, &force, "force"),
+ OPT_BOOLEAN('f', "force", &force, "force"),
OPT_BOOLEAN('d', NULL, &remove_directories,
"remove whole directories"),
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
diff --git a/builtin-clone.c b/builtin-clone.c
index e51978a15..bab2d84ea 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -38,9 +38,10 @@ static const char * const builtin_clone_usage[] = {
};
static int option_quiet, option_no_checkout, option_bare, option_mirror;
-static int option_local, option_no_hardlinks, option_shared;
+static int option_local, option_no_hardlinks, option_shared, option_recursive;
static char *option_template, *option_reference, *option_depth;
static char *option_origin = NULL;
+static char *option_branch = NULL;
static char *option_upload_pack = "git-upload-pack";
static int option_verbose;
@@ -59,12 +60,16 @@ static struct option builtin_clone_options[] = {
"don't use local hardlinks, always copy"),
OPT_BOOLEAN('s', "shared", &option_shared,
"setup as shared repository"),
+ OPT_BOOLEAN(0, "recursive", &option_recursive,
+ "setup as shared repository"),
OPT_STRING(0, "template", &option_template, "path",
"path the template repository"),
OPT_STRING(0, "reference", &option_reference, "repo",
"reference repository"),
OPT_STRING('o', "origin", &option_origin, "branch",
"use <branch> instead of 'origin' to track upstream"),
+ OPT_STRING('b', "branch", &option_branch, "branch",
+ "checkout <branch> instead of the remote's HEAD"),
OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
"path to git-upload-pack on the remote"),
OPT_STRING(0, "depth", &option_depth, "depth",
@@ -73,6 +78,10 @@ static struct option builtin_clone_options[] = {
OPT_END()
};
+static const char *argv_submodule[] = {
+ "submodule", "update", "--init", "--recursive", NULL
+};
+
static char *get_repo_path(const char *repo, int *is_bundle)
{
static char *suffix[] = { "/.git", ".git", "" };
@@ -260,7 +269,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
die_errno("failed to create link '%s'", dest->buf);
option_no_hardlinks = 1;
}
- if (copy_file(dest->buf, src->buf, 0666))
+ if (copy_file_with_time(dest->buf, src->buf, 0666))
die_errno("failed to copy file to '%s'", dest->buf);
}
closedir(dir);
@@ -347,7 +356,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
const char *repo_name, *repo, *work_tree, *git_dir;
char *path, *dir;
int dest_exists;
- const struct ref *refs, *head_points_at, *remote_head, *mapped_refs;
+ const struct ref *refs, *remote_head, *mapped_refs;
+ const struct ref *remote_head_points_at;
+ const struct ref *our_head_points_at;
struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
struct transport *transport = NULL;
@@ -509,7 +520,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
option_upload_pack);
refs = transport_get_remote_refs(transport);
- if(refs)
+ if (refs)
transport_fetch_refs(transport, refs);
}
@@ -519,11 +530,31 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
mapped_refs = write_remote_refs(refs, refspec, reflog_msg.buf);
remote_head = find_ref_by_name(refs, "HEAD");
- head_points_at = guess_remote_head(remote_head, mapped_refs, 0);
+ remote_head_points_at =
+ guess_remote_head(remote_head, mapped_refs, 0);
+
+ if (option_branch) {
+ struct strbuf head = STRBUF_INIT;
+ strbuf_addstr(&head, src_ref_prefix);
+ strbuf_addstr(&head, option_branch);
+ our_head_points_at =
+ find_ref_by_name(mapped_refs, head.buf);
+ strbuf_release(&head);
+
+ if (!our_head_points_at) {
+ warning("Remote branch %s not found in "
+ "upstream %s, using HEAD instead",
+ option_branch, option_origin);
+ our_head_points_at = remote_head_points_at;
+ }
+ }
+ else
+ our_head_points_at = remote_head_points_at;
}
else {
warning("You appear to have cloned an empty repository.");
- head_points_at = NULL;
+ our_head_points_at = NULL;
+ remote_head_points_at = NULL;
remote_head = NULL;
option_no_checkout = 1;
if (!option_bare)
@@ -531,41 +562,35 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
"refs/heads/master");
}
- if (head_points_at) {
- /* Local default branch link */
- create_symref("HEAD", head_points_at->name, NULL);
+ if (remote_head_points_at && !option_bare) {
+ struct strbuf head_ref = STRBUF_INIT;
+ strbuf_addstr(&head_ref, branch_top.buf);
+ strbuf_addstr(&head_ref, "HEAD");
+ create_symref(head_ref.buf,
+ remote_head_points_at->peer_ref->name,
+ reflog_msg.buf);
+ }
+ if (our_head_points_at) {
+ /* Local default branch link */
+ create_symref("HEAD", our_head_points_at->name, NULL);
if (!option_bare) {
- struct strbuf head_ref = STRBUF_INIT;
- const char *head = head_points_at->name;
-
- if (!prefixcmp(head, "refs/heads/"))
- head += 11;
-
- /* Set up the initial local branch */
-
- /* Local branch initial value */
+ const char *head = skip_prefix(our_head_points_at->name,
+ "refs/heads/");
update_ref(reflog_msg.buf, "HEAD",
- head_points_at->old_sha1,
+ our_head_points_at->old_sha1,
NULL, 0, DIE_ON_ERR);
-
- strbuf_addstr(&head_ref, branch_top.buf);
- strbuf_addstr(&head_ref, "HEAD");
-
- /* Remote branch link */
- create_symref(head_ref.buf,
- head_points_at->peer_ref->name,
- reflog_msg.buf);
-
install_branch_config(0, head, option_origin,
- head_points_at->name);
+ our_head_points_at->name);
}
} else if (remote_head) {
/* Source had detached HEAD pointing somewhere. */
- if (!option_bare)
+ if (!option_bare) {
update_ref(reflog_msg.buf, "HEAD",
remote_head->old_sha1,
NULL, REF_NODEREF, DIE_ON_ERR);
+ our_head_points_at = remote_head;
+ }
} else {
/* Nothing to checkout out */
if (!option_no_checkout)
@@ -599,7 +624,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
opts.src_index = &the_index;
opts.dst_index = &the_index;
- tree = parse_tree_indirect(remote_head->old_sha1);
+ tree = parse_tree_indirect(our_head_points_at->old_sha1);
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
unpack_trees(1, &t, &opts);
@@ -610,6 +635,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(remote_head->old_sha1), "1", NULL);
+
+ if (!err && option_recursive)
+ err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
}
strbuf_release(&reflog_msg);
diff --git a/builtin-commit.c b/builtin-commit.c
index 4bcce06fb..200ffdaad 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -51,7 +51,7 @@ static const char *template_file;
static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, no_verify, allow_empty;
+static int quiet, verbose, no_verify, allow_empty, dry_run;
static char *untracked_files_arg;
/*
* The default commit message cleanup mode will remove the lines
@@ -103,6 +103,7 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
+ OPT_BOOLEAN(0, "dry-run", &dry_run, "show what would be committed"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
{ 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" },
OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
@@ -217,12 +218,15 @@ static void create_base_index(void)
exit(128); /* We've already reported the error, finish dying */
}
-static char *prepare_index(int argc, const char **argv, const char *prefix)
+static char *prepare_index(int argc, const char **argv, const char *prefix, int is_status)
{
int fd;
struct string_list partial;
const char **pathspec = NULL;
+ int refresh_flags = REFRESH_QUIET;
+ if (is_status)
+ refresh_flags |= REFRESH_UNMERGED;
if (interactive) {
if (interactive_add(argc, argv, prefix) != 0)
die("interactive add failed");
@@ -253,7 +257,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
if (all || (also && pathspec && *pathspec)) {
int fd = hold_locked_index(&index_lock, 1);
add_files_to_cache(also ? prefix : NULL, pathspec, 0);
- refresh_cache(REFRESH_QUIET);
+ refresh_cache(refresh_flags);
if (write_cache(fd, active_cache, active_nr) ||
close_lock_file(&index_lock))
die("unable to write new_index file");
@@ -272,7 +276,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
*/
if (!pathspec || !*pathspec) {
fd = hold_locked_index(&index_lock, 1);
- refresh_cache(REFRESH_QUIET);
+ refresh_cache(refresh_flags);
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(&index_lock))
die("unable to write new_index file");
@@ -339,27 +343,24 @@ static char *prepare_index(int argc, const char **argv, const char *prefix)
return false_lock.filename;
}
-static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn)
+static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
+ struct wt_status *s)
{
- struct wt_status s;
-
- wt_status_prepare(&s);
- if (wt_status_relative_paths)
- s.prefix = prefix;
+ if (s->relative_paths)
+ s->prefix = prefix;
if (amend) {
- s.amend = 1;
- s.reference = "HEAD^1";
+ s->amend = 1;
+ s->reference = "HEAD^1";
}
- s.verbose = verbose;
- s.untracked = (show_untracked_files == SHOW_ALL_UNTRACKED_FILES);
- s.index_file = index_file;
- s.fp = fp;
- s.nowarn = nowarn;
+ s->verbose = verbose;
+ s->index_file = index_file;
+ s->fp = fp;
+ s->nowarn = nowarn;
- wt_status_print(&s);
+ wt_status_print(s);
- return s.commitable;
+ return s->commitable;
}
static int is_a_merge(const unsigned char *sha1)
@@ -413,7 +414,8 @@ static void determine_author_info(void)
author_date = date;
}
-static int prepare_to_commit(const char *index_file, const char *prefix)
+static int prepare_to_commit(const char *index_file, const char *prefix,
+ struct wt_status *s)
{
struct stat statbuf;
int commitable, saved_color_setting;
@@ -555,10 +557,10 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
if (ident_shown)
fprintf(fp, "#\n");
- saved_color_setting = wt_status_use_color;
- wt_status_use_color = 0;
- commitable = run_status(fp, index_file, prefix, 1);
- wt_status_use_color = saved_color_setting;
+ saved_color_setting = s->use_color;
+ s->use_color = 0;
+ commitable = run_status(fp, index_file, prefix, 1, s);
+ s->use_color = saved_color_setting;
} else {
unsigned char sha1[20];
const char *parent = "HEAD";
@@ -579,7 +581,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
if (!commitable && !in_merge && !allow_empty &&
!(amend && is_a_merge(head_sha1))) {
- run_status(stdout, index_file, prefix, 0);
+ run_status(stdout, index_file, prefix, 0, s);
return 0;
}
@@ -691,7 +693,8 @@ static const char *find_author_by_nickname(const char *name)
static int parse_and_validate_options(int argc, const char *argv[],
const char * const usage[],
- const char *prefix)
+ const char *prefix,
+ struct wt_status *s)
{
int f = 0;
@@ -794,11 +797,11 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (!untracked_files_arg)
; /* default already initialized */
else if (!strcmp(untracked_files_arg, "no"))
- show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+ s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
else if (!strcmp(untracked_files_arg, "normal"))
- show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
else if (!strcmp(untracked_files_arg, "all"))
- show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+ s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
else
die("Invalid untracked files mode '%s'", untracked_files_arg);
@@ -810,28 +813,93 @@ static int parse_and_validate_options(int argc, const char *argv[],
return argc;
}
-int cmd_status(int argc, const char **argv, const char *prefix)
+static int dry_run_commit(int argc, const char **argv, const char *prefix,
+ struct wt_status *s)
{
- const char *index_file;
int commitable;
+ const char *index_file;
- git_config(git_status_config, NULL);
+ index_file = prepare_index(argc, argv, prefix, 1);
+ commitable = run_status(stdout, index_file, prefix, 0, s);
+ rollback_index_files();
- if (wt_status_use_color == -1)
- wt_status_use_color = git_use_color_default;
+ return commitable ? 0 : 1;
+}
- if (diff_use_color_default == -1)
- diff_use_color_default = git_use_color_default;
+static int parse_status_slot(const char *var, int offset)
+{
+ if (!strcasecmp(var+offset, "header"))
+ return WT_STATUS_HEADER;
+ if (!strcasecmp(var+offset, "updated")
+ || !strcasecmp(var+offset, "added"))
+ return WT_STATUS_UPDATED;
+ if (!strcasecmp(var+offset, "changed"))
+ return WT_STATUS_CHANGED;
+ if (!strcasecmp(var+offset, "untracked"))
+ return WT_STATUS_UNTRACKED;
+ if (!strcasecmp(var+offset, "nobranch"))
+ return WT_STATUS_NOBRANCH;
+ if (!strcasecmp(var+offset, "unmerged"))
+ return WT_STATUS_UNMERGED;
+ die("bad config variable '%s'", var);
+}
- argc = parse_and_validate_options(argc, argv, builtin_status_usage, prefix);
+static int git_status_config(const char *k, const char *v, void *cb)
+{
+ struct wt_status *s = cb;
- index_file = prepare_index(argc, argv, prefix);
+ if (!strcmp(k, "status.submodulesummary")) {
+ int is_bool;
+ s->submodule_summary = git_config_bool_or_int(k, v, &is_bool);
+ if (is_bool && s->submodule_summary)
+ s->submodule_summary = -1;
+ return 0;
+ }
+ if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
+ s->use_color = git_config_colorbool(k, v, -1);
+ return 0;
+ }
+ if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
+ int slot = parse_status_slot(k, 13);
+ if (!v)
+ return config_error_nonbool(k);
+ color_parse(v, k, s->color_palette[slot]);
+ return 0;
+ }
+ if (!strcmp(k, "status.relativepaths")) {
+ s->relative_paths = git_config_bool(k, v);
+ return 0;
+ }
+ if (!strcmp(k, "status.showuntrackedfiles")) {
+ if (!v)
+ return config_error_nonbool(k);
+ else if (!strcmp(v, "no"))
+ s->show_untracked_files = SHOW_NO_UNTRACKED_FILES;
+ else if (!strcmp(v, "normal"))
+ s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ else if (!strcmp(v, "all"))
+ s->show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
+ else
+ return error("Invalid untracked files mode '%s'", v);
+ return 0;
+ }
+ return git_diff_ui_config(k, v, NULL);
+}
- commitable = run_status(stdout, index_file, prefix, 0);
+int cmd_status(int argc, const char **argv, const char *prefix)
+{
+ struct wt_status s;
- rollback_index_files();
+ wt_status_prepare(&s);
+ git_config(git_status_config, &s);
+ if (s.use_color == -1)
+ s.use_color = git_use_color_default;
+ if (diff_use_color_default == -1)
+ diff_use_color_default = git_use_color_default;
- return commitable ? 0 : 1;
+ argc = parse_and_validate_options(argc, argv, builtin_status_usage,
+ prefix, &s);
+ return dry_run_commit(argc, argv, prefix, &s);
}
static void print_summary(const char *prefix, const unsigned char *sha1)
@@ -883,10 +951,12 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
static int git_commit_config(const char *k, const char *v, void *cb)
{
+ struct wt_status *s = cb;
+
if (!strcmp(k, "commit.template"))
return git_config_string(&template_file, k, v);
- return git_status_config(k, v, cb);
+ return git_status_config(k, v, s);
}
int cmd_commit(int argc, const char **argv, const char *prefix)
@@ -899,19 +969,26 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
struct commit_list *parents = NULL, **pptr = &parents;
struct stat statbuf;
int allow_fast_forward = 1;
+ struct wt_status s;
- git_config(git_commit_config, NULL);
-
- if (wt_status_use_color == -1)
- wt_status_use_color = git_use_color_default;
+ wt_status_prepare(&s);
+ git_config(git_commit_config, &s);
- argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix);
+ if (s.use_color == -1)
+ s.use_color = git_use_color_default;
- index_file = prepare_index(argc, argv, prefix);
+ argc = parse_and_validate_options(argc, argv, builtin_commit_usage,
+ prefix, &s);
+ if (dry_run) {
+ if (diff_use_color_default == -1)
+ diff_use_color_default = git_use_color_default;
+ return dry_run_commit(argc, argv, prefix, &s);
+ }
+ index_file = prepare_index(argc, argv, prefix, 0);
/* Set up everything for writing the commit object. This includes
running hooks, writing the trees, and interacting with the user. */
- if (!prepare_to_commit(index_file, prefix)) {
+ if (!prepare_to_commit(index_file, prefix, &s)) {
rollback_index_files();
return 1;
}
diff --git a/builtin-describe.c b/builtin-describe.c
index 7a662980d..df67a733a 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -20,6 +20,7 @@ static int tags; /* Allow lightweight tags */
static int longformat;
static int abbrev = DEFAULT_ABBREV;
static int max_candidates = 10;
+static int found_names;
static const char *pattern;
static int always;
@@ -49,6 +50,7 @@ static void add_to_known_names(const char *path,
memcpy(e->path, path, len);
commit->util = e;
}
+ found_names = 1;
}
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
@@ -195,6 +197,9 @@ static void describe(const char *arg, int last_one)
for_each_ref(get_name, NULL);
}
+ if (!found_names)
+ die("cannot describe '%s'", sha1_to_hex(sha1));
+
n = cmit->util;
if (n) {
/*
diff --git a/builtin-diff.c b/builtin-diff.c
index 2e51f408f..ffcdd055c 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -218,6 +218,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
revs->max_count = 3;
else if (!strcmp(argv[1], "-q"))
options |= DIFF_SILENT_ON_REMOVED;
+ else if (!strcmp(argv[1], "-h"))
+ usage(builtin_diff_usage);
else
return error("invalid option: %s", argv[1]);
argv++; argc--;
diff --git a/builtin-fast-export.c b/builtin-fast-export.c
index c48c18d0c..b0a4029c9 100644
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -26,6 +26,7 @@ static int progress;
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 parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset)
@@ -116,6 +117,9 @@ static void handle_object(const unsigned char *sha1)
char *buf;
struct object *object;
+ if (no_data)
+ return;
+
if (is_null_sha1(sha1))
return;
@@ -173,7 +177,7 @@ static void show_filemodify(struct diff_queue_struct *q,
* Links refer to objects in another repositories;
* output the SHA-1 verbatim.
*/
- if (S_ISGITLINK(spec->mode))
+ if (no_data || S_ISGITLINK(spec->mode))
printf("M %06o %s %s\n", spec->mode,
sha1_to_hex(spec->sha1), spec->path);
else {
@@ -580,6 +584,9 @@ 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"),
+ { OPTION_NEGBIT, 0, "data", &no_data, NULL,
+ "Skip output of blob data",
+ PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
OPT_END()
};
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 817dd6bff..cb48c57ca 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -454,7 +454,7 @@ static int quickfetch(struct ref *ref_map)
for (ref = ref_map; ref; ref = ref->next) {
if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
- write_in_full(revlist.in, "\n", 1) < 0) {
+ write_str_in_full(revlist.in, "\n") < 0) {
if (errno != EPIPE && errno != EINVAL)
error("failed write to rev-list: %s", strerror(errno));
err = -1;
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index d7cc8cafb..a5a83f146 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -576,7 +576,7 @@ static void populate_value(struct refinfo *ref)
if (!prefixcmp(name, "refname"))
refname = ref->refname;
- else if(!prefixcmp(name, "upstream")) {
+ else if (!prefixcmp(name, "upstream")) {
struct branch *branch;
/* only local branches may have an upstream */
if (prefixcmp(ref->refname, "refs/heads/"))
diff --git a/builtin-fsck.c b/builtin-fsck.c
index b3d38fa27..c58b0e337 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -589,6 +589,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
struct alternate_object_database *alt;
errors_found = 0;
+ read_replace_refs = 0;
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
if (write_lost_and_found) {
diff --git a/builtin-grep.c b/builtin-grep.c
index fd450bc16..761799d7d 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -54,25 +54,57 @@ static int grep_config(const char *var, const char *value, void *cb)
}
/*
+ * Return non-zero if max_depth is negative or path has no more then max_depth
+ * slashes.
+ */
+static int accept_subdir(const char *path, int max_depth)
+{
+ if (max_depth < 0)
+ return 1;
+
+ while ((path = strchr(path, '/')) != NULL) {
+ max_depth--;
+ if (max_depth < 0)
+ return 0;
+ path++;
+ }
+ return 1;
+}
+
+/*
+ * Return non-zero if name is a subdirectory of match and is not too deep.
+ */
+static int is_subdir(const char *name, int namelen,
+ const char *match, int matchlen, int max_depth)
+{
+ if (matchlen > namelen || strncmp(name, match, matchlen))
+ return 0;
+
+ if (name[matchlen] == '\0') /* exact match */
+ return 1;
+
+ if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
+ return accept_subdir(name + matchlen + 1, max_depth);
+
+ return 0;
+}
+
+/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
* pathname wildcards are allowed.
*/
-static int pathspec_matches(const char **paths, const char *name)
+static int pathspec_matches(const char **paths, const char *name, int max_depth)
{
int namelen, i;
if (!paths || !*paths)
- return 1;
+ return accept_subdir(name, max_depth);
namelen = strlen(name);
for (i = 0; paths[i]; i++) {
const char *match = paths[i];
int matchlen = strlen(match);
const char *cp, *meta;
- if (!matchlen ||
- ((matchlen <= namelen) &&
- !strncmp(name, match, matchlen) &&
- (match[matchlen-1] == '/' ||
- name[matchlen] == '\0' || name[matchlen] == '/')))
+ if (is_subdir(name, namelen, match, matchlen, max_depth))
return 1;
if (!fnmatch(match, name, 0))
return 1;
@@ -411,7 +443,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
int kept;
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
name = ce->name;
if (name[0] == '-') {
@@ -469,7 +501,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
/*
* If CE_VALID is on, we assume worktree file and its cache entry
@@ -529,7 +561,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
strbuf_addch(&pathbuf, '/');
down = pathbuf.buf + tn_len;
- if (!pathspec_matches(paths, down))
+ if (!pathspec_matches(paths, down, opt->max_depth))
;
else if (S_ISREG(entry.mode))
hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
@@ -683,6 +715,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_SET_INT('I', NULL, &opt.binary,
"don't match patterns in binary files",
GREP_BINARY_NOMATCH),
+ { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
+ "descend at most <depth> levels", PARSE_OPT_NONEG,
+ NULL, 1 },
OPT_GROUP(""),
OPT_BIT('E', "extended-regexp", &opt.regflags,
"use extended POSIX regular expressions", REG_EXTENDED),
@@ -760,6 +795,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
opt.pathname = 1;
opt.pattern_tail = &opt.pattern_list;
opt.regflags = REG_NEWLINE;
+ opt.max_depth = -1;
strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
opt.color = -1;
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 4a5600631..dd84caecb 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
+#include "parse-options.h"
#ifndef DEFAULT_GIT_TEMPLATE_DIR
#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -370,8 +371,16 @@ static int guess_repository_type(const char *git_dir)
return 1;
}
-static const char init_db_usage[] =
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]]";
+static int shared_callback(const struct option *opt, const char *arg, int unset)
+{
+ *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
+ return 0;
+}
+
+static const char *const init_db_usage[] = {
+ "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]",
+ NULL
+};
/*
* If you want to, you can share the DB area with any number of branches.
@@ -384,25 +393,60 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *git_dir;
const char *template_dir = NULL;
unsigned int flags = 0;
- int i;
-
- for (i = 1; i < argc; i++, argv++) {
- const char *arg = argv[1];
- if (!prefixcmp(arg, "--template="))
- template_dir = arg+11;
- else if (!strcmp(arg, "--bare")) {
- static char git_dir[PATH_MAX+1];
- is_bare_repository_cfg = 1;
- setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir,
- sizeof(git_dir)), 0);
- } else if (!strcmp(arg, "--shared"))
- init_shared_repository = PERM_GROUP;
- else if (!prefixcmp(arg, "--shared="))
- init_shared_repository = git_config_perm("arg", arg+9);
- else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
- flags |= INIT_DB_QUIET;
- else
- usage(init_db_usage);
+ const struct option init_db_options[] = {
+ OPT_STRING(0, "template", &template_dir, "template-directory",
+ "provide the directory from which templates will be used"),
+ OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+ "create a bare repository", 1),
+ { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+ "permissions",
+ "specify that the git repository is to be shared amongst several users",
+ PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
+ OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+
+ if (argc == 1) {
+ int mkdir_tried = 0;
+ retry:
+ if (chdir(argv[0]) < 0) {
+ if (!mkdir_tried) {
+ int saved;
+ /*
+ * At this point we haven't read any configuration,
+ * and we know shared_repository should always be 0;
+ * but just in case we play safe.
+ */
+ saved = shared_repository;
+ shared_repository = 0;
+ switch (safe_create_leading_directories_const(argv[0])) {
+ case -3:
+ errno = EEXIST;
+ /* fallthru */
+ case -1:
+ die_errno("cannot mkdir %s", argv[0]);
+ break;
+ default:
+ break;
+ }
+ shared_repository = saved;
+ if (mkdir(argv[0], 0777) < 0)
+ die_errno("cannot mkdir %s", argv[0]);
+ mkdir_tried = 1;
+ goto retry;
+ }
+ die_errno("cannot chdir to %s", argv[0]);
+ }
+ } else if (0 < argc) {
+ usage(init_db_usage[0]);
+ }
+ if (is_bare_repository_cfg == 1) {
+ static char git_dir[PATH_MAX+1];
+
+ setenv(GIT_DIR_ENVIRONMENT,
+ getcwd(git_dir, sizeof(git_dir)), 0);
}
if (init_shared_repository != -1)
diff --git a/builtin-log.c b/builtin-log.c
index 8d93c1a2a..25e21ed41 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -27,6 +27,10 @@ static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
+static const char * const builtin_log_usage =
+ "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
+ " or: git show [options] <object>...";
+
static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev)
{
@@ -69,6 +73,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
die("invalid --decorate option: %s", arg);
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
+ } else if (!strcmp(arg, "-h")) {
+ usage(builtin_log_usage);
} else
die("unrecognized argument: %s", arg);
}
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 92637ac0b..d498b1cd2 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -25,6 +25,7 @@ static enum {
static struct strbuf charset = STRBUF_INIT;
static int patch_lines;
static struct strbuf **p_hdr_data, **s_hdr_data;
+static int use_scissors;
#define MAX_HDR_PARSED 10
#define MAX_BOUNDARIES 5
@@ -712,6 +713,56 @@ static inline int patchbreak(const struct strbuf *line)
return 0;
}
+static int is_scissors_line(const struct strbuf *line)
+{
+ size_t i, len = line->len;
+ int scissors = 0, gap = 0;
+ int first_nonblank = -1;
+ int last_nonblank = 0, visible, perforation = 0, in_perforation = 0;
+ const char *buf = line->buf;
+
+ for (i = 0; i < len; i++) {
+ if (isspace(buf[i])) {
+ if (in_perforation) {
+ perforation++;
+ gap++;
+ }
+ continue;
+ }
+ last_nonblank = i;
+ if (first_nonblank < 0)
+ first_nonblank = i;
+ if (buf[i] == '-') {
+ in_perforation = 1;
+ perforation++;
+ continue;
+ }
+ if (i + 1 < len &&
+ (!memcmp(buf + i, ">8", 2) || !memcmp(buf + i, "8<", 2))) {
+ in_perforation = 1;
+ perforation += 2;
+ scissors += 2;
+ i++;
+ continue;
+ }
+ in_perforation = 0;
+ }
+
+ /*
+ * The mark must be at least 8 bytes long (e.g. "-- >8 --").
+ * Even though there can be arbitrary cruft on the same line
+ * (e.g. "cut here"), in order to avoid misidentification, the
+ * perforation must occupy more than a third of the visible
+ * width of the line, and dashes and scissors must occupy more
+ * than half of the perforation.
+ */
+
+ visible = last_nonblank - first_nonblank + 1;
+ return (scissors && 8 <= visible &&
+ visible < perforation * 3 &&
+ gap * 2 < perforation);
+}
+
static int handle_commit_msg(struct strbuf *line)
{
static int still_looking = 1;
@@ -723,7 +774,8 @@ static int handle_commit_msg(struct strbuf *line)
strbuf_ltrim(line);
if (!line->len)
return 0;
- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
+ still_looking = check_header(line, s_hdr_data, 0);
+ if (still_looking)
return 0;
}
@@ -731,6 +783,24 @@ static int handle_commit_msg(struct strbuf *line)
if (metainfo_charset)
convert_to_utf8(line, charset.buf);
+ if (use_scissors && is_scissors_line(line)) {
+ int i;
+ rewind(cmitmsg);
+ ftruncate(fileno(cmitmsg), 0);
+ still_looking = 1;
+
+ /*
+ * We may have already read "secondary headers"; purge
+ * them to give ourselves a clean restart.
+ */
+ for (i = 0; header[i]; i++) {
+ if (s_hdr_data[i])
+ strbuf_release(s_hdr_data[i]);
+ s_hdr_data[i] = NULL;
+ }
+ return 0;
+ }
+
if (patchbreak(line)) {
fclose(cmitmsg);
cmitmsg = NULL;
@@ -765,7 +835,6 @@ static void handle_filter(struct strbuf *line)
static void handle_body(void)
{
- int len = 0;
struct strbuf prev = STRBUF_INIT;
/* Skip up to the first boundary */
@@ -775,8 +844,6 @@ static void handle_body(void)
}
do {
- strbuf_setlen(&line, line.len + len);
-
/* process any boundary lines */
if (*content_top && is_multipart_boundary(&line)) {
/* flush any leftover */
@@ -832,10 +899,7 @@ static void handle_body(void)
handle_filter(&line);
}
- strbuf_reset(&line);
- if (strbuf_avail(&line) < 100)
- strbuf_grow(&line, 100);
- } while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
+ } while (!strbuf_getwholeline(&line, fin, '\n'));
handle_body_out:
strbuf_release(&prev);
@@ -891,12 +955,9 @@ static void handle_info(void)
fprintf(fout, "\n");
}
-static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
- const char *msg, const char *patch)
+static int mailinfo(FILE *in, FILE *out, const char *msg, const char *patch)
{
int peek;
- keep_subject = ks;
- metainfo_charset = encoding;
fin = in;
fout = out;
@@ -930,8 +991,20 @@ static int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
return 0;
}
+static int git_mailinfo_config(const char *var, const char *value, void *unused)
+{
+ if (prefixcmp(var, "mailinfo."))
+ return git_default_config(var, value, unused);
+ if (!strcmp(var, "mailinfo.scissors")) {
+ use_scissors = git_config_bool(var, value);
+ return 0;
+ }
+ /* perhaps others here */
+ return 0;
+}
+
static const char mailinfo_usage[] =
- "git mailinfo [-k] [-u | --encoding=<encoding> | -n] msg patch <mail >info";
+ "git mailinfo [-k] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] msg patch < mail >info";
int cmd_mailinfo(int argc, const char **argv, const char *prefix)
{
@@ -940,7 +1013,7 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
/* NEEDSWORK: might want to do the optional .git/ directory
* discovery
*/
- git_config(git_default_config, NULL);
+ git_config(git_mailinfo_config, NULL);
def_charset = (git_commit_encoding ? git_commit_encoding : "UTF-8");
metainfo_charset = def_charset;
@@ -954,6 +1027,10 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
metainfo_charset = NULL;
else if (!prefixcmp(argv[1], "--encoding="))
metainfo_charset = argv[1] + 11;
+ else if (!strcmp(argv[1], "--scissors"))
+ use_scissors = 1;
+ else if (!strcmp(argv[1], "--no-scissors"))
+ use_scissors = 0;
else
usage(mailinfo_usage);
argc--; argv++;
@@ -962,5 +1039,5 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
if (argc != 3)
usage(mailinfo_usage);
- return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
+ return !!mailinfo(stdin, stdout, argv[1], argv[2]);
}
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index ad5f6b593..dfe5b151e 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -7,6 +7,7 @@
#include "cache.h"
#include "builtin.h"
#include "string-list.h"
+#include "strbuf.h"
static const char git_mailsplit_usage[] =
"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
@@ -42,26 +43,8 @@ static int is_from_line(const char *line, int len)
return 1;
}
-/* Could be as small as 64, enough to hold a Unix "From " line. */
-static char buf[4096];
-
-/* We cannot use fgets() because our lines can contain NULs */
-int read_line_with_nul(char *buf, int size, FILE *in)
-{
- int len = 0, c;
-
- for (;;) {
- c = getc(in);
- if (c == EOF)
- break;
- buf[len++] = c;
- if (c == '\n' || len + 1 >= size)
- break;
- }
- buf[len] = '\0';
-
- return len;
-}
+static struct strbuf buf = STRBUF_INIT;
+static int keep_cr;
/* Called with the first line (potentially partial)
* already in buf[] -- normally that should begin with
@@ -71,10 +54,9 @@ int read_line_with_nul(char *buf, int size, FILE *in)
static int split_one(FILE *mbox, const char *name, int allow_bare)
{
FILE *output = NULL;
- int len = strlen(buf);
int fd;
int status = 0;
- int is_bare = !is_from_line(buf, len);
+ int is_bare = !is_from_line(buf.buf, buf.len);
if (is_bare && !allow_bare)
goto corrupt;
@@ -82,26 +64,29 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0)
die_errno("cannot open output file '%s'", name);
- output = fdopen(fd, "w");
+ output = xfdopen(fd, "w");
/* Copy it out, while searching for a line that begins with
* "From " and having something that looks like a date format.
*/
for (;;) {
- int is_partial = len && buf[len-1] != '\n';
+ if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
+ buf.buf[buf.len-2] == '\r') {
+ strbuf_setlen(&buf, buf.len-2);
+ strbuf_addch(&buf, '\n');
+ }
- if (fwrite(buf, 1, len, output) != len)
+ if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
die_errno("cannot write output");
- len = read_line_with_nul(buf, sizeof(buf), mbox);
- if (len == 0) {
+ if (strbuf_getwholeline(&buf, mbox, '\n')) {
if (feof(mbox)) {
status = 1;
break;
}
die_errno("cannot read mbox");
}
- if (!is_partial && !is_bare && is_from_line(buf, len))
+ if (!is_bare && is_from_line(buf.buf, buf.len))
break; /* done with one message */
}
fclose(output);
@@ -166,7 +151,7 @@ static int split_maildir(const char *maildir, const char *dir,
goto out;
}
- if (fgets(buf, sizeof(buf), f) == NULL) {
+ if (strbuf_getwholeline(&buf, f, '\n')) {
error("cannot read mail %s (%s)", file, strerror(errno));
goto out;
}
@@ -203,7 +188,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
} while (isspace(peek));
ungetc(peek, f);
- if (fgets(buf, sizeof(buf), f) == NULL) {
+ if (strbuf_getwholeline(&buf, f, '\n')) {
/* empty stdin is OK */
if (f != stdin) {
error("cannot read mbox %s", file);
@@ -248,6 +233,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
nr = strtol(arg+2, NULL, 10);
} else if ( arg[1] == 'b' && !arg[2] ) {
allow_bare = 1;
+ } else if (!strcmp(arg, "--keep-cr")) {
+ keep_cr = 1;
} else if ( arg[1] == 'o' && arg[2] ) {
dir = arg+2;
} else if ( arg[1] == '-' && !arg[2] ) {
diff --git a/builtin-merge-base.c b/builtin-merge-base.c
index a6ec2f7ab..54e7ec223 100644
--- a/builtin-merge-base.c
+++ b/builtin-merge-base.c
@@ -23,7 +23,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
}
static const char * const merge_base_usage[] = {
- "git merge-base [--all] <commit-id> <commit-id>...",
+ "git merge-base [-a|--all] <commit> <commit>...",
NULL
};
diff --git a/builtin-merge.c b/builtin-merge.c
index f4de73fa9..b6b84286b 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -598,7 +598,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
discard_cache();
if (read_cache() < 0)
die("failed to read the cache");
- return -ret;
+ return ret;
}
}
diff --git a/builtin-mv.c b/builtin-mv.c
index b592c367b..1b20028c6 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -53,7 +53,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
struct option builtin_mv_options[] = {
OPT__DRY_RUN(&show_only),
- OPT_BOOLEAN('f', NULL, &force, "force move/rename even if target exists"),
+ OPT_BOOLEAN('f', "force", &force, "force move/rename even if target exists"),
OPT_BOOLEAN('k', NULL, &ignore_errors, "skip move/rename errors"),
OPT_END(),
};
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 9cc8a8451..7a390e1d4 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -1008,6 +1008,33 @@ static void add_preferred_base(unsigned char *sha1)
it->pcache.tree_size = size;
}
+static void cleanup_preferred_base(void)
+{
+ struct pbase_tree *it;
+ unsigned i;
+
+ it = pbase_tree;
+ pbase_tree = NULL;
+ while (it) {
+ struct pbase_tree *this = it;
+ it = this->next;
+ free(this->pcache.tree_data);
+ free(this);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pbase_tree_cache); i++) {
+ if (!pbase_tree_cache[i])
+ continue;
+ free(pbase_tree_cache[i]->tree_data);
+ free(pbase_tree_cache[i]);
+ pbase_tree_cache[i] = NULL;
+ }
+
+ free(done_pbase_paths);
+ done_pbase_paths = NULL;
+ done_pbase_paths_num = done_pbase_paths_alloc = 0;
+}
+
static void check_object(struct object_entry *entry)
{
if (entry->in_pack) {
@@ -1599,7 +1626,7 @@ static void *threaded_find_deltas(void *arg)
static void ll_find_deltas(struct object_entry **list, unsigned list_size,
int window, int depth, unsigned *processed)
{
- struct thread_params p[delta_search_threads];
+ struct thread_params *p;
int i, ret, active_threads = 0;
if (delta_search_threads <= 1) {
@@ -1609,6 +1636,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
if (progress > pack_to_stdout)
fprintf(stderr, "Delta compression using up to %d threads.\n",
delta_search_threads);
+ p = xcalloc(delta_search_threads, sizeof(*p));
/* Partition the work amongst work threads. */
for (i = 0; i < delta_search_threads; i++) {
@@ -1717,6 +1745,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
active_threads--;
}
}
+ free(p);
}
#else
@@ -1808,7 +1837,7 @@ static void prepare_pack(int window, int depth)
static int git_pack_config(const char *k, const char *v, void *cb)
{
- if(!strcmp(k, "pack.window")) {
+ if (!strcmp(k, "pack.window")) {
window = git_config_int(k, v);
return 0;
}
@@ -2098,6 +2127,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
int rp_ac_alloc = 64;
int rp_ac;
+ read_replace_refs = 0;
+
rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av));
rp_av[0] = "pack-objects";
@@ -2308,6 +2339,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
rp_av[rp_ac] = NULL;
get_object_list(rp_ac, rp_av);
}
+ cleanup_preferred_base();
if (include_tag && nr_result)
for_each_ref(add_ref_tag, NULL);
stop_progress(&progress_state);
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
index 00590b1c3..be99eb0ac 100644
--- a/builtin-prune-packed.c
+++ b/builtin-prune-packed.c
@@ -1,9 +1,12 @@
#include "builtin.h"
#include "cache.h"
#include "progress.h"
+#include "parse-options.h"
-static const char prune_packed_usage[] =
-"git prune-packed [-n] [-q]";
+static const char * const prune_packed_usage[] = {
+ "git prune-packed [-n|--dry-run] [-q|--quiet]",
+ NULL
+};
#define DRY_RUN 01
#define VERBOSE 02
@@ -68,24 +71,16 @@ void prune_packed_objects(int opts)
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
{
- int i;
int opts = VERBOSE;
+ const struct option prune_packed_options[] = {
+ OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
+ OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
+ OPT_END()
+ };
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ argc = parse_options(argc, argv, prefix, prune_packed_options,
+ prune_packed_usage, 0);
- if (*arg == '-') {
- if (!strcmp(arg, "-n"))
- opts |= DRY_RUN;
- else if (!strcmp(arg, "-q"))
- opts &= ~VERBOSE;
- else
- usage(prune_packed_usage);
- continue;
- }
- /* Handle arguments here .. */
- usage(prune_packed_usage);
- }
prune_packed_objects(opts);
return 0;
}
diff --git a/builtin-prune.c b/builtin-prune.c
index 0ed9cce4a..8459aec8e 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -140,6 +140,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
char *s;
save_commit_buffer = 0;
+ read_replace_refs = 0;
init_revisions(&revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
diff --git a/builtin-push.c b/builtin-push.c
index f8376cffa..3cb1ee46d 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -10,7 +10,7 @@
#include "parse-options.h"
static const char * const push_usage[] = {
- "git push [--all | --mirror] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
+ "git push [--all | --mirror] [-n | --dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
NULL,
};
@@ -157,7 +157,7 @@ static int do_push(const char *repo, int flags)
continue;
error("failed to push some refs to '%s'", url[i]);
- if (nonfastforward) {
+ if (nonfastforward && advice_push_nonfastforward) {
printf("To prevent you from losing history, non-fast-forward updates were rejected\n"
"Merge the remote changes before pushing again. See the 'non-fast forward'\n"
"section of 'git push --help' for details.\n");
@@ -175,13 +175,14 @@ int cmd_push(int argc, const char **argv, const char *prefix)
const char *repo = NULL; /* default repository */
struct option options[] = {
+ OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET),
OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
(TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
- OPT_BIT( 0 , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
+ OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
OPT_BIT( 0, "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 82e25eaa0..14c836b16 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -12,6 +12,7 @@
#include "unpack-trees.h"
#include "dir.h"
#include "builtin.h"
+#include "parse-options.h"
static int nr_trees;
static struct tree *trees[MAX_UNPACK_TREES];
@@ -29,7 +30,39 @@ static int list_tree(unsigned char *sha1)
return 0;
}
-static const char read_tree_usage[] = "git read-tree (<sha> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])";
+static const char * const read_tree_usage[] = {
+ "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
+ NULL
+};
+
+static int index_output_cb(const struct option *opt, const char *arg,
+ int unset)
+{
+ set_alternate_index_output(arg);
+ return 0;
+}
+
+static int exclude_per_directory_cb(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct dir_struct *dir;
+ struct unpack_trees_options *opts;
+
+ opts = (struct unpack_trees_options *)opt->value;
+
+ if (opts->dir)
+ die("more than one --exclude-per-directory given.");
+
+ dir = xcalloc(1, sizeof(*opts->dir));
+ dir->flags |= DIR_SHOW_IGNORED;
+ dir->exclude_per_dir = arg;
+ opts->dir = dir;
+ /* We do not need to nor want to do read-directory
+ * here; we are merely interested in reusing the
+ * per directory ignore stack mechanism.
+ */
+ return 0;
+}
static struct lock_file lock_file;
@@ -39,6 +72,34 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
unsigned char sha1[20];
struct tree_desc t[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
+ int prefix_set = 0;
+ const struct option read_tree_options[] = {
+ { OPTION_CALLBACK, 0, "index-output", NULL, "FILE",
+ "write resulting index to <FILE>",
+ PARSE_OPT_NONEG, index_output_cb },
+ OPT__VERBOSE(&opts.verbose_update),
+ OPT_GROUP("Merging"),
+ OPT_SET_INT('m', NULL, &opts.merge,
+ "perform a merge in addition to a read", 1),
+ OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
+ "3-way merge if no file level merging required", 1),
+ OPT_SET_INT(0, "aggressive", &opts.aggressive,
+ "3-way merge in presence of adds and removes", 1),
+ OPT_SET_INT(0, "reset", &opts.reset,
+ "same as -m, but discard unmerged entries", 1),
+ { OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/",
+ "read the tree into the index under <subdirectory>/",
+ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
+ OPT_SET_INT('u', NULL, &opts.update,
+ "update working tree with merge result", 1),
+ { OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
+ "gitignore",
+ "allow explicitly ignored files to be overwritten",
+ PARSE_OPT_NONEG, exclude_per_directory_cb },
+ OPT_SET_INT('i', NULL, &opts.index_only,
+ "don't check the working tree after merging", 1),
+ OPT_END()
+ };
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
@@ -49,105 +110,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
newfd = hold_locked_index(&lock_file, 1);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- /* "-u" means "update", meaning that a merge will update
- * the working tree.
- */
- if (!strcmp(arg, "-u")) {
- opts.update = 1;
- continue;
- }
+ argc = parse_options(argc, argv, unused_prefix, read_tree_options,
+ read_tree_usage, 0);
- if (!strcmp(arg, "-v")) {
- opts.verbose_update = 1;
- continue;
- }
+ prefix_set = opts.prefix ? 1 : 0;
+ if (1 < opts.merge + opts.reset + prefix_set)
+ die("Which one? -m, --reset, or --prefix?");
- /* "-i" means "index only", meaning that a merge will
- * not even look at the working tree.
- */
- if (!strcmp(arg, "-i")) {
- opts.index_only = 1;
- continue;
- }
-
- if (!prefixcmp(arg, "--index-output=")) {
- set_alternate_index_output(arg + 15);
- continue;
- }
-
- /* "--prefix=<subdirectory>/" means keep the current index
- * entries and put the entries from the tree under the
- * given subdirectory.
- */
- if (!prefixcmp(arg, "--prefix=")) {
- if (stage || opts.merge || opts.prefix)
- usage(read_tree_usage);
- opts.prefix = arg + 9;
- opts.merge = 1;
- stage = 1;
- if (read_cache_unmerged())
- die("you need to resolve your current index first");
- continue;
- }
-
- /* This differs from "-m" in that we'll silently ignore
- * unmerged entries and overwrite working tree files that
- * correspond to them.
- */
- if (!strcmp(arg, "--reset")) {
- if (stage || opts.merge || opts.prefix)
- usage(read_tree_usage);
- opts.reset = 1;
- opts.merge = 1;
- stage = 1;
- read_cache_unmerged();
- continue;
- }
-
- if (!strcmp(arg, "--trivial")) {
- opts.trivial_merges_only = 1;
- continue;
- }
-
- if (!strcmp(arg, "--aggressive")) {
- opts.aggressive = 1;
- continue;
- }
-
- /* "-m" stands for "merge", meaning we start in stage 1 */
- if (!strcmp(arg, "-m")) {
- if (stage || opts.merge || opts.prefix)
- usage(read_tree_usage);
- if (read_cache_unmerged())
- die("you need to resolve your current index first");
- stage = 1;
- opts.merge = 1;
- continue;
- }
-
- if (!prefixcmp(arg, "--exclude-per-directory=")) {
- struct dir_struct *dir;
-
- if (opts.dir)
- die("more than one --exclude-per-directory are given.");
-
- dir = xcalloc(1, sizeof(*opts.dir));
- dir->flags |= DIR_SHOW_IGNORED;
- dir->exclude_per_dir = arg + 24;
- opts.dir = dir;
- /* We do not need to nor want to do read-directory
- * here; we are merely interested in reusing the
- * per directory ignore stack mechanism.
- */
- continue;
- }
+ if (opts.reset || opts.merge || opts.prefix) {
+ if (read_cache_unmerged() && (opts.prefix || opts.merge))
+ die("You need to resolve your current index first");
+ stage = opts.merge = 1;
+ }
- /* using -u and -i at the same time makes no sense */
- if (1 < opts.index_only + opts.update)
- usage(read_tree_usage);
+ for (i = 0; i < argc; i++) {
+ const char *arg = argv[i];
if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg);
@@ -155,8 +132,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
die("failed to unpack tree object %s", arg);
stage++;
}
+ if (1 < opts.index_only + opts.update)
+ die("-u and -i at the same time makes no sense");
if ((opts.update||opts.index_only) && !opts.merge)
- usage(read_tree_usage);
+ die("%s is meaningless without -m, --reset, or --prefix",
+ opts.update ? "-u" : "-i");
if ((opts.dir && !opts.update))
die("--exclude-per-directory is meaningless unless -u");
if (opts.merge && !opts.index_only)
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 6ec1d056e..b771fe9b2 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -123,31 +123,6 @@ static struct command *commands;
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
-static int run_status(int code, const char *cmd_name)
-{
- switch (code) {
- case 0:
- return 0;
- case -ERR_RUN_COMMAND_FORK:
- return error("fork of %s failed", cmd_name);
- case -ERR_RUN_COMMAND_EXEC:
- return error("execute of %s failed", cmd_name);
- case -ERR_RUN_COMMAND_PIPE:
- return error("pipe failed");
- case -ERR_RUN_COMMAND_WAITPID:
- return error("waitpid failed");
- case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
- return error("waitpid is confused");
- case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
- return error("%s died of signal", cmd_name);
- case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
- return error("%s died strangely", cmd_name);
- default:
- error("%s exited with error code %d", cmd_name, -code);
- return -code;
- }
-}
-
static int run_receive_hook(const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
@@ -174,7 +149,7 @@ static int run_receive_hook(const char *hook_name)
code = start_command(&proc);
if (code)
- return run_status(code, hook_name);
+ return code;
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) {
size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@@ -186,7 +161,7 @@ static int run_receive_hook(const char *hook_name)
}
}
close(proc.in);
- return run_status(finish_command(&proc), hook_name);
+ return finish_command(&proc);
}
static int run_update_hook(struct command *cmd)
@@ -203,9 +178,8 @@ static int run_update_hook(struct command *cmd)
argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL;
- return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
- RUN_COMMAND_STDOUT_TO_STDERR),
- update_hook);
+ return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
+ RUN_COMMAND_STDOUT_TO_STDERR);
}
static int is_ref_checked_out(const char *ref)
@@ -419,7 +393,6 @@ static void run_update_post_hook(struct command *cmd)
argv[argc] = NULL;
status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
| RUN_COMMAND_STDOUT_TO_STDERR);
- run_status(status, update_post_hook);
}
static void execute_commands(const char *unpacker_error)
@@ -537,7 +510,6 @@ static const char *unpack(void)
code = run_command_v_opt(unpacker, RUN_GIT_CMD);
if (!code)
return NULL;
- run_status(code, unpacker[0]);
return "unpack-objects abnormal exit";
} else {
const char *keeper[7];
@@ -563,7 +535,6 @@ static const char *unpack(void)
ip.git_cmd = 1;
status = start_command(&ip);
if (status) {
- run_status(status, keeper[0]);
return "index-pack fork failed";
}
pack_lockfile = index_pack_lockfile(ip.out);
@@ -573,7 +544,6 @@ static const char *unpack(void)
reprepare_packed_git();
return NULL;
}
- run_status(status, keeper[0]);
return "index-pack abnormal exit";
}
}
diff --git a/builtin-reflog.c b/builtin-reflog.c
index 95198c5de..e23b5ef97 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -362,7 +362,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
} else if (cmd->updateref &&
(write_in_full(lock->lock_fd,
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
- write_in_full(lock->lock_fd, "\n", 1) != 1 ||
+ write_str_in_full(lock->lock_fd, "\n") != 1 ||
close_ref(lock) < 0)) {
status |= error("Couldn't write %s",
lock->lk->filename);
diff --git a/builtin-remote.c b/builtin-remote.c
index 008abfe09..0777dd719 100644
--- a/builtin-remote.c
+++ b/builtin-remote.c
@@ -385,7 +385,7 @@ static int get_head_names(const struct ref *remote_refs, struct ref_states *stat
get_fetch_map(remote_refs, &refspec, &fetch_map_tail, 0);
matches = guess_remote_head(find_ref_by_name(remote_refs, "HEAD"),
fetch_map, 1);
- for(ref = matches; ref; ref = ref->next)
+ for (ref = matches; ref; ref = ref->next)
string_list_append(abbrev_branch(ref->name), &states->heads);
free_refs(fetch_map);
@@ -484,7 +484,7 @@ static int read_remote_branches(const char *refname,
const char *symref;
strbuf_addf(&buf, "refs/remotes/%s", rename->old);
- if(!prefixcmp(refname, buf.buf)) {
+ if (!prefixcmp(refname, buf.buf)) {
item = string_list_append(xstrdup(refname), rename->remote_branches);
symref = resolve_ref(refname, orig_sha1, 1, &flag);
if (flag & REF_ISSYMREF)
diff --git a/builtin-replace.c b/builtin-replace.c
new file mode 100644
index 000000000..fe3a647a3
--- /dev/null
+++ b/builtin-replace.c
@@ -0,0 +1,159 @@
+/*
+ * Builtin "git replace"
+ *
+ * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
+ *
+ * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * and Carlos Rica <jasampler@gmail.com> that was itself based on
+ * git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "refs.h"
+#include "parse-options.h"
+
+static const char * const git_replace_usage[] = {
+ "git replace [-f] <object> <replacement>",
+ "git replace -d <object>...",
+ "git replace -l [<pattern>]",
+ NULL
+};
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+ int flag, void *cb_data)
+{
+ const char *pattern = cb_data;
+
+ if (!fnmatch(pattern, refname, 0))
+ printf("%s\n", refname);
+
+ return 0;
+}
+
+static int list_replace_refs(const char *pattern)
+{
+ if (pattern == NULL)
+ pattern = "*";
+
+ for_each_replace_ref(show_reference, (void *) pattern);
+
+ return 0;
+}
+
+typedef int (*each_replace_name_fn)(const char *name, const char *ref,
+ const unsigned char *sha1);
+
+static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
+{
+ const char **p;
+ char ref[PATH_MAX];
+ int had_error = 0;
+ unsigned char sha1[20];
+
+ for (p = argv; *p; p++) {
+ if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p)
+ >= sizeof(ref)) {
+ error("replace ref name too long: %.*s...", 50, *p);
+ had_error = 1;
+ continue;
+ }
+ if (!resolve_ref(ref, sha1, 1, NULL)) {
+ error("replace ref '%s' not found.", *p);
+ had_error = 1;
+ continue;
+ }
+ if (fn(*p, ref, sha1))
+ had_error = 1;
+ }
+ return had_error;
+}
+
+static int delete_replace_ref(const char *name, const char *ref,
+ const unsigned char *sha1)
+{
+ if (delete_ref(ref, sha1, 0))
+ return 1;
+ printf("Deleted replace ref '%s'\n", name);
+ return 0;
+}
+
+static int replace_object(const char *object_ref, const char *replace_ref,
+ int force)
+{
+ unsigned char object[20], prev[20], repl[20];
+ char ref[PATH_MAX];
+ struct ref_lock *lock;
+
+ if (get_sha1(object_ref, object))
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
+ if (get_sha1(replace_ref, repl))
+ die("Failed to resolve '%s' as a valid ref.", replace_ref);
+
+ if (snprintf(ref, sizeof(ref),
+ "refs/replace/%s",
+ sha1_to_hex(object)) > sizeof(ref) - 1)
+ die("replace ref name too long: %.*s...", 50, ref);
+ if (check_ref_format(ref))
+ die("'%s' is not a valid ref name.", ref);
+
+ if (!resolve_ref(ref, prev, 1, NULL))
+ hashclr(prev);
+ else if (!force)
+ die("replace ref '%s' already exists", ref);
+
+ lock = lock_any_ref_for_update(ref, prev, 0);
+ if (!lock)
+ die("%s: cannot lock the ref", ref);
+ if (write_ref_sha1(lock, repl, NULL) < 0)
+ die("%s: cannot update the ref", ref);
+
+ return 0;
+}
+
+int cmd_replace(int argc, const char **argv, const char *prefix)
+{
+ int list = 0, delete = 0, force = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('l', NULL, &list, "list replace refs"),
+ OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"),
+ OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
+
+ if (list && delete)
+ usage_msg_opt("-l and -d cannot be used together",
+ git_replace_usage, options);
+
+ if (force && (list || delete))
+ usage_msg_opt("-f cannot be used with -d or -l",
+ git_replace_usage, options);
+
+ /* Delete refs */
+ if (delete) {
+ if (argc < 1)
+ usage_msg_opt("-d needs at least one argument",
+ git_replace_usage, options);
+ return for_each_replace_name(argv, delete_replace_ref);
+ }
+
+ /* Replace object */
+ if (!list && argc) {
+ if (argc != 2)
+ usage_msg_opt("bad number of arguments",
+ git_replace_usage, options);
+ return replace_object(argv[0], argv[1], force);
+ }
+
+ /* List refs, even if "list" is not set */
+ if (argc > 1)
+ usage_msg_opt("only one pattern can be given with -l",
+ git_replace_usage, options);
+ if (force)
+ usage_msg_opt("-f needs some arguments",
+ git_replace_usage, options);
+
+ return list_replace_refs(argv[0]);
+}
diff --git a/builtin-reset.c b/builtin-reset.c
index 5fa1789d0..73e60223d 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -108,7 +108,8 @@ static int update_index_refresh(int fd, struct lock_file *index_lock, int flags)
if (read_cache() < 0)
return error("Could not read index");
- result = refresh_cache(flags) ? 1 : 0;
+ result = refresh_index(&the_index, (flags), NULL, NULL,
+ "Unstaged changes after reset:") ? 1 : 0;
if (write_cache(fd, active_cache, active_nr) ||
commit_locked_index(index_lock))
return error ("Could not refresh index");
@@ -142,6 +143,17 @@ static void update_index_from_diff(struct diff_queue_struct *q,
}
}
+static int interactive_reset(const char *revision, const char **argv,
+ const char *prefix)
+{
+ const char **pathspec = NULL;
+
+ if (*argv)
+ pathspec = get_pathspec(prefix, argv);
+
+ return run_add_interactive(revision, "--patch=reset", pathspec);
+}
+
static int read_from_tree(const char *prefix, const char **argv,
unsigned char *tree_sha1, int refresh_flags)
{
@@ -183,6 +195,7 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
+ int patch_mode = 0;
const char *rev = "HEAD";
unsigned char sha1[20], *orig = NULL, sha1_orig[20],
*old_orig = NULL, sha1_old_orig[20];
@@ -198,6 +211,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
"reset HEAD, index and working tree", MERGE),
OPT_BOOLEAN('q', NULL, &quiet,
"disable showing new HEAD in hard reset and progress message"),
+ OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
OPT_END()
};
@@ -251,6 +265,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die("Could not parse object '%s'.", rev);
hashcpy(sha1, commit->object.sha1);
+ if (patch_mode) {
+ if (reset_type != NONE)
+ die("--patch is incompatible with --{hard,mixed,soft}");
+ return interactive_reset(rev, argv + i, prefix);
+ }
+
/* git reset tree [--] paths... can be used to
* load chosen paths from the tree into the index without
* affecting the working tree nor HEAD. */
@@ -261,7 +281,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
die("Cannot do %s reset with paths.",
reset_type_names[reset_type]);
return read_from_tree(prefix, argv + i, sha1,
- quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
+ quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
}
if (reset_type == NONE)
reset_type = MIXED; /* by default */
@@ -302,7 +322,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
break;
case MIXED: /* Report what has not been updated. */
update_index_refresh(0, NULL,
- quiet ? REFRESH_QUIET : REFRESH_SAY_CHANGED);
+ quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN);
break;
}
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 47fb9f7ba..37e528e28 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -44,6 +44,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
NULL,
NULL,
NULL,
+ NULL,
};
struct child_process po;
int i;
@@ -53,6 +54,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
argv[i++] = "--thin";
if (args->use_ofs_delta)
argv[i++] = "--delta-base-offset";
+ if (args->quiet)
+ argv[i++] = "-q";
memset(&po, 0, sizeof(po));
po.argv = argv;
po.in = -1;
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 6a3812ee1..4d4a3c82d 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -56,7 +56,7 @@ static void insert_one_record(struct shortlog *log,
/* copy author name to namebuf, to support matching on both name and email */
memcpy(namebuf, author, boemail - author);
len = boemail - author;
- while(len > 0 && isspace(namebuf[len-1]))
+ while (len > 0 && isspace(namebuf[len-1]))
len--;
namebuf[len] = 0;
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 01bea3b58..3510a86e3 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -6,8 +6,8 @@
#include "parse-options.h"
static const char* show_branch_usage[] = {
- "git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base] [--topics] [--color] [<refs>...]",
- "--reflog[=n[,b]] [--list] [--color] <branch>",
+ "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
+ "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
NULL
};
@@ -665,7 +665,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
OPT_BOOLEAN(0, "sha1-name", &sha1_name,
"name commits with their object names"),
OPT_BOOLEAN(0, "merge-base", &merge_base,
- "act like git merge-base -a"),
+ "show possible merge bases"),
OPT_BOOLEAN(0, "independent", &independent,
"show refs unreachable from any other ref"),
OPT_BOOLEAN(0, "topo-order", &lifo,
diff --git a/builtin-tag.c b/builtin-tag.c
index a51a6d1ea..c4790185e 100644
--- a/builtin-tag.c
+++ b/builtin-tag.c
@@ -390,7 +390,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN('s', NULL, &sign, "annotated and GPG-signed tag"),
OPT_STRING('u', NULL, &keyid, "key-id",
"use another key to sign the tag"),
- OPT_BOOLEAN('f', NULL, &force, "replace the tag if exists"),
+ OPT_BOOLEAN('f', "force", &force, "replace the tag if exists"),
OPT_GROUP("Tag listing options"),
{
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 3d650a1c4..685566e0b 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -497,6 +497,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
int i;
unsigned char sha1[20];
+ read_replace_refs = 0;
+
git_config(git_default_config, NULL);
quiet = !isatty(2);
diff --git a/builtin-update-server-info.c b/builtin-update-server-info.c
new file mode 100644
index 000000000..2b3fddcc6
--- /dev/null
+++ b/builtin-update-server-info.c
@@ -0,0 +1,25 @@
+#include "cache.h"
+#include "builtin.h"
+#include "parse-options.h"
+
+static const char * const update_server_info_usage[] = {
+ "git update-server-info [--force]",
+ NULL
+};
+
+int cmd_update_server_info(int argc, const char **argv, const char *prefix)
+{
+ int force = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('f', "force", &force,
+ "update the info files from scratch"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options,
+ update_server_info_usage, 0);
+ if (argc > 0)
+ usage_with_options(update_server_info_usage, options);
+
+ return !!update_server_info(force);
+}
diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c
index a18df04cf..b6079ae6c 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -2,13 +2,18 @@
#include "cache.h"
#include "pack.h"
#include "pack-revindex.h"
+#include "parse-options.h"
#define MAX_CHAIN 50
-static void show_pack_info(struct packed_git *p)
+#define VERIFY_PACK_VERBOSE 01
+#define VERIFY_PACK_STAT_ONLY 02
+
+static void show_pack_info(struct packed_git *p, unsigned int flags)
{
uint32_t nr_objects, i;
int cnt;
+ int stat_only = flags & VERIFY_PACK_STAT_ONLY;
unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;
nr_objects = p->num_objects;
@@ -31,16 +36,19 @@ static void show_pack_info(struct packed_git *p)
type = packed_object_info_detail(p, offset, &size, &store_size,
&delta_chain_length,
base_sha1);
- printf("%s ", sha1_to_hex(sha1));
+ if (!stat_only)
+ printf("%s ", sha1_to_hex(sha1));
if (!delta_chain_length) {
- printf("%-6s %lu %lu %"PRIuMAX"\n",
- type, size, store_size, (uintmax_t)offset);
+ if (!stat_only)
+ printf("%-6s %lu %lu %"PRIuMAX"\n",
+ type, size, store_size, (uintmax_t)offset);
baseobjects++;
}
else {
- printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
- type, size, store_size, (uintmax_t)offset,
- delta_chain_length, sha1_to_hex(base_sha1));
+ if (!stat_only)
+ printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
+ type, size, store_size, (uintmax_t)offset,
+ delta_chain_length, sha1_to_hex(base_sha1));
if (delta_chain_length <= MAX_CHAIN)
chain_histogram[delta_chain_length]++;
else
@@ -65,10 +73,12 @@ static void show_pack_info(struct packed_git *p)
chain_histogram[0] > 1 ? "s" : "");
}
-static int verify_one_pack(const char *path, int verbose)
+static int verify_one_pack(const char *path, unsigned int flags)
{
char arg[PATH_MAX];
int len;
+ int verbose = flags & VERIFY_PACK_VERBOSE;
+ int stat_only = flags & VERIFY_PACK_STAT_ONLY;
struct packed_git *pack;
int err;
@@ -104,50 +114,53 @@ static int verify_one_pack(const char *path, int verbose)
return error("packfile %s not found.", arg);
install_packed_git(pack);
- err = verify_pack(pack);
- if (verbose) {
+ if (!stat_only)
+ err = verify_pack(pack);
+ else
+ err = open_pack_index(pack);
+
+ if (verbose || stat_only) {
if (err)
printf("%s: bad\n", pack->pack_name);
else {
- show_pack_info(pack);
- printf("%s: ok\n", pack->pack_name);
+ show_pack_info(pack, flags);
+ if (!stat_only)
+ printf("%s: ok\n", pack->pack_name);
}
}
return err;
}
-static const char verify_pack_usage[] = "git verify-pack [-v] <pack>...";
+static const char * const verify_pack_usage[] = {
+ "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
+ NULL
+};
int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
int err = 0;
- int verbose = 0;
- int no_more_options = 0;
- int nothing_done = 1;
+ unsigned int flags = 0;
+ int i;
+ const struct option verify_pack_options[] = {
+ OPT_BIT('v', "verbose", &flags, "verbose",
+ VERIFY_PACK_VERBOSE),
+ OPT_BIT('s', "stat-only", &flags, "show statistics only",
+ VERIFY_PACK_STAT_ONLY),
+ OPT_END()
+ };
git_config(git_default_config, NULL);
- while (1 < argc) {
- if (!no_more_options && argv[1][0] == '-') {
- if (!strcmp("-v", argv[1]))
- verbose = 1;
- else if (!strcmp("--", argv[1]))
- no_more_options = 1;
- else
- usage(verify_pack_usage);
- }
- else {
- if (verify_one_pack(argv[1], verbose))
- err = 1;
- discard_revindex();
- nothing_done = 0;
- }
- argc--; argv++;
+ argc = parse_options(argc, argv, prefix, verify_pack_options,
+ verify_pack_usage, 0);
+ if (argc < 1)
+ usage_with_options(verify_pack_usage, verify_pack_options);
+ for (i = 0; i < argc; i++) {
+ if (verify_one_pack(argv[i], flags))
+ err = 1;
+ discard_revindex();
}
- if (nothing_done)
- usage(verify_pack_usage);
-
return err;
}
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
index 7f7fda42f..9f482c29f 100644
--- a/builtin-verify-tag.c
+++ b/builtin-verify-tag.c
@@ -10,9 +10,12 @@
#include "tag.h"
#include "run-command.h"
#include <signal.h>
+#include "parse-options.h"
-static const char builtin_verify_tag_usage[] =
- "git verify-tag [-v|--verbose] <tag>...";
+static const char * const verify_tag_usage[] = {
+ "git verify-tag [-v|--verbose] <tag>...",
+ NULL
+};
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
@@ -89,17 +92,17 @@ static int verify_tag(const char *name, int verbose)
int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
+ const struct option verify_tag_options[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_END()
+ };
git_config(git_default_config, NULL);
- if (argc > 1 &&
- (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) {
- verbose = 1;
- i++;
- }
-
+ argc = parse_options(argc, argv, prefix, verify_tag_options,
+ verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
if (argc <= i)
- usage(builtin_verify_tag_usage);
+ usage_with_options(verify_tag_usage, verify_tag_options);
/* sometimes the program was terminated because this signal
* was received in the process of writing the gpg input: */
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 3a24ce815..b223af416 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -7,9 +7,12 @@
#include "cache.h"
#include "tree.h"
#include "cache-tree.h"
+#include "parse-options.h"
-static const char write_tree_usage[] =
-"git write-tree [--missing-ok] [--prefix=<prefix>/]";
+static const char * const write_tree_usage[] = {
+ "git write-tree [--missing-ok] [--prefix=<prefix>/]",
+ NULL
+};
int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
{
@@ -17,27 +20,22 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
const char *prefix = NULL;
unsigned char sha1[20];
const char *me = "git-write-tree";
+ struct option write_tree_options[] = {
+ OPT_BIT(0, "missing-ok", &flags, "allow missing objects",
+ WRITE_TREE_MISSING_OK),
+ { OPTION_STRING, 0, "prefix", &prefix, "<prefix>/",
+ "write tree object for a subdirectory <prefix>" ,
+ PARSE_OPT_LITERAL_ARGHELP },
+ { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
+ "only useful for debugging",
+ PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,
+ WRITE_TREE_IGNORE_CACHE_TREE },
+ OPT_END()
+ };
git_config(git_default_config, NULL);
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--missing-ok"))
- flags |= WRITE_TREE_MISSING_OK;
- else if (!prefixcmp(arg, "--prefix="))
- prefix = arg + 9;
- else if (!prefixcmp(arg, "--ignore-cache-tree"))
- /*
- * This is only useful for debugging, so I
- * do not bother documenting it.
- */
- flags |= WRITE_TREE_IGNORE_CACHE_TREE;
- else
- usage(write_tree_usage);
- argc--; argv++;
- }
-
- if (argc > 2)
- die("too many options");
+ argc = parse_options(argc, argv, unused_prefix, write_tree_options,
+ write_tree_usage, 0);
ret = write_cache_as_tree(sha1, flags, prefix);
switch (ret) {
diff --git a/builtin.h b/builtin.h
index 20427d296..a2174dc85 100644
--- a/builtin.h
+++ b/builtin.h
@@ -13,7 +13,6 @@ extern const char git_more_info_string[];
extern void list_common_cmds_help(void);
extern const char *help_unknown_cmd(const char *cmd);
extern void prune_packed_objects(int);
-extern int read_line_with_nul(char *buf, int size, FILE *file);
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
struct strbuf *out);
extern int commit_tree(const char *msg, unsigned char *tree,
@@ -103,6 +102,7 @@ extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
extern int cmd_unpack_objects(int argc, const char **argv, const char *prefix);
extern int cmd_update_index(int argc, const char **argv, const char *prefix);
extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_update_server_info(int argc, const char **argv, const char *prefix);
extern int cmd_upload_archive(int argc, const char **argv, const char *prefix);
extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
extern int cmd_verify_tag(int argc, const char **argv, const char *prefix);
@@ -112,5 +112,6 @@ extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
+extern int cmd_replace(int argc, const char **argv, const char *prefix);
#endif
diff --git a/bundle.c b/bundle.c
index e4b2aa9c4..df95e151e 100644
--- a/bundle.c
+++ b/bundle.c
@@ -234,7 +234,7 @@ int create_bundle(struct bundle_header *header, const char *path,
rls.git_cmd = 1;
if (start_command(&rls))
return -1;
- rls_fout = fdopen(rls.out, "r");
+ rls_fout = xfdopen(rls.out, "r");
while (fgets(buffer, sizeof(buffer), rls_fout)) {
unsigned char sha1[20];
if (buffer[0] == '-') {
diff --git a/cache.h b/cache.h
index 2c2f05c3e..1a6412dfd 100644
--- a/cache.h
+++ b/cache.h
@@ -4,6 +4,7 @@
#include "git-compat-util.h"
#include "strbuf.h"
#include "hash.h"
+#include "advice.h"
#include SHA1_HEADER
#ifndef git_SHA_CTX
@@ -330,7 +331,7 @@ static inline void remove_name_hash(struct cache_entry *ce)
#define remove_file_from_cache(path) remove_file_from_index(&the_index, (path))
#define add_to_cache(path, st, flags) add_to_index(&the_index, (path), (st), (flags))
#define add_file_to_cache(path, flags) add_file_to_index(&the_index, (path), (flags))
-#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL)
+#define refresh_cache(flags) refresh_index(&the_index, (flags), NULL, NULL, NULL)
#define ce_match_stat(ce, st, options) ie_match_stat(&the_index, (ce), (st), (options))
#define ce_modified(ce, st, options) ie_modified(&the_index, (ce), (st), (options))
#define cache_name_exists(name, namelen, igncase) index_name_exists(&the_index, (name), (namelen), (igncase))
@@ -476,8 +477,8 @@ extern int check_path(const char *path, int len, struct stat *st, int skiplen);
#define REFRESH_QUIET 0x0004 /* be quiet about it */
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
-#define REFRESH_SAY_CHANGED 0x0020 /* say "changed" not "needs update" */
-extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen);
+#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
+extern int refresh_index(struct index_state *, unsigned int flags, const char **pathspec, char *seen, char *header_msg);
struct lock_file {
struct lock_file *next;
@@ -512,6 +513,7 @@ extern int log_all_ref_updates;
extern int warn_ambiguous_refs;
extern int shared_repository;
extern const char *apply_default_whitespace;
+extern const char *apply_default_ignorewhitespace;
extern int zlib_compression_level;
extern int core_compression_level;
extern int core_compression_seen;
@@ -519,6 +521,7 @@ extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
extern int auto_crlf;
+extern int read_replace_refs;
extern int fsync_object_files;
extern int core_preload_index;
@@ -655,7 +658,11 @@ char *strip_path_suffix(const char *path, const char *suffix);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
-extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
+extern void *read_sha1_file_repl(const unsigned char *sha1, enum object_type *type, unsigned long *size, const unsigned char **replacement);
+static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+{
+ return read_sha1_file_repl(sha1, type, size, NULL);
+}
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
@@ -725,9 +732,14 @@ enum date_mode {
};
const char *show_date(unsigned long time, int timezone, enum date_mode mode);
+const char *show_date_relative(unsigned long time, int tz,
+ const struct timeval *now,
+ char *timebuf,
+ size_t timebuf_size);
int parse_date(const char *date, char *buf, int bufsize);
void datestamp(char *buf, int bufsize);
unsigned long approxidate(const char *);
+unsigned long approxidate_relative(const char *date, const struct timeval *now);
enum date_mode parse_date_format(const char *format);
#define IDENT_WARN_ON_NO_NAME 1
@@ -912,13 +924,19 @@ extern const char *git_mailmap_file;
extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode);
-extern ssize_t read_in_full(int fd, void *buf, size_t count);
-extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+extern int copy_file_with_time(const char *dst, const char *src, int mode);
extern void write_or_die(int fd, const void *buf, size_t count);
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
extern void fsync_or_die(int fd, const char *);
+extern ssize_t read_in_full(int fd, void *buf, size_t count);
+extern ssize_t write_in_full(int fd, const void *buf, size_t count);
+static inline ssize_t write_str_in_full(int fd, const char *str)
+{
+ return write_in_full(fd, str, strlen(str));
+}
+
/* pager.c */
extern void setup_pager(void);
extern const char *pager_program;
diff --git a/commit.c b/commit.c
index e2bcbe814..fedbd5e52 100644
--- a/commit.c
+++ b/commit.c
@@ -212,7 +212,7 @@ int write_shallow_commits(int fd, int use_pack_protocol)
else {
if (write_in_full(fd, hex, 40) != 40)
break;
- if (write_in_full(fd, "\n", 1) != 1)
+ if (write_str_in_full(fd, "\n") != 1)
break;
}
}
@@ -564,13 +564,13 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
while (interesting(list)) {
struct commit *commit;
struct commit_list *parents;
- struct commit_list *n;
+ struct commit_list *next;
int flags;
commit = list->item;
- n = list->next;
+ next = list->next;
free(list);
- list = n;
+ list = next;
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
@@ -598,11 +598,11 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
free_commit_list(list);
list = result; result = NULL;
while (list) {
- struct commit_list *n = list->next;
+ struct commit_list *next = list->next;
if (!(list->item->object.flags & STALE))
insert_by_date(list->item, &result);
free(list);
- list = n;
+ list = next;
}
return result;
}
diff --git a/commit.h b/commit.h
index 4886544b6..f4fc5c558 100644
--- a/commit.h
+++ b/commit.h
@@ -123,6 +123,8 @@ struct commit_graft *read_graft_line(char *buf, int len);
int register_commit_graft(struct commit_graft *, int);
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
+const unsigned char *lookup_replace_object(const unsigned char *sha1);
+
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
@@ -138,6 +140,8 @@ int is_descendant_of(struct commit *, struct commit_list *);
int in_merge_bases(struct commit *, struct commit **, int);
extern int interactive_add(int argc, const char **argv, const char *prefix);
+extern int run_add_interactive(const char *revision, const char *patch_mode,
+ const char **pathspec);
static inline int single_parent(struct commit *commit)
{
diff --git a/compat/bswap.h b/compat/bswap.h
new file mode 100644
index 000000000..7246a12c6
--- /dev/null
+++ b/compat/bswap.h
@@ -0,0 +1,36 @@
+/*
+ * Let's make sure we always have a sane definition for ntohl()/htonl().
+ * Some libraries define those as a function call, just to perform byte
+ * shifting, bringing significant overhead to what should be a simple
+ * operation.
+ */
+
+/*
+ * Default version that the compiler ought to optimize properly with
+ * constant values.
+ */
+static inline unsigned int default_swab32(unsigned int val)
+{
+ return (((val & 0xff000000) >> 24) |
+ ((val & 0x00ff0000) >> 8) |
+ ((val & 0x0000ff00) << 8) |
+ ((val & 0x000000ff) << 24));
+}
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#define bswap32(x) ({ \
+ unsigned int __res; \
+ if (__builtin_constant_p(x)) { \
+ __res = default_swab32(x); \
+ } else { \
+ __asm__("bswap %0" : "=r" (__res) : "0" (x)); \
+ } \
+ __res; })
+
+#undef ntohl
+#undef htonl
+#define ntohl(x) bswap32(x)
+#define htonl(x) bswap32(x)
+
+#endif
diff --git a/compat/mingw.c b/compat/mingw.c
index bed417875..36ef8d321 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -824,7 +824,7 @@ void mingw_execvp(const char *cmd, char *const *argv)
free_path_split(path);
}
-char **copy_environ()
+static char **copy_environ(void)
{
char **env;
int i = 0;
@@ -861,7 +861,7 @@ static int lookup_env(char **env, const char *name, size_t nmln)
/*
* If name contains '=', then sets the variable, otherwise it unsets it
*/
-char **env_setenv(char **env, const char *name)
+static char **env_setenv(char **env, const char *name)
{
char *eq = strchrnul(name, '=');
int i = lookup_env(env, name, eq-name);
@@ -886,6 +886,18 @@ char **env_setenv(char **env, const char *name)
return env;
}
+/*
+ * Copies global environ and adjusts variables as specified by vars.
+ */
+char **make_augmented_environ(const char *const *vars)
+{
+ char **env = copy_environ();
+
+ while (*vars)
+ env = env_setenv(env, *vars++);
+ return env;
+}
+
/* this is the first function to call into WS_32; initialize it */
#undef gethostbyname
struct hostent *mingw_gethostbyname(const char *host)
diff --git a/compat/mingw.h b/compat/mingw.h
index c1859c548..c43917cd6 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -17,9 +17,10 @@ typedef int pid_t;
#define S_IROTH 0
#define S_IXOTH 0
-#define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */
+#define WIFEXITED(x) 1
+#define WIFSIGNALED(x) 0
#define WEXITSTATUS(x) ((x) & 0xff)
-#define WIFSIGNALED(x) ((unsigned)(x) > 259)
+#define WTERMSIG(x) SIGTERM
#define SIGHUP 1
#define SIGQUIT 3
@@ -221,9 +222,8 @@ void mingw_open_html(const char *path);
* helpers
*/
-char **copy_environ(void);
+char **make_augmented_environ(const char *const *vars);
void free_environ(char **env);
-char **env_setenv(char **env, const char *name);
/*
* A replacement of main() that ensures that argv[0] has a path
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 6c0fb056a..4d07087ab 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -3,7 +3,8 @@
/*
* The size parameter specifies the available space, i.e. includes
* the trailing NUL byte; but Windows's vsnprintf expects the
- * number of characters to write without the trailing NUL.
+ * number of characters to write, and does not necessarily write the
+ * trailing NUL.
*/
#ifndef SNPRINTF_SIZE_CORR
#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4
diff --git a/config.c b/config.c
index e87edeab0..c64406113 100644
--- a/config.c
+++ b/config.c
@@ -627,6 +627,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
if (!prefixcmp(var, "mailmap."))
return git_default_mailmap_config(var, value);
+ if (!prefixcmp(var, "advice."))
+ return git_default_advice_config(var, value);
+
if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
pager_use_color = git_config_bool(var,value);
return 0;
@@ -1116,7 +1119,7 @@ int git_config_set_multivar(const char *key, const char *value,
copy_end - copy_begin)
goto write_err_out;
if (new_line &&
- write_in_full(fd, "\n", 1) != 1)
+ write_str_in_full(fd, "\n") != 1)
goto write_err_out;
}
copy_begin = store.offset[i];
diff --git a/configure.ac b/configure.ac
index 3f1922d0b..b09b8e446 100644
--- a/configure.ac
+++ b/configure.ac
@@ -156,19 +156,11 @@ AC_MSG_NOTICE([CHECKS for site configuration])
# tests. These tests take up a significant amount of the total test time
# but are not needed unless you plan to talk to SVN repos.
#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
#
# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
diff --git a/connect.c b/connect.c
index 76e542776..7945e38ac 100644
--- a/connect.c
+++ b/connect.c
@@ -513,7 +513,7 @@ struct child_process *git_connect(int fd[2], const char *url_orig,
signal(SIGCHLD, SIG_DFL);
host = strstr(url, "://");
- if(host) {
+ if (host) {
*host = '\0';
protocol = get_protocol(url);
host += 3;
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 745b5fb78..2d2d5794a 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -318,13 +318,9 @@ __git_remotes ()
echo ${i#$d/remotes/}
done
[ "$ngoff" ] && shopt -u nullglob
- for i in $(git --git-dir="$d" config --list); do
- case "$i" in
- remote.*.url=*)
- i="${i#remote.}"
- echo "${i/.url=*/}"
- ;;
- esac
+ for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
+ i="${i#remote.}"
+ echo "${i/.url*/}"
done
}
@@ -605,13 +601,9 @@ __git_porcelain_commandlist="$(__git_porcelain_commands 2>/dev/null)"
__git_aliases ()
{
local i IFS=$'\n'
- for i in $(git --git-dir="$(__gitdir)" config --list); do
- case "$i" in
- alias.*)
- i="${i#alias.}"
- echo "${i/=*/}"
- ;;
- esac
+ for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
+ i="${i#alias.}"
+ echo "${i/ */}"
done
}
@@ -674,6 +666,7 @@ _git_am ()
--*)
__gitcomp "
--3way --committer-date-is-author-date --ignore-date
+ --ignore-whitespace --ignore-space-change
--interactive --keep --no-utf8 --signoff --utf8
--whitespace=
"
@@ -695,6 +688,7 @@ _git_apply ()
--stat --numstat --summary --check --index
--cached --index-info --reverse --reject --unidiff-zero
--apply --no-add --exclude=
+ --ignore-whitespace --ignore-space-change
--whitespace= --inaccurate-eof --verbose
"
return
@@ -1047,6 +1041,7 @@ _git_grep ()
--extended-regexp --basic-regexp --fixed-strings
--files-with-matches --name-only
--files-without-match
+ --max-depth
--count
--and --or --not --all-match
"
@@ -1529,13 +1524,14 @@ _git_config ()
url.*.*)
local pfx="${cur%.*}."
cur="${cur##*.}"
- __gitcomp "insteadof" "$pfx" "$cur"
+ __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
return
;;
esac
__gitcomp "
add.ignore-errors
alias.
+ apply.ignorewhitespace
apply.whitespace
branch.autosetupmerge
branch.autosetuprebase
@@ -1765,13 +1761,9 @@ _git_remote ()
;;
update)
local i c='' IFS=$'\n'
- for i in $(git --git-dir="$(__gitdir)" config --list); do
- case "$i" in
- remotes.*)
- i="${i#remotes.}"
- c="$c ${i/=*/}"
- ;;
- esac
+ for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
+ i="${i#remotes.}"
+ c="$c ${i/ */}"
done
__gitcomp "$c"
;;
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index eace9c18e..214930a02 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -429,16 +429,19 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)."
(git-get-string-sha1
(git-call-process-string-display-error "write-tree"))))
-(defun git-commit-tree (buffer tree head)
- "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
+(defun git-commit-tree (buffer tree parent)
+ "Create a commit and possibly update HEAD.
+Create a commit with the message in BUFFER using the tree with hash TREE.
+Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\",
+update the \"HEAD\" reference to the new commit."
(let ((author-name (git-get-committer-name))
(author-email (git-get-committer-email))
(subject "commit (initial): ")
author-date log-start log-end args coding-system-for-write)
- (when head
+ (when parent
(setq subject "commit: ")
(push "-p" args)
- (push head args))
+ (push parent args))
(with-current-buffer buffer
(goto-char (point-min))
(if
@@ -474,7 +477,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)."
(apply #'git-run-command-region
buffer log-start log-end env
"commit-tree" tree (nreverse args))))))
- (when commit (git-update-ref "HEAD" commit head subject))
+ (when commit (git-update-ref "HEAD" commit parent subject))
commit)))
(defun git-empty-db-p ()
@@ -1043,7 +1046,7 @@ The FILES list must be sorted."
(defun git-add-file ()
"Add marked file(s) to the index cache."
(interactive)
- (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
+ (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored 'unmerged))))
;; FIXME: add support for directories
(unless files
(push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
@@ -1116,15 +1119,6 @@ The FILES list must be sorted."
(when buffer (with-current-buffer buffer (revert-buffer t t t)))))
(git-success-message "Reverted" names))))))
-(defun git-resolve-file ()
- "Resolve conflicts in marked file(s)."
- (interactive)
- (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
- (when files
- (when (apply 'git-call-process-display-error "update-index" "--" files)
- (git-update-status-files files)
- (git-success-message "Resolved" files)))))
-
(defun git-remove-handled ()
"Remove handled files from the status list."
(interactive)
@@ -1553,7 +1547,6 @@ amended version of it."
(define-key map "P" 'git-prev-unmerged-file)
(define-key map "q" 'git-status-quit)
(define-key map "r" 'git-remove-file)
- (define-key map "R" 'git-resolve-file)
(define-key map "t" toggle-map)
(define-key map "T" 'git-toggle-all-marks)
(define-key map "u" 'git-unmark-file)
@@ -1595,7 +1588,6 @@ amended version of it."
("Merge"
["Next Unmerged File" git-next-unmerged-file t]
["Prev Unmerged File" git-prev-unmerged-file t]
- ["Mark as Resolved" git-resolve-file t]
["Interactive Merge File" git-find-file-imerge t]
["Diff Against Common Base File" git-diff-file-base t]
["Diff Combined" git-diff-file-combined t]
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 342529db3..e710219ca 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -8,12 +8,10 @@
# License: MIT <http://www.opensource.org/licenses/mit-license.php>
#
-import optparse, sys, os, marshal, popen2, subprocess, shelve
-import tempfile, getopt, sha, os.path, time, platform
+import optparse, sys, os, marshal, subprocess, shelve
+import tempfile, getopt, os.path, time, platform
import re
-from sets import Set;
-
verbose = False
@@ -201,7 +199,7 @@ def isModeExec(mode):
def isModeExecChanged(src_mode, dst_mode):
return isModeExec(src_mode) != isModeExec(dst_mode)
-def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
cmd = p4_build_cmd("-G %s" % (cmd))
if verbose:
sys.stderr.write("Opening pipe: %s\n" % cmd)
@@ -224,7 +222,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
try:
while True:
entry = marshal.load(p4.stdout)
- result.append(entry)
+ if cb is not None:
+ cb(entry)
+ else:
+ result.append(entry)
except EOFError:
pass
exitCode = p4.wait()
@@ -861,8 +862,8 @@ class P4Sync(Command):
self.usage += " //depot/path[@revRange]"
self.silent = False
- self.createdBranches = Set()
- self.committedChanges = Set()
+ self.createdBranches = set()
+ self.committedChanges = set()
self.branch = ""
self.detectBranches = False
self.detectLabels = False
@@ -950,10 +951,84 @@ class P4Sync(Command):
return branches
- ## Should move this out, doesn't use SELF.
- def readP4Files(self, files):
+ # output one file from the P4 stream
+ # - helper for streamP4Files
+
+ def streamOneP4File(self, file, contents):
+ if file["type"] == "apple":
+ print "\nfile %s is a strange apple file that forks. Ignoring" % \
+ file['depotFile']
+ return
+
+ relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+ if verbose:
+ sys.stderr.write("%s\n" % relPath)
+
+ mode = "644"
+ if isP4Exec(file["type"]):
+ mode = "755"
+ elif file["type"] == "symlink":
+ mode = "120000"
+ # p4 print on a symlink contains "target\n", so strip it off
+ last = contents.pop()
+ last = last[:-1]
+ contents.append(last)
+
+ if self.isWindows and file["type"].endswith("text"):
+ mangled = []
+ for data in contents:
+ data = data.replace("\r\n", "\n")
+ mangled.append(data)
+ contents = mangled
+
+ if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+ contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
+ elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+ contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
+
+ self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+
+ # total length...
+ length = 0
+ for d in contents:
+ length = length + len(d)
+
+ self.gitStream.write("data %d\n" % length)
+ for d in contents:
+ self.gitStream.write(d)
+ self.gitStream.write("\n")
+
+ def streamOneP4Deletion(self, file):
+ relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
+ if verbose:
+ sys.stderr.write("delete %s\n" % relPath)
+ self.gitStream.write("D %s\n" % relPath)
+
+ # handle another chunk of streaming data
+ def streamP4FilesCb(self, marshalled):
+
+ if marshalled.has_key('depotFile') and self.stream_have_file_info:
+ # start of a new file - output the old one first
+ self.streamOneP4File(self.stream_file, self.stream_contents)
+ self.stream_file = {}
+ self.stream_contents = []
+ self.stream_have_file_info = False
+
+ # pick up the new file information... for the
+ # 'data' field we need to append to our array
+ for k in marshalled.keys():
+ if k == 'data':
+ self.stream_contents.append(marshalled['data'])
+ else:
+ self.stream_file[k] = marshalled[k]
+
+ self.stream_have_file_info = True
+
+ # Stream directly from "p4 files" into "git fast-import"
+ def streamP4Files(self, files):
filesForCommit = []
filesToRead = []
+ filesToDelete = []
for f in files:
includeFile = True
@@ -967,50 +1042,35 @@ class P4Sync(Command):
filesForCommit.append(f)
if f['action'] not in ('delete', 'purge'):
filesToRead.append(f)
+ else:
+ filesToDelete.append(f)
- filedata = []
- if len(filesToRead) > 0:
- filedata = p4CmdList('-x - print',
- stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
- for f in filesToRead]),
- stdin_mode='w+')
-
- if "p4ExitCode" in filedata[0]:
- die("Problems executing p4. Error: [%d]."
- % (filedata[0]['p4ExitCode']));
-
- j = 0;
- contents = {}
- while j < len(filedata):
- stat = filedata[j]
- j += 1
- text = ''
- while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
- text += filedata[j]['data']
- del filedata[j]['data']
- j += 1
-
- if not stat.has_key('depotFile'):
- sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
- continue
+ # deleted files...
+ for f in filesToDelete:
+ self.streamOneP4Deletion(f)
- if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
- text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
- elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
- text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
+ if len(filesToRead) > 0:
+ self.stream_file = {}
+ self.stream_contents = []
+ self.stream_have_file_info = False
- contents[stat['depotFile']] = text
+ # curry self argument
+ def streamP4FilesCbSelf(entry):
+ self.streamP4FilesCb(entry)
- for f in filesForCommit:
- path = f['path']
- if contents.has_key(path):
- f['data'] = contents[path]
+ p4CmdList("-x - print",
+ '\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in filesToRead]),
+ cb=streamP4FilesCbSelf)
- return filesForCommit
+ # do the last chunk
+ if self.stream_file.has_key('depotFile'):
+ self.streamOneP4File(self.stream_file, self.stream_contents)
def commit(self, details, files, branch, branchPrefixes, parent = ""):
epoch = details["time"]
author = details["user"]
+ self.branchPrefixes = branchPrefixes
if self.verbose:
print "commit into %s" % branch
@@ -1023,7 +1083,6 @@ class P4Sync(Command):
new_files.append (f)
else:
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
- files = self.readP4Files(new_files)
self.gitStream.write("commit %s\n" % branch)
# gitStream.write("mark :%s\n" % details["change"])
@@ -1051,33 +1110,7 @@ class P4Sync(Command):
print "parent %s" % parent
self.gitStream.write("from %s\n" % parent)
- for file in files:
- if file["type"] == "apple":
- print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
- continue
-
- relPath = self.stripRepoPath(file['path'], branchPrefixes)
- if file["action"] in ("delete", "purge"):
- self.gitStream.write("D %s\n" % relPath)
- else:
- data = file['data']
-
- mode = "644"
- if isP4Exec(file["type"]):
- mode = "755"
- elif file["type"] == "symlink":
- mode = "120000"
- # p4 print on a symlink contains "target\n", so strip it off
- data = data[:-1]
-
- if self.isWindows and file["type"].endswith("text"):
- data = data.replace("\r\n", "\n")
-
- self.gitStream.write("M %s inline %s\n" % (mode, relPath))
- self.gitStream.write("data %s\n" % len(data))
- self.gitStream.write(data)
- self.gitStream.write("\n")
-
+ self.streamP4Files(new_files)
self.gitStream.write("\n")
change = int(details["change"])
@@ -1627,7 +1660,7 @@ class P4Sync(Command):
if len(self.changesFile) > 0:
output = open(self.changesFile).readlines()
- changeSet = Set()
+ changeSet = set()
for line in output:
changeSet.add(int(line))
diff --git a/contrib/fast-import/import-directories.perl b/contrib/fast-import/import-directories.perl
new file mode 100755
index 000000000..5782d80e2
--- /dev/null
+++ b/contrib/fast-import/import-directories.perl
@@ -0,0 +1,416 @@
+#!/usr/bin/perl -w
+#
+# Copyright 2008-2009 Peter Krefting <peter@softwolves.pp.se>
+#
+# ------------------------------------------------------------------------
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# ------------------------------------------------------------------------
+
+=pod
+
+=head1 NAME
+
+import-directories - Import bits and pieces to Git.
+
+=head1 SYNOPSIS
+
+B<import-directories.perl> F<configfile> F<outputfile>
+
+=head1 DESCRIPTION
+
+Script to import arbitrary projects version controlled by the "copy the
+source directory to a new location and edit it there"-version controlled
+projects into version control. Handles projects with arbitrary branching
+and version trees, taking a file describing the inputs and generating a
+file compatible with the L<git-fast-import(1)> format.
+
+=head1 CONFIGURATION FILE
+
+=head2 Format
+
+The configuration file is based on the standard I<.ini> format.
+
+ ; Comments start with semi-colons
+ [section]
+ key=value
+
+Please see below for information on how to escape special characters.
+
+=head2 Global configuration
+
+Global configuration is done in the B<[config]> section, which should be
+the first section in the file. Configuration can be changed by
+repeating configuration sections later on.
+
+ [config]
+ ; configure conversion of CRLFs. "convert" means that all CRLFs
+ ; should be converted into LFs (suitable for the core.autocrlf
+ ; setting set to true in Git). "none" means that all data is
+ ; treated as binary.
+ crlf=convert
+
+=head2 Revision configuration
+
+Each revision that is to be imported is described in three
+sections. Revisions should be defined in topological order, so
+that a revision's parent has always been defined when a new revision
+is introduced. All the sections for one revision must be defined
+before defining the next revision.
+
+Each revision is assigned a unique numerical identifier. The
+numbers do not need to be consecutive, nor monotonically
+increasing.
+
+For instance, if your configuration file contains only the two
+revisions 4711 and 42, where 4711 is the initial commit, the
+only requirement is that 4711 is completely defined before 42.
+
+=pod
+
+=head3 Revision description section
+
+A section whose section name is just an integer gives meta-data
+about the revision.
+
+ [3]
+ ; author sets the author of the revisions
+ author=Peter Krefting <peter@softwolves.pp.se>
+ ; branch sets the branch that the revision should be committed to
+ branch=master
+ ; parent describes the revision that is the parent of this commit
+ ; (optional)
+ parent=1
+ ; merges describes a revision that is merged into this commit
+ ; (optional; can be repeated)
+ merges=2
+ ; selects one file to take the timestamp from
+ ; (optional; if unspecified, the most recent file from the .files
+ ; section is used)
+ timestamp=3/source.c
+
+=head3 Revision contents section
+
+A section whose section name is an integer followed by B<.files>
+describe all the files included in this revision. If a file that
+was available previously is not included in this revision, it will
+be removed.
+
+If an on-disk revision is incomplete, you can point to files from
+a previous revision. There are no restriction as to where the source
+files are located, nor to the names of them.
+
+ [3.files]
+ ; the key is the path inside the repository, the value is the path
+ ; as seen from the importer script.
+ source.c=ver-3.00/source.c
+ source.h=ver-2.99/source.h
+ readme.txt=ver-3.00/introduction to the project.txt
+
+File names are treated as byte strings (but please see below on
+quoting rules), and should be stored in the configuration file in
+the encoding that should be used in the generated repository.
+
+=head3 Revision commit message section
+
+A section whose section name is an integer followed by B<.message>
+gives the commit message. This section is read verbatim, up until
+the beginning of the next section. As such, a commit message may not
+contain a line that begins with an opening square bracket ("[") and
+ends with a closing square bracket ("]"), unless they are surrounded
+by whitespace or other characters.
+
+ [3.message]
+ Implement foobar.
+ ; trailing blank lines are ignored.
+
+=cut
+
+# Globals
+use strict;
+use integer;
+my $crlfmode = 0;
+my @revs;
+my (%revmap, %message, %files, %author, %branch, %parent, %merges, %time, %timesource);
+my $sectiontype = 0;
+my $rev = 0;
+my $mark = 1;
+
+# Check command line
+if ($#ARGV < 1 || $ARGV[0] =~ /^--?h/)
+{
+ exec('perldoc', $0);
+ exit 1;
+}
+
+# Open configuration
+my $config = $ARGV[0];
+open CFG, '<', $config or die "Cannot open configuration file \"$config\": ";
+
+# Open output
+my $output = $ARGV[1];
+open OUT, '>', $output or die "Cannot create output file \"$output\": ";
+binmode OUT;
+
+LINE: while (my $line = <CFG>)
+{
+ $line =~ s/\r?\n$//;
+ next LINE if $sectiontype != 4 && $line eq '';
+ next LINE if $line =~ /^;/;
+ my $oldsectiontype = $sectiontype;
+ my $oldrev = $rev;
+
+ # Sections
+ if ($line =~ m"^\[(config|(\d+)(|\.files|\.message))\]$")
+ {
+ if ($1 eq 'config')
+ {
+ $sectiontype = 1;
+ }
+ elsif ($3 eq '')
+ {
+ $sectiontype = 2;
+ $rev = $2;
+ # Create a new revision
+ die "Duplicate rev: $line\n " if defined $revmap{$rev};
+ print "Reading revision $rev\n";
+ push @revs, $rev;
+ $revmap{$rev} = $mark ++;
+ $time{$revmap{$rev}} = 0;
+ }
+ elsif ($3 eq '.files')
+ {
+ $sectiontype = 3;
+ $rev = $2;
+ die "Revision mismatch: $line\n " unless $rev == $oldrev;
+ }
+ elsif ($3 eq '.message')
+ {
+ $sectiontype = 4;
+ $rev = $2;
+ die "Revision mismatch: $line\n " unless $rev == $oldrev;
+ }
+ else
+ {
+ die "Internal parse error: $line\n ";
+ }
+ next LINE;
+ }
+
+ # Parse data
+ if ($sectiontype != 4)
+ {
+ # Key and value
+ if ($line =~ m"^\s*([^\s].*=.*[^\s])\s*$")
+ {
+ my ($key, $value) = &parsekeyvaluepair($1);
+ # Global configuration
+ if (1 == $sectiontype)
+ {
+ if ($key eq 'crlf')
+ {
+ $crlfmode = 1, next LINE if $value eq 'convert';
+ $crlfmode = 0, next LINE if $value eq 'none';
+ }
+ die "Unknown configuration option: $line\n ";
+ }
+ # Revision specification
+ if (2 == $sectiontype)
+ {
+ my $current = $revmap{$rev};
+ $author{$current} = $value, next LINE if $key eq 'author';
+ $branch{$current} = $value, next LINE if $key eq 'branch';
+ $parent{$current} = $value, next LINE if $key eq 'parent';
+ $timesource{$current} = $value, next LINE if $key eq 'timestamp';
+ push(@{$merges{$current}}, $value), next LINE if $key eq 'merges';
+ die "Unknown revision option: $line\n ";
+ }
+ # Filespecs
+ if (3 == $sectiontype)
+ {
+ # Add the file and create a marker
+ die "File not found: $line\n " unless -f $value;
+ my $current = $revmap{$rev};
+ ${$files{$current}}{$key} = $mark;
+ my $time = &fileblob($value, $crlfmode, $mark ++);
+
+ # Update revision timestamp if more recent than other
+ # files seen, or if this is the file we have selected
+ # to take the time stamp from using the "timestamp"
+ # directive.
+ if ((defined $timesource{$current} && $timesource{$current} eq $value)
+ || $time > $time{$current})
+ {
+ $time{$current} = $time;
+ }
+ }
+ }
+ else
+ {
+ die "Parse error: $line\n ";
+ }
+ }
+ else
+ {
+ # Commit message
+ my $current = $revmap{$rev};
+ if (defined $message{$current})
+ {
+ $message{$current} .= "\n";
+ }
+ $message{$current} .= $line;
+ }
+}
+close CFG;
+
+# Start spewing out data for git-fast-import
+foreach my $commit (@revs)
+{
+ # Progress
+ print OUT "progress Creating revision $commit\n";
+
+ # Create commit header
+ my $mark = $revmap{$commit};
+
+ # Branch and commit id
+ print OUT "commit refs/heads/", $branch{$mark}, "\nmark :", $mark, "\n";
+
+ # Author and timestamp
+ die "No timestamp defined for $commit (no files?)\n" unless defined $time{$mark};
+ print OUT "committer ", $author{$mark}, " ", $time{$mark}, " +0100\n";
+
+ # Commit message
+ die "No message defined for $commit\n" unless defined $message{$mark};
+ my $message = $message{$mark};
+ $message =~ s/\n$//; # Kill trailing empty line
+ print OUT "data ", length($message), "\n", $message, "\n";
+
+ # Parent and any merges
+ print OUT "from :", $revmap{$parent{$mark}}, "\n" if defined $parent{$mark};
+ if (defined $merges{$mark})
+ {
+ foreach my $merge (@{$merges{$mark}})
+ {
+ print OUT "merge :", $revmap{$merge}, "\n";
+ }
+ }
+
+ # Output file marks
+ print OUT "deleteall\n"; # start from scratch
+ foreach my $file (sort keys %{$files{$mark}})
+ {
+ print OUT "M 644 :", ${$files{$mark}}{$file}, " $file\n";
+ }
+ print OUT "\n";
+}
+
+# Create one file blob
+sub fileblob
+{
+ my ($filename, $crlfmode, $mark) = @_;
+
+ # Import the file
+ print OUT "progress Importing $filename\nblob\nmark :$mark\n";
+ open FILE, '<', $filename or die "Cannot read $filename\n ";
+ binmode FILE;
+ my ($size, $mtime) = (stat(FILE))[7,9];
+ my $file;
+ read FILE, $file, $size;
+ close FILE;
+ $file =~ s/\r\n/\n/g if $crlfmode;
+ print OUT "data ", length($file), "\n", $file, "\n";
+
+ return $mtime;
+}
+
+# Parse a key=value pair
+sub parsekeyvaluepair
+{
+=pod
+
+=head2 Escaping special characters
+
+Key and value strings may be enclosed in quotes, in which case
+whitespace inside the quotes is preserved. Additionally, an equal
+sign may be included in the key by preceeding it with a backslash.
+For example:
+
+ "key1 "=value1
+ key2=" value2"
+ key\=3=value3
+ key4=value=4
+ "key5""=value5
+
+Here the first key is "key1 " (note the trailing white-space) and the
+second value is " value2" (note the leading white-space). The third
+key contains an equal sign "key=3" and so does the fourth value, which
+does not need to be escaped. The fifth key contains a trailing quote,
+which does not need to be escaped since it is inside a surrounding
+quote.
+
+=cut
+ my $pair = shift;
+
+ # Separate key and value by the first non-quoted equal sign
+ my ($key, $value);
+ if ($pair =~ /^(.*[^\\])=(.*)$/)
+ {
+ ($key, $value) = ($1, $2)
+ }
+ else
+ {
+ die "Parse error: $pair\n ";
+ }
+
+ # Unquote and unescape the key and value separately
+ return (&unescape($key), &unescape($value));
+}
+
+# Unquote and unescape
+sub unescape
+{
+ my $string = shift;
+
+ # First remove enclosing quotes. Backslash before the trailing
+ # quote leaves both.
+ if ($string =~ /^"(.*[^\\])"$/)
+ {
+ $string = $1;
+ }
+
+ # Second remove any backslashes inside the unquoted string.
+ # For later: Handle special sequences like \t ?
+ $string =~ s/\\(.)/$1/g;
+
+ return $string;
+}
+
+__END__
+
+=pod
+
+=head1 EXAMPLES
+
+B<import-directories.perl> F<project.import>
+
+=head1 AUTHOR
+
+Copyright 2008-2009 Peter Krefting E<lt>peter@softwolves.pp.se>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation.
+
+=cut
diff --git a/contrib/fast-import/import-tars.perl b/contrib/fast-import/import-tars.perl
index 78e40d2a1..a90971668 100755
--- a/contrib/fast-import/import-tars.perl
+++ b/contrib/fast-import/import-tars.perl
@@ -8,9 +8,20 @@
## perl import-tars.perl *.tar.bz2
## git whatchanged import-tars
##
+## Use --metainfo to specify the extension for a meta data file, where
+## import-tars can read the commit message and optionally author and
+## committer information.
+##
+## echo 'This is the commit message' > myfile.tar.bz2.msg
+## perl import-tars.perl --metainfo=msg myfile.tar.bz2
use strict;
-die "usage: import-tars *.tar.{gz,bz2,Z}\n" unless @ARGV;
+use Getopt::Long;
+
+my $metaext = '';
+
+die "usage: import-tars [--metainfo=extension] *.tar.{gz,bz2,Z}\n"
+ unless GetOptions('metainfo=s' => \$metaext) && @ARGV;
my $branch_name = 'import-tars';
my $branch_ref = "refs/heads/$branch_name";
@@ -109,12 +120,43 @@ foreach my $tar_file (@ARGV)
$have_top_dir = 0 if $top_dir ne $1;
}
+ my $commit_msg = "Imported from $tar_file.";
+ my $this_committer_name = $committer_name;
+ my $this_committer_email = $committer_email;
+ my $this_author_name = $author_name;
+ my $this_author_email = $author_email;
+ if ($metaext ne '') {
+ # Optionally read a commit message from <filename.tar>.msg
+ # Add a line on the form "Committer: name <e-mail>" to override
+ # the committer and "Author: name <e-mail>" to override the
+ # author for this tar ball.
+ if (open MSG, '<', "${tar_file}.${metaext}") {
+ my $header_done = 0;
+ $commit_msg = '';
+ while (<MSG>) {
+ if (!$header_done && /^Committer:\s+([^<>]*)\s+<(.*)>\s*$/i) {
+ $this_committer_name = $1;
+ $this_committer_email = $2;
+ } elsif (!$header_done && /^Author:\s+([^<>]*)\s+<(.*)>\s*$/i) {
+ $this_author_name = $1;
+ $this_author_email = $2;
+ } elsif (!$header_done && /^$/ { # empty line ends header.
+ $header_done = 1;
+ } else {
+ $commit_msg .= $_;
+ $header_done = 1;
+ }
+ }
+ close MSG;
+ }
+ }
+
print FI <<EOF;
commit $branch_ref
-author $author_name <$author_email> $author_time +0000
-committer $committer_name <$committer_email> $commit_time +0000
+author $this_author_name <$this_author_email> $author_time +0000
+committer $this_committer_name <$this_committer_email> $commit_time +0000
data <<END_OF_COMMIT_MESSAGE
-Imported from $tar_file.
+$commit_msg
END_OF_COMMIT_MESSAGE
deleteall
diff --git a/convert.c b/convert.c
index 1816e977b..491e7141b 100644
--- a/convert.c
+++ b/convert.c
@@ -267,7 +267,7 @@ static int filter_buffer(int fd, void *data)
status = finish_command(&child_process);
if (status)
- error("external filter %s failed %d", params->cmd, -status);
+ error("external filter %s failed %d", params->cmd, status);
return (write_err || status);
}
diff --git a/copy.c b/copy.c
index e54d15ace..a7f58fd90 100644
--- a/copy.c
+++ b/copy.c
@@ -35,6 +35,19 @@ int copy_fd(int ifd, int ofd)
return 0;
}
+static int copy_times(const char *dst, const char *src)
+{
+ struct stat st;
+ struct utimbuf times;
+ if (stat(src, &st) < 0)
+ return -1;
+ times.actime = st.st_atime;
+ times.modtime = st.st_mtime;
+ if (utime(dst, &times) < 0)
+ return -1;
+ return 0;
+}
+
int copy_file(const char *dst, const char *src, int mode)
{
int fdi, fdo, status;
@@ -55,3 +68,11 @@ int copy_file(const char *dst, const char *src, int mode)
return status;
}
+
+int copy_file_with_time(const char *dst, const char *src, int mode)
+{
+ int status = copy_file(dst, src, mode);
+ if (!status)
+ return copy_times(dst, src);
+ return status;
+}
diff --git a/date.c b/date.c
index f011692c2..e9ee4aa74 100644
--- a/date.c
+++ b/date.c
@@ -24,6 +24,8 @@ time_t tm_to_time_t(const struct tm *tm)
return -1;
if (month < 2 || (year + 2) % 4)
day--;
+ if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
+ return -1;
return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
}
@@ -84,6 +86,67 @@ static int local_tzoffset(unsigned long time)
return offset * eastwest;
}
+const char *show_date_relative(unsigned long time, int tz,
+ const struct timeval *now,
+ char *timebuf,
+ size_t timebuf_size)
+{
+ unsigned long diff;
+ if (now->tv_sec < time)
+ return "in the future";
+ diff = now->tv_sec - time;
+ if (diff < 90) {
+ snprintf(timebuf, timebuf_size, "%lu seconds ago", diff);
+ return timebuf;
+ }
+ /* Turn it into minutes */
+ diff = (diff + 30) / 60;
+ if (diff < 90) {
+ snprintf(timebuf, timebuf_size, "%lu minutes ago", diff);
+ return timebuf;
+ }
+ /* Turn it into hours */
+ diff = (diff + 30) / 60;
+ if (diff < 36) {
+ snprintf(timebuf, timebuf_size, "%lu hours ago", diff);
+ return timebuf;
+ }
+ /* We deal with number of days from here on */
+ diff = (diff + 12) / 24;
+ if (diff < 14) {
+ snprintf(timebuf, timebuf_size, "%lu days ago", diff);
+ return timebuf;
+ }
+ /* Say weeks for the past 10 weeks or so */
+ if (diff < 70) {
+ snprintf(timebuf, timebuf_size, "%lu weeks ago", (diff + 3) / 7);
+ return timebuf;
+ }
+ /* Say months for the past 12 months or so */
+ if (diff < 360) {
+ snprintf(timebuf, timebuf_size, "%lu months ago", (diff + 15) / 30);
+ return timebuf;
+ }
+ /* Give years and months for 5 years or so */
+ if (diff < 1825) {
+ unsigned long years = diff / 365;
+ unsigned long months = (diff % 365 + 15) / 30;
+ int n;
+ n = snprintf(timebuf, timebuf_size, "%lu year%s",
+ years, (years > 1 ? "s" : ""));
+ if (months)
+ snprintf(timebuf + n, timebuf_size - n,
+ ", %lu month%s ago",
+ months, (months > 1 ? "s" : ""));
+ else
+ snprintf(timebuf + n, timebuf_size - n, " ago");
+ return timebuf;
+ }
+ /* Otherwise, just years. Centuries is probably overkill. */
+ snprintf(timebuf, timebuf_size, "%lu years ago", (diff + 183) / 365);
+ return timebuf;
+}
+
const char *show_date(unsigned long time, int tz, enum date_mode mode)
{
struct tm *tm;
@@ -95,63 +158,10 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
}
if (mode == DATE_RELATIVE) {
- unsigned long diff;
struct timeval now;
gettimeofday(&now, NULL);
- if (now.tv_sec < time)
- return "in the future";
- diff = now.tv_sec - time;
- if (diff < 90) {
- snprintf(timebuf, sizeof(timebuf), "%lu seconds ago", diff);
- return timebuf;
- }
- /* Turn it into minutes */
- diff = (diff + 30) / 60;
- if (diff < 90) {
- snprintf(timebuf, sizeof(timebuf), "%lu minutes ago", diff);
- return timebuf;
- }
- /* Turn it into hours */
- diff = (diff + 30) / 60;
- if (diff < 36) {
- snprintf(timebuf, sizeof(timebuf), "%lu hours ago", diff);
- return timebuf;
- }
- /* We deal with number of days from here on */
- diff = (diff + 12) / 24;
- if (diff < 14) {
- snprintf(timebuf, sizeof(timebuf), "%lu days ago", diff);
- return timebuf;
- }
- /* Say weeks for the past 10 weeks or so */
- if (diff < 70) {
- snprintf(timebuf, sizeof(timebuf), "%lu weeks ago", (diff + 3) / 7);
- return timebuf;
- }
- /* Say months for the past 12 months or so */
- if (diff < 360) {
- snprintf(timebuf, sizeof(timebuf), "%lu months ago", (diff + 15) / 30);
- return timebuf;
- }
- /* Give years and months for 5 years or so */
- if (diff < 1825) {
- unsigned long years = diff / 365;
- unsigned long months = (diff % 365 + 15) / 30;
- int n;
- n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
- years, (years > 1 ? "s" : ""));
- if (months)
- snprintf(timebuf + n, sizeof(timebuf) - n,
- ", %lu month%s ago",
- months, (months > 1 ? "s" : ""));
- else
- snprintf(timebuf + n, sizeof(timebuf) - n,
- " ago");
- return timebuf;
- }
- /* Otherwise, just years. Centuries is probably overkill. */
- snprintf(timebuf, sizeof(timebuf), "%lu years ago", (diff + 183) / 365);
- return timebuf;
+ return show_date_relative(time, tz, &now,
+ timebuf, sizeof(timebuf));
}
if (mode == DATE_LOCAL)
@@ -425,13 +435,19 @@ static int match_multi_number(unsigned long num, char c, const char *date, char
return end - date;
}
-/* Have we filled in any part of the time/date yet? */
+/*
+ * Have we filled in any part of the time/date yet?
+ * We just do a binary 'and' to see if the sign bit
+ * is set in all the values.
+ */
static inline int nodate(struct tm *tm)
{
- return tm->tm_year < 0 &&
- tm->tm_mon < 0 &&
- tm->tm_mday < 0 &&
- !(tm->tm_hour | tm->tm_min | tm->tm_sec);
+ return (tm->tm_year &
+ tm->tm_mon &
+ tm->tm_mday &
+ tm->tm_hour &
+ tm->tm_min &
+ tm->tm_sec) < 0;
}
/*
@@ -525,11 +541,8 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
}
}
- if (num > 0 && num < 32) {
- tm->tm_mday = num;
- } else if (num > 0 && num < 13) {
+ if (num > 0 && num < 13 && tm->tm_mon < 0)
tm->tm_mon = num-1;
- }
return n;
}
@@ -583,6 +596,9 @@ int parse_date(const char *date, char *result, int maxlen)
tm.tm_mon = -1;
tm.tm_mday = -1;
tm.tm_isdst = -1;
+ tm.tm_hour = -1;
+ tm.tm_min = -1;
+ tm.tm_sec = -1;
offset = -1;
tm_gmt = 0;
@@ -657,42 +673,59 @@ void datestamp(char *buf, int bufsize)
date_string(now, offset, buf, bufsize);
}
-static void update_tm(struct tm *tm, unsigned long sec)
+/*
+ * Relative time update (eg "2 days ago"). If we haven't set the time
+ * yet, we need to set it from current time.
+ */
+static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
{
- time_t n = mktime(tm) - sec;
+ time_t n;
+
+ if (tm->tm_mday < 0)
+ tm->tm_mday = now->tm_mday;
+ if (tm->tm_mon < 0)
+ tm->tm_mon = now->tm_mon;
+ if (tm->tm_year < 0) {
+ tm->tm_year = now->tm_year;
+ if (tm->tm_mon > now->tm_mon)
+ tm->tm_year--;
+ }
+
+ n = mktime(tm) - sec;
localtime_r(&n, tm);
+ return n;
}
-static void date_yesterday(struct tm *tm, int *num)
+static void date_yesterday(struct tm *tm, struct tm *now, int *num)
{
- update_tm(tm, 24*60*60);
+ update_tm(tm, now, 24*60*60);
}
-static void date_time(struct tm *tm, int hour)
+static void date_time(struct tm *tm, struct tm *now, int hour)
{
if (tm->tm_hour < hour)
- date_yesterday(tm, NULL);
+ date_yesterday(tm, now, NULL);
tm->tm_hour = hour;
tm->tm_min = 0;
tm->tm_sec = 0;
}
-static void date_midnight(struct tm *tm, int *num)
+static void date_midnight(struct tm *tm, struct tm *now, int *num)
{
- date_time(tm, 0);
+ date_time(tm, now, 0);
}
-static void date_noon(struct tm *tm, int *num)
+static void date_noon(struct tm *tm, struct tm *now, int *num)
{
- date_time(tm, 12);
+ date_time(tm, now, 12);
}
-static void date_tea(struct tm *tm, int *num)
+static void date_tea(struct tm *tm, struct tm *now, int *num)
{
- date_time(tm, 17);
+ date_time(tm, now, 17);
}
-static void date_pm(struct tm *tm, int *num)
+static void date_pm(struct tm *tm, struct tm *now, int *num)
{
int hour, n = *num;
*num = 0;
@@ -706,7 +739,7 @@ static void date_pm(struct tm *tm, int *num)
tm->tm_hour = (hour % 12) + 12;
}
-static void date_am(struct tm *tm, int *num)
+static void date_am(struct tm *tm, struct tm *now, int *num)
{
int hour, n = *num;
*num = 0;
@@ -720,7 +753,7 @@ static void date_am(struct tm *tm, int *num)
tm->tm_hour = (hour % 12);
}
-static void date_never(struct tm *tm, int *num)
+static void date_never(struct tm *tm, struct tm *now, int *num)
{
time_t n = 0;
localtime_r(&n, tm);
@@ -728,7 +761,7 @@ static void date_never(struct tm *tm, int *num)
static const struct special {
const char *name;
- void (*fn)(struct tm *, int *);
+ void (*fn)(struct tm *, struct tm *, int *);
} special[] = {
{ "yesterday", date_yesterday },
{ "noon", date_noon },
@@ -757,7 +790,7 @@ static const struct typelen {
{ NULL }
};
-static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num)
{
const struct typelen *tl;
const struct special *s;
@@ -778,7 +811,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
for (s = special; s->name; s++) {
int len = strlen(s->name);
if (match_string(date, s->name) == len) {
- s->fn(tm, num);
+ s->fn(tm, now, num);
return end;
}
}
@@ -800,7 +833,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
while (tl->type) {
int len = strlen(tl->type);
if (match_string(date, tl->type) >= len-1) {
- update_tm(tm, tl->length * *num);
+ update_tm(tm, now, tl->length * *num);
*num = 0;
return end;
}
@@ -818,13 +851,15 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
n++;
diff += 7*n;
- update_tm(tm, diff * 24 * 60 * 60);
+ update_tm(tm, now, diff * 24 * 60 * 60);
return end;
}
}
if (match_string(date, "months") >= 5) {
- int n = tm->tm_mon - *num;
+ int n;
+ update_tm(tm, now, 0); /* fill in date fields if needed */
+ n = tm->tm_mon - *num;
*num = 0;
while (n < 0) {
n += 12;
@@ -835,6 +870,7 @@ static const char *approxidate_alpha(const char *date, struct tm *tm, int *num)
}
if (match_string(date, "years") >= 4) {
+ update_tm(tm, now, 0); /* fill in date fields if needed */
tm->tm_year -= *num;
*num = 0;
return end;
@@ -866,36 +902,82 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
return end;
}
-unsigned long approxidate(const char *date)
+/*
+ * Do we have a pending number at the end, or when
+ * we see a new one? Let's assume it's a month day,
+ * as in "Dec 6, 1992"
+ */
+static void pending_number(struct tm *tm, int *num)
+{
+ int number = *num;
+
+ if (number) {
+ *num = 0;
+ if (tm->tm_mday < 0 && number < 32)
+ tm->tm_mday = number;
+ else if (tm->tm_mon < 0 && number < 13)
+ tm->tm_mon = number-1;
+ else if (tm->tm_year < 0) {
+ if (number > 1969 && number < 2100)
+ tm->tm_year = number - 1900;
+ else if (number > 69 && number < 100)
+ tm->tm_year = number;
+ else if (number < 38)
+ tm->tm_year = 100 + number;
+ /* We screw up for number = 00 ? */
+ }
+ }
+}
+
+static unsigned long approxidate_str(const char *date, const struct timeval *tv)
{
int number = 0;
struct tm tm, now;
- struct timeval tv;
time_t time_sec;
- char buffer[50];
-
- if (parse_date(date, buffer, sizeof(buffer)) > 0)
- return strtoul(buffer, NULL, 10);
- gettimeofday(&tv, NULL);
- time_sec = tv.tv_sec;
+ time_sec = tv->tv_sec;
localtime_r(&time_sec, &tm);
now = tm;
+
+ tm.tm_year = -1;
+ tm.tm_mon = -1;
+ tm.tm_mday = -1;
+
for (;;) {
unsigned char c = *date;
if (!c)
break;
date++;
if (isdigit(c)) {
+ pending_number(&tm, &number);
date = approxidate_digit(date-1, &tm, &number);
continue;
}
if (isalpha(c))
- date = approxidate_alpha(date-1, &tm, &number);
+ date = approxidate_alpha(date-1, &tm, &now, &number);
}
- if (number > 0 && number < 32)
- tm.tm_mday = number;
- if (tm.tm_mon > now.tm_mon && tm.tm_year == now.tm_year)
- tm.tm_year--;
- return mktime(&tm);
+ pending_number(&tm, &number);
+ return update_tm(&tm, &now, 0);
+}
+
+unsigned long approxidate_relative(const char *date, const struct timeval *tv)
+{
+ char buffer[50];
+
+ if (parse_date(date, buffer, sizeof(buffer)) > 0)
+ return strtoul(buffer, NULL, 0);
+
+ return approxidate_str(date, tv);
+}
+
+unsigned long approxidate(const char *date)
+{
+ struct timeval tv;
+ char buffer[50];
+
+ if (parse_date(date, buffer, sizeof(buffer)) > 0)
+ return strtoul(buffer, NULL, 0);
+
+ gettimeofday(&tv, NULL);
+ return approxidate_str(date, &tv);
}
diff --git a/diff-delta.c b/diff-delta.c
index a4e28df71..464ac3ffc 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -4,7 +4,7 @@
* This code was greatly inspired by parts of LibXDiff from Davide Libenzi
* http://www.xmailserver.org/xdiff-lib.html
*
- * Rewritten for GIT by Nicolas Pitre <nico@cam.org>, (C) 2005-2007
+ * Rewritten for GIT by Nicolas Pitre <nico@fluxnic.net>, (C) 2005-2007
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/diff-lib.c b/diff-lib.c
index ad2a4cde7..0c74ef5cb 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -162,7 +162,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (ce_uptodate(ce))
continue;
- changed = check_removed(ce, &st);
+ /* If CE_VALID is set, don't look at workdir for file removal */
+ changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st);
if (changed) {
if (changed < 0) {
perror(ce->name);
@@ -309,22 +310,6 @@ static int show_modified(struct rev_info *revs,
}
/*
- * This turns all merge entries into "stage 3". That guarantees that
- * when we read in the new tree (into "stage 1"), we won't lose sight
- * of the fact that we had unmerged entries.
- */
-static void mark_merge_entries(void)
-{
- int i;
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- if (!ce_stage(ce))
- continue;
- ce->ce_flags |= CE_STAGEMASK;
- }
-}
-
-/*
* This gets a mix of an existing index and a tree, one pathname entry
* at a time. The index entry may be a single stage-0 one, but it could
* also be multiple unmerged entries (in which case idx_pos/idx_nr will
@@ -337,6 +322,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
struct rev_info *revs = o->unpack_data;
int match_missing, cached;
+ /* if the entry is not checked out, don't examine work tree */
+ cached = o->index_only || (idx && (idx->ce_flags & CE_VALID));
/*
* Backward compatibility wart - "diff-index -m" does
* not mean "do not ignore merges", but "match_missing".
@@ -344,12 +331,11 @@ static void do_oneway_diff(struct unpack_trees_options *o,
* But with the revision flag parsing, that's found in
* "!revs->ignore_merges".
*/
- cached = o->index_only;
match_missing = !revs->ignore_merges;
if (cached && idx && ce_stage(idx)) {
- if (tree)
- diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode, idx->sha1);
+ diff_unmerge(&revs->diffopt, idx->name, idx->ce_mode,
+ idx->sha1);
return;
}
@@ -435,8 +421,6 @@ int run_diff_index(struct rev_info *revs, int cached)
struct unpack_trees_options opts;
struct tree_desc t;
- mark_merge_entries();
-
ent = revs->pending.objects[0].item;
tree_name = revs->pending.objects[0].name;
tree = parse_tree_indirect(ent->sha1);
diff --git a/diff.c b/diff.c
index cd35e0c2d..e1be18974 100644
--- a/diff.c
+++ b/diff.c
@@ -2691,7 +2691,7 @@ static int parse_num(const char **cp_p)
num = 0;
scale = 1;
dot = 0;
- for(;;) {
+ for (;;) {
ch = *cp;
if ( !dot && ch == '.' ) {
scale = 1;
diff --git a/environment.c b/environment.c
index 8f5eaa7dd..5de683784 100644
--- a/environment.c
+++ b/environment.c
@@ -26,6 +26,7 @@ const char *git_commit_encoding;
const char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
+const char *apply_default_ignorewhitespace;
int zlib_compression_level = Z_BEST_SPEED;
int core_compression_level;
int core_compression_seen;
@@ -38,6 +39,7 @@ int pager_use_color = 1;
const char *editor_program;
const char *excludes_file;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
+int read_replace_refs = 1;
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 06f70602c..392efb913 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -72,6 +72,79 @@ sub colored {
# command line options
my $patch_mode;
+my $patch_mode_revision;
+
+sub apply_patch;
+sub apply_patch_for_checkout_commit;
+sub apply_patch_for_stash;
+
+my %patch_modes = (
+ 'stage' => {
+ DIFF => 'diff-files -p',
+ APPLY => sub { apply_patch 'apply --cached', @_; },
+ APPLY_CHECK => 'apply --cached',
+ VERB => 'Stage',
+ TARGET => '',
+ PARTICIPLE => 'staging',
+ FILTER => 'file-only',
+ },
+ 'stash' => {
+ DIFF => 'diff-index -p HEAD',
+ APPLY => sub { apply_patch 'apply --cached', @_; },
+ APPLY_CHECK => 'apply --cached',
+ VERB => 'Stash',
+ TARGET => '',
+ PARTICIPLE => 'stashing',
+ FILTER => undef,
+ },
+ 'reset_head' => {
+ DIFF => 'diff-index -p --cached',
+ APPLY => sub { apply_patch 'apply -R --cached', @_; },
+ APPLY_CHECK => 'apply -R --cached',
+ VERB => 'Unstage',
+ TARGET => '',
+ PARTICIPLE => 'unstaging',
+ FILTER => 'index-only',
+ },
+ 'reset_nothead' => {
+ DIFF => 'diff-index -R -p --cached',
+ APPLY => sub { apply_patch 'apply --cached', @_; },
+ APPLY_CHECK => 'apply --cached',
+ VERB => 'Apply',
+ TARGET => ' to index',
+ PARTICIPLE => 'applying',
+ FILTER => 'index-only',
+ },
+ 'checkout_index' => {
+ DIFF => 'diff-files -p',
+ APPLY => sub { apply_patch 'apply -R', @_; },
+ APPLY_CHECK => 'apply -R',
+ VERB => 'Discard',
+ TARGET => ' from worktree',
+ PARTICIPLE => 'discarding',
+ FILTER => 'file-only',
+ },
+ 'checkout_head' => {
+ DIFF => 'diff-index -p',
+ APPLY => sub { apply_patch_for_checkout_commit '-R', @_ },
+ APPLY_CHECK => 'apply -R',
+ VERB => 'Discard',
+ TARGET => ' from index and worktree',
+ PARTICIPLE => 'discarding',
+ FILTER => undef,
+ },
+ 'checkout_nothead' => {
+ DIFF => 'diff-index -R -p',
+ APPLY => sub { apply_patch_for_checkout_commit '', @_ },
+ APPLY_CHECK => 'apply',
+ VERB => 'Apply',
+ TARGET => ' to index and worktree',
+ PARTICIPLE => 'applying',
+ FILTER => undef,
+ },
+);
+
+my %patch_mode_flavour = %{$patch_modes{stage}};
sub run_cmd_pipe {
if ($^O eq 'MSWin32' || $^O eq 'msys') {
@@ -190,7 +263,14 @@ sub list_modified {
return if (!@tracked);
}
- my $reference = is_initial_commit() ? get_empty_tree() : 'HEAD';
+ my $reference;
+ if (defined $patch_mode_revision and $patch_mode_revision ne 'HEAD') {
+ $reference = $patch_mode_revision;
+ } elsif (is_initial_commit()) {
+ $reference = get_empty_tree();
+ } else {
+ $reference = 'HEAD';
+ }
for (run_cmd_pipe(qw(git diff-index --cached
--numstat --summary), $reference,
'--', @tracked)) {
@@ -613,12 +693,24 @@ sub add_untracked_cmd {
print "\n";
}
+sub run_git_apply {
+ my $cmd = shift;
+ my $fh;
+ open $fh, '| git ' . $cmd;
+ print $fh @_;
+ return close $fh;
+}
+
sub parse_diff {
my ($path) = @_;
- my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
+ my @diff_cmd = split(" ", $patch_mode_flavour{DIFF});
+ if (defined $patch_mode_revision) {
+ push @diff_cmd, $patch_mode_revision;
+ }
+ my @diff = run_cmd_pipe("git", @diff_cmd, "--", $path);
my @colored = ();
if ($diff_use_color) {
- @colored = run_cmd_pipe(qw(git diff-files -p --color --), $path);
+ @colored = run_cmd_pipe("git", @diff_cmd, qw(--color --), $path);
}
my (@hunk) = { TEXT => [], DISPLAY => [], TYPE => 'header' };
@@ -881,6 +973,7 @@ sub edit_hunk_manually {
or die "failed to open hunk edit file for writing: " . $!;
print $fh "# Manual hunk edit mode -- see bottom for a quick guide\n";
print $fh @$oldtext;
+ my $participle = $patch_mode_flavour{PARTICIPLE};
print $fh <<EOF;
# ---
# To remove '-' lines, make them ' ' lines (context).
@@ -888,7 +981,7 @@ sub edit_hunk_manually {
# Lines starting with # will be removed.
#
# If the patch applies cleanly, the edited hunk will immediately be
-# marked for staging. If it does not apply cleanly, you will be given
+# marked for $participle. If it does not apply cleanly, you will be given
# an opportunity to edit again. If all lines of the hunk are removed,
# then the edit is aborted and the hunk is left unchanged.
EOF
@@ -922,11 +1015,8 @@ EOF
sub diff_applies {
my $fh;
- open $fh, '| git apply --recount --cached --check';
- for my $h (@_) {
- print $fh @{$h->{TEXT}};
- }
- return close $fh;
+ return run_git_apply($patch_mode_flavour{APPLY_CHECK} . ' --recount --check',
+ map { @{$_->{TEXT}} } @_);
}
sub _restore_terminal_and_die {
@@ -992,12 +1082,14 @@ sub edit_hunk_loop {
}
sub help_patch_cmd {
- print colored $help_color, <<\EOF ;
-y - stage this hunk
-n - do not stage this hunk
-q - quit, do not stage this hunk nor any of the remaining ones
-a - stage this and all the remaining hunks in the file
-d - do not stage this hunk nor any of the remaining hunks in the file
+ my $verb = lc $patch_mode_flavour{VERB};
+ my $target = $patch_mode_flavour{TARGET};
+ print colored $help_color, <<EOF ;
+y - $verb this hunk$target
+n - do not $verb this hunk$target
+q - quit, do not $verb this hunk nor any of the remaining ones
+a - $verb this and all the remaining hunks in the file
+d - do not $verb this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
@@ -1010,8 +1102,40 @@ e - manually edit the current hunk
EOF
}
+sub apply_patch {
+ my $cmd = shift;
+ my $ret = run_git_apply $cmd . ' --recount', @_;
+ if (!$ret) {
+ print STDERR @_;
+ }
+ return $ret;
+}
+
+sub apply_patch_for_checkout_commit {
+ my $reverse = shift;
+ my $applies_index = run_git_apply 'apply '.$reverse.' --cached --recount --check', @_;
+ my $applies_worktree = run_git_apply 'apply '.$reverse.' --recount --check', @_;
+
+ if ($applies_worktree && $applies_index) {
+ run_git_apply 'apply '.$reverse.' --cached --recount', @_;
+ run_git_apply 'apply '.$reverse.' --recount', @_;
+ return 1;
+ } elsif (!$applies_index) {
+ print colored $error_color, "The selected hunks do not apply to the index!\n";
+ if (prompt_yesno "Apply them to the worktree anyway? ") {
+ return run_git_apply 'apply '.$reverse.' --recount', @_;
+ } else {
+ print colored $error_color, "Nothing was applied.\n";
+ return 0;
+ }
+ } else {
+ print STDERR @_;
+ return 0;
+ }
+}
+
sub patch_update_cmd {
- my @all_mods = list_modified('file-only');
+ my @all_mods = list_modified($patch_mode_flavour{FILTER});
my @mods = grep { !($_->{BINARY}) } @all_mods;
my @them;
@@ -1142,8 +1266,9 @@ sub patch_update_file {
for (@{$hunk[$ix]{DISPLAY}}) {
print;
}
- print colored $prompt_color, 'Stage ',
- ($hunk[$ix]{TYPE} eq 'mode' ? 'mode change' : 'this hunk'),
+ print colored $prompt_color, $patch_mode_flavour{VERB},
+ ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : ' this hunk'),
+ $patch_mode_flavour{TARGET},
" [y,n,q,a,d,/$other,?]? ";
my $line = prompt_single_character;
if ($line) {
@@ -1317,16 +1442,9 @@ sub patch_update_file {
if (@result) {
my $fh;
-
- open $fh, '| git apply --cached --recount';
- for (@{$head->{TEXT}}, @result) {
- print $fh $_;
- }
- if (!close $fh) {
- for (@{$head->{TEXT}}, @result) {
- print STDERR $_;
- }
- }
+ my @patch = (@{$head->{TEXT}}, @result);
+ my $apply_routine = $patch_mode_flavour{APPLY};
+ &$apply_routine(@patch);
refresh();
}
@@ -1367,11 +1485,41 @@ EOF
sub process_args {
return unless @ARGV;
my $arg = shift @ARGV;
- if ($arg eq "--patch") {
- $patch_mode = 1;
- $arg = shift @ARGV or die "missing --";
+ if ($arg =~ /--patch(?:=(.*))?/) {
+ if (defined $1) {
+ if ($1 eq 'reset') {
+ $patch_mode = 'reset_head';
+ $patch_mode_revision = 'HEAD';
+ $arg = shift @ARGV or die "missing --";
+ if ($arg ne '--') {
+ $patch_mode_revision = $arg;
+ $patch_mode = ($arg eq 'HEAD' ?
+ 'reset_head' : 'reset_nothead');
+ $arg = shift @ARGV or die "missing --";
+ }
+ } elsif ($1 eq 'checkout') {
+ $arg = shift @ARGV or die "missing --";
+ if ($arg eq '--') {
+ $patch_mode = 'checkout_index';
+ } else {
+ $patch_mode_revision = $arg;
+ $patch_mode = ($arg eq 'HEAD' ?
+ 'checkout_head' : 'checkout_nothead');
+ $arg = shift @ARGV or die "missing --";
+ }
+ } elsif ($1 eq 'stage' or $1 eq 'stash') {
+ $patch_mode = $1;
+ $arg = shift @ARGV or die "missing --";
+ } else {
+ die "unknown --patch mode: $1";
+ }
+ } else {
+ $patch_mode = 'stage';
+ $arg = shift @ARGV or die "missing --";
+ }
die "invalid argument $arg, expecting --"
unless $arg eq "--";
+ %patch_mode_flavour = %{$patch_modes{$patch_mode}};
}
elsif ($arg ne "--") {
die "invalid argument $arg, expecting --";
diff --git a/git-am.sh b/git-am.sh
index f719f6e65..26ffe702e 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -15,7 +15,10 @@ q,quiet be quiet
s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
k,keep pass -k flag to git-mailinfo
+c,scissors strip everything before a scissors line
whitespace= pass it through git-apply
+ignore-space-change pass it through git-apply
+ignore-whitespace pass it through git-apply
directory= pass it through git-apply
C= pass it through git-apply
p= pass it through git-apply
@@ -211,7 +214,13 @@ check_patch_format () {
split_patches () {
case "$patch_format" in
mbox)
- git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
+ case "$rebasing" in
+ '')
+ keep_cr= ;;
+ ?*)
+ keep_cr=--keep-cr ;;
+ esac
+ git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
clean_abort
;;
stgit-series)
@@ -280,7 +289,7 @@ split_patches () {
prec=4
dotest="$GIT_DIR/rebase-apply"
sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
-resolvemsg= resume=
+resolvemsg= resume= scissors=
git_apply_opt=
committer_date_is_author_date=
ignore_date=
@@ -302,6 +311,10 @@ do
utf8= ;;
-k|--keep)
keep=t ;;
+ -c|--scissors)
+ scissors=t ;;
+ --no-scissors)
+ scissors=f ;;
-r|--resolved)
resolved=t ;;
--skip)
@@ -309,7 +322,7 @@ do
--abort)
abort=t ;;
--rebasing)
- rebasing=t threeway=t keep=t ;;
+ rebasing=t threeway=t keep=t scissors=f ;;
-d|--dotest)
die "-d option is no longer supported. Do not use."
;;
@@ -321,7 +334,7 @@ do
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
--patch-format)
shift ; patch_format="$1" ;;
- --reject)
+ --reject|--ignore-whitespace|--ignore-space-change)
git_apply_opt="$git_apply_opt $1" ;;
--committer-date-is-author-date)
committer_date_is_author_date=t ;;
@@ -427,14 +440,14 @@ else
split_patches "$@"
- # -s, -u, -k, --whitespace, -3, -C, -q and -p flags are kept
- # for the resuming session after a patch failure.
- # -i can and must be given when resuming.
+ # -i can and must be given when resuming; everything
+ # else is kept
echo " $git_apply_opt" >"$dotest/apply-opt"
echo "$threeway" >"$dotest/threeway"
echo "$sign" >"$dotest/sign"
echo "$utf8" >"$dotest/utf8"
echo "$keep" >"$dotest/keep"
+ echo "$scissors" >"$dotest/scissors"
echo "$GIT_QUIET" >"$dotest/quiet"
echo 1 >"$dotest/next"
if test -n "$rebasing"
@@ -476,6 +489,12 @@ if test "$(cat "$dotest/keep")" = t
then
keep=-k
fi
+case "$(cat "$dotest/scissors")" in
+t)
+ scissors=--scissors ;;
+f)
+ scissors=--no-scissors ;;
+esac
if test "$(cat "$dotest/quiet")" = t
then
GIT_QUIET=t
@@ -530,7 +549,7 @@ do
# by the user, or the user can tell us to do so by --resolved flag.
case "$resume" in
'')
- git mailinfo $keep $utf8 "$dotest/msg" "$dotest/patch" \
+ git mailinfo $keep $scissors $utf8 "$dotest/msg" "$dotest/patch" \
<"$dotest/$msgnum" >"$dotest/info" ||
stop_here $this
diff --git a/git-compat-util.h b/git-compat-util.h
index 9f941e42b..e5e9f39c5 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -57,10 +57,8 @@
# endif
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
-#ifndef __sun__
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#endif
-#endif
#define _ALL_SOURCE 1
#define _GNU_SOURCE 1
#define _BSD_SOURCE 1
@@ -176,6 +174,8 @@ extern char *gitbasename(char *);
#endif
#endif
+#include "compat/bswap.h"
+
/* General helper functions */
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index e43920296..d7411151d 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -238,7 +238,9 @@ sub conn {
}
my $rr = ":pserver:$user\@$serv:$port$repo";
- unless ($pass) {
+ if ($pass) {
+ $pass = $self->_scramble($pass);
+ } else {
open(H,$ENV{'HOME'}."/.cvspass") and do {
# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
while (<H>) {
@@ -252,7 +254,6 @@ sub conn {
}
};
}
- $pass="A" unless $pass;
my ($s, $rep);
if ($proxyhost) {
@@ -484,6 +485,42 @@ sub _fetchfile {
return $res;
}
+sub _scramble {
+ my ($self, $pass) = @_;
+ my $scrambled = "A";
+
+ return $scrambled unless $pass;
+
+ my $pass_len = length($pass);
+ my @pass_arr = split("", $pass);
+ my $i;
+
+ # from cvs/src/scramble.c
+ my @shifts = (
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+ 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+ 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+ 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+ 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+ 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+ 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+ 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+ 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+ 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+ 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+ 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+ 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+ 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+ );
+
+ for ($i = 0; $i < $pass_len; $i++) {
+ $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
+ }
+
+ return $scrambled;
+}
package main;
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 5f5cac75e..d96eddbe5 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -77,11 +77,30 @@ start_httpd () {
resolve_full_httpd
# don't quote $full_httpd, there can be arguments to it (-f)
- $full_httpd "$fqgitdir/gitweb/httpd.conf"
- if test $? != 0; then
- echo "Could not execute http daemon $httpd."
- exit 1
- fi
+ case "$httpd" in
+ *mongoose*)
+ #The mongoose server doesn't have a daemon mode so we'll have to fork it
+ $full_httpd "$fqgitdir/gitweb/httpd.conf" &
+ #Save the pid before doing anything else (we'll print it later)
+ pid=$!
+
+ if test $? != 0; then
+ echo "Could not execute http daemon $httpd."
+ exit 1
+ fi
+
+ cat > "$fqgitdir/pid" <<EOF
+$pid
+EOF
+ ;;
+ *)
+ $full_httpd "$fqgitdir/gitweb/httpd.conf"
+ if test $? != 0; then
+ echo "Could not execute http daemon $httpd."
+ exit 1
+ fi
+ ;;
+ esac
}
stop_httpd () {
@@ -308,6 +327,31 @@ EOF
fi
}
+mongoose_conf() {
+ cat > "$conf" <<EOF
+# Mongoose web server configuration file.
+# Lines starting with '#' and empty lines are ignored.
+# For detailed description of every option, visit
+# http://code.google.com/p/mongoose/wiki/MongooseManual
+
+root $fqgitdir/gitweb
+ports $port
+index_files gitweb.cgi
+#ssl_cert $fqgitdir/gitweb/ssl_cert.pem
+error_log $fqgitdir/gitweb/error.log
+access_log $fqgitdir/gitweb/access.log
+
+#cgi setup
+cgi_env PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_interp $PERL
+cgi_ext cgi,pl
+
+# mimetype mapping
+mime_types .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
+EOF
+}
+
+
script='
s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
@@ -344,6 +388,9 @@ case "$httpd" in
webrick)
webrick_conf
;;
+*mongoose*)
+ mongoose_conf
+ ;;
*)
echo "Unknown httpd specified: $httpd"
exit 1
diff --git a/git-pull.sh b/git-pull.sh
index 0f2418297..0bbd5bf7d 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -119,11 +119,19 @@ error_on_no_merge_candidates () {
}
test true = "$rebase" && {
- git update-index --ignore-submodules --refresh &&
- git diff-files --ignore-submodules --quiet &&
- git diff-index --ignore-submodules --cached --quiet HEAD -- ||
- die "refusing to pull with rebase: your working tree is not up-to-date"
-
+ if ! git rev-parse -q --verify HEAD >/dev/null
+ then
+ # On an unborn branch
+ if test -f "$GIT_DIR/index"
+ then
+ die "updating an unborn branch with changes added to the index"
+ fi
+ else
+ git update-index --ignore-submodules --refresh &&
+ git diff-files --ignore-submodules --quiet &&
+ git diff-index --ignore-submodules --cached --quiet HEAD -- ||
+ die "refusing to pull with rebase: your working tree is not up-to-date"
+ fi
oldremoteref= &&
. git-parse-remote &&
remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
diff --git a/git-rebase.sh b/git-rebase.sh
index 18bc6946c..6ec155cf0 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -333,6 +333,9 @@ do
;;
esac
;;
+ --ignore-whitespace)
+ git_am_opt="$git_am_opt $1"
+ ;;
--committer-date-is-author-date|--ignore-date)
git_am_opt="$git_am_opt $1"
force_rebase=t
@@ -382,8 +385,10 @@ else
fi
# The tree must be really really clean.
-if ! git update-index --ignore-submodules --refresh; then
- die "cannot rebase: you have unstaged changes"
+if ! git update-index --ignore-submodules --refresh > /dev/null; then
+ echo >&2 "cannot rebase: you have unstaged changes"
+ git diff-files --name-status -r --ignore-submodules -- >&2
+ exit 1
fi
diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
case "$diff" in
diff --git a/git-request-pull.sh b/git-request-pull.sh
index fd95beada..630ceddf0 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -8,13 +8,33 @@ USAGE='<start> <url> [<end>]'
LONG_USAGE='Summarizes the changes between two commits to the standard output,
and includes the given URL in the generated summary.'
SUBDIRECTORY_OK='Yes'
-OPTIONS_SPEC=
+OPTIONS_SPEC='git request-pull [options] start url [end]
+--
+p show patch text as well
+'
+
. git-sh-setup
. git-parse-remote
GIT_PAGER=
export GIT_PAGER
+patch=
+while case "$#" in 0) break ;; esac
+do
+ case "$1" in
+ -p)
+ patch=-p ;;
+ --)
+ shift; break ;;
+ -*)
+ usage ;;
+ *)
+ break ;;
+ esac
+ shift
+done
+
base=$1
url=$2
head=${3-HEAD}
@@ -54,5 +74,5 @@ echo " $url $branch"
echo
git shortlog ^$baserev $headrev
-git diff -M --stat --summary $merge_base $headrev
+git diff -M --stat --summary $patch $merge_base..$headrev
exit $status
diff --git a/git-stash.sh b/git-stash.sh
index 03e589f76..4febbbfa5 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -7,7 +7,7 @@ USAGE="list [<options>]
or: $dashless drop [-q|--quiet] [<stash>]
or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
or: $dashless branch <branchname> [<stash>]
- or: $dashless [save [--keep-index] [-q|--quiet] [<message>]]
+ or: $dashless [save [-k|--keep-index] [-q|--quiet] [<message>]]
or: $dashless clear"
SUBDIRECTORY_OK=Yes
@@ -21,6 +21,14 @@ trap 'rm -f "$TMP-*"' 0
ref_stash=refs/stash
+if git config --get-colorbool color.interactive; then
+ help_color="$(git config --get-color color.interactive.help 'red bold')"
+ reset_color="$(git config --get-color '' reset)"
+else
+ help_color=
+ reset_color=
+fi
+
no_changes () {
git diff-index --quiet --cached HEAD --ignore-submodules -- &&
git diff-files --quiet --ignore-submodules
@@ -68,19 +76,44 @@ create_stash () {
git commit-tree $i_tree -p $b_commit) ||
die "Cannot save the current index state"
- # state of the working tree
- w_tree=$( (
+ if test -z "$patch_mode"
+ then
+
+ # state of the working tree
+ w_tree=$( (
+ rm -f "$TMP-index" &&
+ cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
+ GIT_INDEX_FILE="$TMP-index" &&
+ export GIT_INDEX_FILE &&
+ git read-tree -m $i_tree &&
+ git add -u &&
+ git write-tree &&
+ rm -f "$TMP-index"
+ ) ) ||
+ die "Cannot save the current worktree state"
+
+ else
+
rm -f "$TMP-index" &&
- cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
- GIT_INDEX_FILE="$TMP-index" &&
- export GIT_INDEX_FILE &&
- git read-tree -m $i_tree &&
- git add -u &&
- git write-tree &&
- rm -f "$TMP-index"
- ) ) ||
+ GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
+
+ # find out what the user wants
+ GIT_INDEX_FILE="$TMP-index" \
+ git add--interactive --patch=stash -- &&
+
+ # state of the working tree
+ w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
die "Cannot save the current worktree state"
+ git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
+ test -s "$TMP-patch" ||
+ die "No changes selected"
+
+ rm -f "$TMP-index" ||
+ die "Cannot remove temporary index (can't happen)"
+
+ fi
+
# create the stash
if test -z "$stash_msg"
then
@@ -95,15 +128,31 @@ create_stash () {
save_stash () {
keep_index=
+ patch_mode=
while test $# != 0
do
case "$1" in
- --keep-index)
+ -k|--keep-index)
+ keep_index=t
+ ;;
+ --no-keep-index)
+ keep_index=
+ ;;
+ -p|--patch)
+ patch_mode=t
keep_index=t
;;
-q|--quiet)
GIT_QUIET=t
;;
+ --)
+ shift
+ break
+ ;;
+ -*)
+ echo "error: unknown option for 'stash save': $1"
+ usage
+ ;;
*)
break
;;
@@ -131,11 +180,22 @@ save_stash () {
die "Cannot save the current status"
say Saved working directory and index state "$stash_msg"
- git reset --hard ${GIT_QUIET:+-q}
-
- if test -n "$keep_index" && test -n $i_tree
+ if test -z "$patch_mode"
then
- git read-tree --reset -u $i_tree
+ git reset --hard ${GIT_QUIET:+-q}
+
+ if test -n "$keep_index" && test -n $i_tree
+ then
+ git read-tree --reset -u $i_tree
+ fi
+ else
+ git apply -R < "$TMP-patch" ||
+ die "Cannot remove worktree changes"
+
+ if test -z "$keep_index"
+ then
+ git reset
+ fi
fi
}
@@ -162,10 +222,6 @@ show_stash () {
}
apply_stash () {
- git update-index -q --refresh &&
- git diff-files --quiet --ignore-submodules ||
- die 'Cannot apply to a dirty working tree, please stage your changes'
-
unstash_index=
while test $# != 0
@@ -184,18 +240,27 @@ apply_stash () {
shift
done
- # current index state
- c_tree=$(git write-tree) ||
- die 'Cannot apply a stash in the middle of a merge'
+ if test $# = 0
+ then
+ have_stash || die 'Nothing to apply'
+ fi
# stash records the work tree, and is a merge between the
# base commit (first parent) and the index tree (second parent).
- s=$(git rev-parse --verify --default $ref_stash "$@") &&
- w_tree=$(git rev-parse --verify "$s:") &&
- b_tree=$(git rev-parse --verify "$s^1:") &&
- i_tree=$(git rev-parse --verify "$s^2:") ||
+ s=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
+ w_tree=$(git rev-parse --quiet --verify "$s:") &&
+ b_tree=$(git rev-parse --quiet --verify "$s^1:") &&
+ i_tree=$(git rev-parse --quiet --verify "$s^2:") ||
die "$*: no valid stashed state found"
+ git update-index -q --refresh &&
+ git diff-files --quiet --ignore-submodules ||
+ die 'Cannot apply to a dirty working tree, please stage your changes'
+
+ # current index state
+ c_tree=$(git write-tree) ||
+ die 'Cannot apply a stash in the middle of a merge'
+
unstashed_index_tree=
if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
test "$c_tree" != "$i_tree"
@@ -302,6 +367,18 @@ apply_to_branch () {
drop_stash $stash
}
+# The default command is "save" if nothing but options are given
+seen_non_option=
+for opt
+do
+ case "$opt" in
+ -*) ;;
+ *) seen_non_option=t; break ;;
+ esac
+done
+
+test -n "$seen_non_option" || set "save" "$@"
+
# Main command set
case "$1" in
list)
@@ -353,12 +430,13 @@ branch)
apply_to_branch "$@"
;;
*)
- if test $# -eq 0
- then
+ case $# in
+ 0)
save_stash &&
say '(To restore them type "git stash apply")'
- else
+ ;;
+ *)
usage
- fi
+ esac
;;
esac
diff --git a/git-submodule.sh b/git-submodule.sh
index ebed711da..bfbd36b6f 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -4,9 +4,14 @@
#
# Copyright (c) 2007 Lars Hjemli
-USAGE="[--quiet] [--cached] \
-[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch] [--rebase|--merge]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path>
+ or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
+ or: $dashless [--quiet] init [--] [<path>...]
+ or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+ or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+ or: $dashless [--quiet] foreach [--recursive] <command>
+ or: $dashless [--quiet] sync [--] [<path>...]"
OPTIONS_SPEC=
. git-sh-setup
. git-parse-remote
@@ -16,8 +21,10 @@ command=
branch=
reference=
cached=
+files=
nofetch=
update=
+prefix=
# Resolve relative url by appending to parent's url
resolve_relative_url ()
@@ -237,13 +244,43 @@ cmd_add()
#
cmd_foreach()
{
+ # parse $args after "submodule ... foreach".
+ while test $# -ne 0
+ do
+ case "$1" in
+ -q|--quiet)
+ GIT_QUIET=1
+ ;;
+ --recursive)
+ recursive=1
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+
module_list |
while read mode sha1 stage path
do
if test -e "$path"/.git
then
- say "Entering '$path'"
- (cd "$path" && eval "$@") ||
+ say "Entering '$prefix$path'"
+ name=$(module_name "$path")
+ (
+ prefix="$prefix$path/"
+ unset GIT_DIR
+ cd "$path" &&
+ eval "$@" &&
+ if test -n "$recursive"
+ then
+ cmd_foreach "--recursive" "$@"
+ fi
+ ) ||
die "Stopping at '$path'; script returned non-zero status."
fi
done
@@ -316,6 +353,7 @@ cmd_init()
cmd_update()
{
# parse $args after "submodule ... update".
+ orig_args="$@"
while test $# -ne 0
do
case "$1" in
@@ -348,6 +386,10 @@ cmd_update()
shift
update="merge"
;;
+ --recursive)
+ shift
+ recursive=1
+ ;;
--)
shift
break
@@ -434,6 +476,12 @@ cmd_update()
die "Unable to $action '$sha1' in submodule path '$path'"
say "Submodule path '$path': $msg '$sha1'"
fi
+
+ if test -n "$recursive"
+ then
+ (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
+ die "Failed to recurse into submodule path '$path'"
+ fi
done
}
@@ -460,6 +508,7 @@ set_name_rev () {
cmd_summary() {
summary_limit=-1
for_status=
+ diff_cmd=diff-index
# parse $args after "submodule ... summary".
while test $# -ne 0
@@ -468,6 +517,9 @@ cmd_summary() {
--cached)
cached="$1"
;;
+ --files)
+ files="$1"
+ ;;
--for-status)
for_status="$1"
;;
@@ -504,9 +556,17 @@ cmd_summary() {
head=HEAD
fi
+ if [ -n "$files" ]
+ then
+ test -n "$cached" &&
+ die "--cached cannot be used with --files"
+ diff_cmd=diff-files
+ head=
+ fi
+
cd_to_toplevel
# Get modified modules cared by user
- modules=$(git diff-index $cached --raw $head -- "$@" |
+ modules=$(git $diff_cmd $cached --raw $head -- "$@" |
egrep '^:([0-7]* )?160000' |
while read mod_src mod_dst sha1_src sha1_dst status name
do
@@ -520,7 +580,7 @@ cmd_summary() {
test -z "$modules" && return
- git diff-index $cached --raw $head -- $modules |
+ git $diff_cmd $cached --raw $head -- $modules |
egrep '^:([0-7]* )?160000' |
cut -c2- |
while read mod_src mod_dst sha1_src sha1_dst status name
@@ -643,6 +703,7 @@ cmd_summary() {
cmd_status()
{
# parse $args after "submodule ... status".
+ orig_args="$@"
while test $# -ne 0
do
case "$1" in
@@ -652,6 +713,9 @@ cmd_status()
--cached)
cached=1
;;
+ --recursive)
+ recursive=1
+ ;;
--)
shift
break
@@ -671,22 +735,34 @@ cmd_status()
do
name=$(module_name "$path") || exit
url=$(git config submodule."$name".url)
+ displaypath="$prefix$path"
if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
then
- say "-$sha1 $path"
+ say "-$sha1 $displaypath"
continue;
fi
set_name_rev "$path" "$sha1"
if git diff-files --quiet -- "$path"
then
- say " $sha1 $path$revname"
+ say " $sha1 $displaypath$revname"
else
if test -z "$cached"
then
sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
set_name_rev "$path" "$sha1"
fi
- say "+$sha1 $path$revname"
+ say "+$sha1 $displaypath$revname"
+ fi
+
+ if test -n "$recursive"
+ then
+ (
+ prefix="$displaypath/"
+ unset GIT_DIR
+ cd "$path" &&
+ cmd_status $orig_args
+ ) ||
+ die "Failed to recurse into submodule path '$path'"
fi
done
}
diff --git a/git-svn.perl b/git-svn.perl
index 24bdbf5b8..e0ec258e3 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -21,6 +21,15 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
$Git::SVN::Ra::_log_window_size = 100;
$Git::SVN::_minimize_url = 'unset';
+if (! exists $ENV{SVN_SSH}) {
+ if (exists $ENV{GIT_SSH}) {
+ $ENV{SVN_SSH} = $ENV{GIT_SSH};
+ if ($^O eq 'msys') {
+ $ENV{SVN_SSH} =~ s/\\/\\\\/g;
+ }
+ }
+}
+
$Git::SVN::Log::TZ = $ENV{TZ};
$ENV{TZ} = 'UTC';
$| = 1; # unbuffer STDOUT
@@ -764,6 +773,7 @@ sub cmd_show_ignore {
print STDOUT "\n# $path\n";
my $s = $props->{'svn:ignore'} or return;
$s =~ s/[\r\n]+/\n/g;
+ $s =~ s/^\n+//;
chomp $s;
$s =~ s#^#$path#gm;
print STDOUT "$s\n";
@@ -801,6 +811,7 @@ sub cmd_create_ignore {
open(GITIGNORE, '>', $ignore)
or fatal("Failed to open `$ignore' for writing: $!");
$s =~ s/[\r\n]+/\n/g;
+ $s =~ s/^\n+//;
chomp $s;
# Prefix all patterns so that the ignore doesn't apply
# to sub-directories.
@@ -907,7 +918,7 @@ sub cmd_multi_init {
}
do_git_init_db();
if (defined $_trunk) {
- my $trunk_ref = $_prefix . 'trunk';
+ my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
# try both old-style and new-style lookups:
my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
unless ($gs_trunk) {
@@ -1154,6 +1165,17 @@ sub post_fetch_checkout {
my $gs = $Git::SVN::_head or return;
return if verify_ref('refs/heads/master^0');
+ # look for "trunk" ref if it exists
+ my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
+ my $fetch = $remote->{fetch};
+ if ($fetch) {
+ foreach my $p (keys %$fetch) {
+ basename($fetch->{$p}) eq 'trunk' or next;
+ $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p);
+ last;
+ }
+ }
+
my $valid_head = verify_ref('HEAD^0');
command_noisy(qw(update-ref refs/heads/master), $gs->refname);
return if ($valid_head || !verify_ref('HEAD^0'));
@@ -1209,6 +1231,7 @@ sub complete_url_ls_init {
}
command_oneline('config', $k, $gs->{url}) unless $orig_url;
my $remote_path = "$gs->{path}/$repo_path";
+ $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
$remote_path =~ s#/+#/#g;
$remote_path =~ s#^/##g;
$remote_path .= "/*" if $remote_path !~ /\*/;
@@ -1641,23 +1664,23 @@ sub resolve_local_globs {
return unless defined $glob_spec;
my $ref = $glob_spec->{ref};
my $path = $glob_spec->{path};
- foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
- next unless m#^refs/remotes/$ref->{regex}$#;
+ foreach (command(qw#for-each-ref --format=%(refname) refs/#)) {
+ next unless m#^$ref->{regex}$#;
my $p = $1;
my $pathname = desanitize_refname($path->full_path($p));
my $refname = desanitize_refname($ref->full_path($p));
if (my $existing = $fetch->{$pathname}) {
if ($existing ne $refname) {
die "Refspec conflict:\n",
- "existing: refs/remotes/$existing\n",
- " globbed: refs/remotes/$refname\n";
+ "existing: $existing\n",
+ " globbed: $refname\n";
}
- my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
+ my $u = (::cmt_metadata("$refname"))[0];
$u =~ s!^\Q$url\E(/|$)!! or die
- "refs/remotes/$refname: '$url' not found in '$u'\n";
+ "$refname: '$url' not found in '$u'\n";
if ($pathname ne $u) {
warn "W: Refspec glob conflict ",
- "(ref: refs/remotes/$refname):\n",
+ "(ref: $refname):\n",
"expected path: $pathname\n",
" real path: $u\n",
"Continuing ahead with $u\n";
@@ -1735,33 +1758,35 @@ sub read_all_remotes {
my $use_svm_props = eval { command_oneline(qw/config --bool
svn.useSvmProps/) };
$use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
+ my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*};
foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
- if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) {
- my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3);
- die("svn-remote.$remote: remote ref '$_remote_ref' "
- . "must start with 'refs/remotes/'\n")
- unless $_remote_ref =~ m{^refs/remotes/(.+)};
- my $remote_ref = $1;
- $local_ref =~ s{^/}{};
+ if (m!^(.+)\.fetch=$svn_refspec$!) {
+ my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
+ die("svn-remote.$remote: remote ref '$remote_ref' "
+ . "must start with 'refs/'\n")
+ unless $remote_ref =~ m{^refs/};
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
$r->{$remote}->{svm} = {} if $use_svm_props;
} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
$r->{$1}->{svm} = {};
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r->{$1}->{url} = $2;
- } elsif (m!^(.+)\.(branches|tags)=
- (.*):refs/remotes/(.+)\s*$/!x) {
- my ($p, $g) = ($3, $4);
+ } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
+ my ($remote, $t, $local_ref, $remote_ref) =
+ ($1, $2, $3, $4);
+ die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
+ . "must start with 'refs/'\n")
+ unless $remote_ref =~ m{^refs/};
my $rs = {
- t => $2,
- remote => $1,
- path => Git::SVN::GlobSpec->new($p),
- ref => Git::SVN::GlobSpec->new($g) };
+ t => $t,
+ remote => $remote,
+ path => Git::SVN::GlobSpec->new($local_ref),
+ ref => Git::SVN::GlobSpec->new($remote_ref) };
if (length($rs->{ref}->{right}) != 0) {
die "The '*' glob character must be the last ",
- "character of '$g'\n";
+ "character of '$remote_ref'\n";
}
- push @{ $r->{$1}->{$2} }, $rs;
+ push @{ $r->{$remote}->{$t} }, $rs;
}
}
@@ -1869,14 +1894,15 @@ sub init_remote_config {
}
}
my ($xrepo_id, $xpath) = find_ref($self->refname);
- if (defined $xpath) {
+ if (!$no_write && defined $xpath) {
die "svn-remote.$xrepo_id.fetch already set to track ",
- "$xpath:refs/remotes/", $self->refname, "\n";
+ "$xpath:", $self->refname, "\n";
}
unless ($no_write) {
command_noisy('config',
"svn-remote.$self->{repo_id}.url", $url);
$self->{path} =~ s{^/}{};
+ $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
command_noisy('config', '--add',
"svn-remote.$self->{repo_id}.fetch",
"$self->{path}:".$self->refname);
@@ -1946,7 +1972,7 @@ sub find_ref {
my ($ref_id) = @_;
foreach (command(qw/config -l/)) {
next unless m!^svn-remote\.(.+)\.fetch=
- \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x;
+ \s*/?(.*?)\s*:\s*(.+?)\s*$!x;
my ($repo_id, $path, $ref) = ($1, $2, $3);
if ($ref eq $ref_id) {
$path = '' if ($path =~ m#^\./?#);
@@ -1963,16 +1989,16 @@ sub new {
if (!defined $repo_id) {
die "Could not find a \"svn-remote.*.fetch\" key ",
"in the repository configuration matching: ",
- "refs/remotes/$ref_id\n";
+ "$ref_id\n";
}
}
my $self = _new($class, $repo_id, $ref_id, $path);
if (!defined $self->{path} || !length $self->{path}) {
my $fetch = command_oneline('config', '--get',
"svn-remote.$repo_id.fetch",
- ":refs/remotes/$ref_id\$") or
+ ":$ref_id\$") or
die "Failed to read \"svn-remote.$repo_id.fetch\" ",
- "\":refs/remotes/$ref_id\$\" in config\n";
+ "\":$ref_id\$\" in config\n";
($self->{path}, undef) = split(/\s*:\s*/, $fetch);
}
$self->{url} = command_oneline('config', '--get',
@@ -1983,7 +2009,7 @@ sub new {
}
sub refname {
- my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
+ my ($refname) = $_[0]->{ref_id} ;
# It cannot end with a slash /, we'll throw up on this because
# SVN can't have directories with a slash in their name, either:
@@ -3263,7 +3289,7 @@ sub _rev_map_get {
my $i = int(($l/24 + $u/24) / 2) * 24;
sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
sysread($fh, my $buf, 24) == 24 or croak "read: $!";
- my ($r, $c) = unpack('NH40', $buf);
+ my ($r, $c) = unpack(rev_map_fmt, $buf);
if ($r < $rev) {
$l = $i + 24;
@@ -3318,12 +3344,24 @@ sub _new {
$repo_id = $Git::SVN::default_repo_id;
}
unless (defined $ref_id && length $ref_id) {
- $_[2] = $ref_id = $Git::SVN::default_ref_id;
+ $_prefix = '' unless defined($_prefix);
+ $_[2] = $ref_id =
+ "refs/remotes/$_prefix$Git::SVN::default_ref_id";
}
$_[1] = $repo_id;
my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
+
+ # Older repos imported by us used $GIT_DIR/svn/foo instead of
+ # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
+ if ($ref_id =~ m{^refs/remotes/(.*)}) {
+ my $old_dir = "$ENV{GIT_DIR}/svn/$1";
+ if (-d $old_dir && ! -d $dir) {
+ $dir = $old_dir;
+ }
+ }
+
$_[3] = $path = '' unless (defined $path);
- mkpath(["$ENV{GIT_DIR}/svn"]);
+ mkpath([$dir]);
bless {
ref_id => $ref_id, dir => $dir, index => "$dir/index",
path => $path, config => "$ENV{GIT_DIR}/svn/config",
@@ -5496,7 +5534,7 @@ sub minimize_connections {
my $pfx = "svn-remote.$x->{old_repo_id}";
my $old_fetch = quotemeta("$x->{old_path}:".
- "refs/remotes/$x->{ref_id}");
+ "$x->{ref_id}");
command_noisy(qw/config --unset/,
"$pfx.fetch", '^'. $old_fetch . '$');
delete $r->{$x->{old_repo_id}}->
@@ -5565,7 +5603,7 @@ sub new {
my ($class, $glob) = @_;
my $re = $glob;
$re =~ s!/+$!!g; # no need for trailing slashes
- $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!;
+ $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!;
my $temp = $re;
my ($left, $right) = ($1, $3);
$re = $2;
diff --git a/git-web--browse.sh b/git-web--browse.sh
index 4f5c740df..a578c3a73 100755
--- a/git-web--browse.sh
+++ b/git-web--browse.sh
@@ -111,7 +111,8 @@ if test -z "$browser" ; then
browser_candidates="w3m links lynx"
fi
# SECURITYSESSIONID indicates an OS X GUI login session
- if test -n "$SECURITYSESSIONID"; then
+ if test -n "$SECURITYSESSIONID" \
+ -o "$TERM_PROGRAM" = "Apple_Terminal" ; then
browser_candidates="open $browser_candidates"
fi
# /bin/start indicates MinGW
diff --git a/git.c b/git.c
index 807d875ae..9883009b5 100644
--- a/git.c
+++ b/git.c
@@ -5,7 +5,10 @@
#include "run-command.h"
const char git_usage_string[] =
- "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS]";
+ "git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
+ " [-p|--paginate|--no-pager]\n"
+ " [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
+ " [--help] COMMAND [ARGS]";
const char git_more_info_string[] =
"See 'git help COMMAND' for more information on a specific command.";
@@ -309,9 +312,6 @@ static void handle_internal_command(int argc, const char **argv)
{ "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP | USE_PAGER },
{ "help", cmd_help },
-#ifndef NO_CURL
- { "http-fetch", cmd_http_fetch, RUN_SETUP },
-#endif
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },
{ "log", cmd_log, RUN_SETUP | USE_PAGER },
@@ -339,6 +339,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
+ { "replace", cmd_replace, RUN_SETUP },
{ "repo-config", cmd_config },
{ "rerere", cmd_rerere, RUN_SETUP },
{ "reset", cmd_reset, RUN_SETUP },
@@ -358,6 +359,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "unpack-objects", cmd_unpack_objects, RUN_SETUP },
{ "update-index", cmd_update_index, RUN_SETUP },
{ "update-ref", cmd_update_ref, RUN_SETUP },
+ { "update-server-info", cmd_update_server_info, RUN_SETUP },
{ "upload-archive", cmd_upload_archive },
{ "verify-tag", cmd_verify_tag, RUN_SETUP },
{ "version", cmd_version },
@@ -416,13 +418,9 @@ static void execv_dashed_external(const char **argv)
* if we fail because the command is not found, it is
* OK to return. Otherwise, we just pass along the status code.
*/
- status = run_command_v_opt(argv, 0);
- if (status != -ERR_RUN_COMMAND_EXEC) {
- if (IS_RUN_COMMAND_ERR(status))
- die("unable to run '%s'", argv[0]);
- exit(-status);
- }
- errno = ENOENT; /* as if we called execvp */
+ status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+ if (status >= 0 || errno != ENOENT)
+ exit(status);
argv[0] = tmp;
diff --git a/git.spec.in b/git.spec.in
index 4be0834f0..ab224f7ea 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -233,7 +233,7 @@ rm -rf $RPM_BUILD_ROOT
* Tue Mar 27 2007 Eygene Ryabinkin <rea-git@codelabs.ru>
- Added the git-p4 package: Perforce import stuff.
-* Mon Feb 13 2007 Nicolas Pitre <nico@cam.org>
+* Mon Feb 13 2007 Nicolas Pitre <nico@fluxnic.net>
- Update core package description (Git isn't as stupid as it used to be)
* Mon Feb 12 2007 Junio C Hamano <junkio@cox.net>
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 4604c831f..8c08310e6 100644
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -288,7 +288,7 @@ proc parseviewrevs {view revs} {
if {$sdm != 2} {
lappend ret $id
} else {
- lset ret end [lindex $ret end]...$id
+ lset ret end $id...[lindex $ret end]
}
lappend pos $id
}
@@ -1677,6 +1677,7 @@ proc readrefs {} {
global tagids idtags headids idheads tagobjid
global otherrefids idotherrefs mainhead mainheadid
global selecthead selectheadid
+ global hideremotes
foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
catch {unset $v}
@@ -1689,7 +1690,7 @@ proc readrefs {} {
if {![string match "refs/*" $ref]} continue
set name [string range $ref 5 end]
if {[string match "remotes/*" $name]} {
- if {![string match "*/HEAD" $name]} {
+ if {![string match "*/HEAD" $name] && !$hideremotes} {
set headids($name) $id
lappend idheads($id) $name
}
@@ -2520,6 +2521,7 @@ proc savestuff {w} {
global cmitmode wrapcomment datetimeformat limitdiffs
global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
global autoselect extdifftool perfile_attrs markbgcolor
+ global hideremotes
if {$stuffsaved} return
if {![winfo viewable .]} return
@@ -2539,6 +2541,7 @@ proc savestuff {w} {
puts $f [list set wrapcomment $wrapcomment]
puts $f [list set autoselect $autoselect]
puts $f [list set showneartags $showneartags]
+ puts $f [list set hideremotes $hideremotes]
puts $f [list set showlocalchanges $showlocalchanges]
puts $f [list set datetimeformat $datetimeformat]
puts $f [list set limitdiffs $limitdiffs]
@@ -7906,6 +7909,11 @@ proc gotocommit {} {
}
set id [lindex $matches 0]
}
+ } else {
+ if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
+ error_popup [mc "Revision %s is not known" $sha1string]
+ return
+ }
}
}
if {[commitinview $id $curview]} {
@@ -7915,7 +7923,7 @@ proc gotocommit {} {
if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
set msg [mc "SHA1 id %s is not known" $sha1string]
} else {
- set msg [mc "Tag/Head %s is not known" $sha1string]
+ set msg [mc "Revision %s is not in the current view" $sha1string]
}
error_popup $msg
}
@@ -10383,6 +10391,7 @@ proc doprefs {} {
global oldprefs prefstop showneartags showlocalchanges
global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
global tabstop limitdiffs autoselect extdifftool perfile_attrs
+ global hideremotes
set top .gitkprefs
set prefstop $top
@@ -10391,7 +10400,7 @@ proc doprefs {} {
return
}
foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
- limitdiffs tabstop perfile_attrs} {
+ limitdiffs tabstop perfile_attrs hideremotes} {
set oldprefs($v) [set $v]
}
toplevel $top
@@ -10423,6 +10432,9 @@ proc doprefs {} {
checkbutton $top.ntag -text [mc "Display nearby tags"] \
-font optionfont -variable showneartags
grid x $top.ntag -sticky w
+ checkbutton $top.hideremotes -text [mc "Hide remote refs"] \
+ -font optionfont -variable hideremotes
+ grid x $top.hideremotes -sticky w
checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
-font optionfont -variable limitdiffs
grid x $top.ldiff -sticky w
@@ -10547,7 +10559,7 @@ proc prefscan {} {
global oldprefs prefstop
foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
- limitdiffs tabstop perfile_attrs} {
+ limitdiffs tabstop perfile_attrs hideremotes} {
global $v
set $v $oldprefs($v)
}
@@ -10561,6 +10573,7 @@ proc prefsok {} {
global oldprefs prefstop showneartags showlocalchanges
global fontpref mainfont textfont uifont
global limitdiffs treediffs perfile_attrs
+ global hideremotes
catch {destroy $prefstop}
unset prefstop
@@ -10606,6 +10619,9 @@ proc prefsok {} {
$limitdiffs != $oldprefs(limitdiffs)} {
reselectline
}
+ if {$hideremotes != $oldprefs(hideremotes)} {
+ rereadrefs
+ }
}
proc formatdate {d} {
@@ -10901,7 +10917,7 @@ proc gitattr {path attr default} {
} else {
set r "unspecified"
if {![catch {set line [exec git check-attr $attr -- $path]}]} {
- regexp "(.*): encoding: (.*)" $line m f r
+ regexp "(.*): $attr: (.*)" $line m f r
}
set path_attr_cache($attr,$path) $r
}
@@ -10929,7 +10945,7 @@ proc cache_gitattr {attr pathlist} {
set newlist [lrange $newlist $lim end]
if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
foreach row [split $rlist "\n"] {
- if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+ if {[regexp "(.*): $attr: (.*)" $row m path value]} {
if {[string index $path 0] eq "\""} {
set path [encoding convertfrom [lindex $path 0]]
}
@@ -11011,6 +11027,7 @@ set mingaplen 100
set cmitmode "patch"
set wrapcomment "none"
set showneartags 1
+set hideremotes 0
set maxrefs 20
set maxlinelen 200
set showlocalchanges 1
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 18c9ce35e..b76a0cfff 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -123,6 +123,15 @@ GITWEB_CONFIG file:
$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
$feature{'snapshot'}{'override'} = 1;
+If you allow overriding for the snapshot feature, you can specify which
+snapshot formats are globally disabled. You can also add any command line
+options you want (such as setting the compression level). For instance,
+you can disable Zip compressed snapshots and set GZip to run at level 6 by
+adding the following lines to your $GITWEB_CONFIG:
+
+ $known_snapshot_formats{'zip'}{'disabled'} = 1;
+ $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
+
Gitweb repositories
-------------------
diff --git a/gitweb/git-favicon.png b/gitweb/git-favicon.png
index de637c060..aae35a70e 100644
--- a/gitweb/git-favicon.png
+++ b/gitweb/git-favicon.png
Binary files differ
diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png
index 16ae8d538..f4ede2e94 100644
--- a/gitweb/git-logo.png
+++ b/gitweb/git-logo.png
Binary files differ
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index d05bc3764..8f68fe309 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -226,22 +226,30 @@ th {
text-align: left;
}
-tr.light:hover {
- background-color: #edece6;
-}
-
-tr.dark {
- background-color: #f6f6f0;
+/* do not change row style on hover for 'blame' view */
+tr.light,
+table.blame .light:hover {
+ background-color: #ffffff;
}
-tr.dark2 {
+tr.dark,
+table.blame .dark:hover {
background-color: #f6f6f0;
}
+/* currently both use the same, but it can change */
+tr.light:hover,
tr.dark:hover {
background-color: #edece6;
}
+/* boundary commits in 'blame' view */
+/* and commits without "previous" */
+tr.boundary td.sha1,
+tr.no-previous td.linenr {
+ font-weight: bold;
+}
+
td {
padding: 2px 5px;
font-size: 100%;
@@ -262,7 +270,7 @@ td.sha1 {
font-family: monospace;
}
-td.error {
+.error {
color: red;
background-color: yellow;
}
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 2cb832753..24b219310 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -160,7 +160,8 @@ our %known_snapshot_formats = (
# 'suffix' => filename suffix,
# 'format' => --format for git-archive,
# 'compressor' => [compressor command and arguments]
- # (array reference, optional)}
+ # (array reference, optional)
+ # 'disabled' => boolean (optional)}
#
'tgz' => {
'display' => 'tar.gz',
@@ -176,6 +177,14 @@ our %known_snapshot_formats = (
'format' => 'tar',
'compressor' => ['bzip2']},
+ 'txz' => {
+ 'display' => 'tar.xz',
+ 'type' => 'application/x-xz',
+ 'suffix' => '.tar.xz',
+ 'format' => 'tar',
+ 'compressor' => ['xz'],
+ 'disabled' => 1},
+
'zip' => {
'display' => 'zip',
'type' => 'application/x-zip',
@@ -188,6 +197,7 @@ our %known_snapshot_formats = (
our %known_snapshot_format_aliases = (
'gzip' => 'tgz',
'bzip2' => 'tbz2',
+ 'xz' => 'txz',
# backward compatibility: legacy gitweb config support
'x-gzip' => undef, 'gz' => undef,
@@ -494,7 +504,8 @@ sub filter_snapshot_fmts {
exists $known_snapshot_format_aliases{$_} ?
$known_snapshot_format_aliases{$_} : $_} @fmts;
@fmts = grep {
- exists $known_snapshot_formats{$_} } @fmts;
+ exists $known_snapshot_formats{$_} &&
+ !$known_snapshot_formats{$_}{'disabled'}} @fmts;
}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@ -940,10 +951,13 @@ sub href {
if (defined $params{'hash_parent_base'}) {
$href .= esc_url($params{'hash_parent_base'});
# skip the file_parent if it's the same as the file_name
- delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
- if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
- $href .= ":/".esc_url($params{'file_parent'});
- delete $params{'file_parent'};
+ if (defined $params{'file_parent'}) {
+ if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
+ delete $params{'file_parent'};
+ } elsif ($params{'file_parent'} !~ /\.\./) {
+ $href .= ":/".esc_url($params{'file_parent'});
+ delete $params{'file_parent'};
+ }
}
$href .= "..";
delete $params{'hash_parent'};
@@ -1510,10 +1524,10 @@ sub format_subject_html {
$long =~ s/[[:cntrl:]]/?/g;
return $cgi->a({-href => $href, -class => "list subject",
-title => to_utf8($long)},
- esc_html($short) . $extra);
+ esc_html($short)) . $extra;
} else {
return $cgi->a({-href => $href, -class => "list subject"},
- esc_html($long) . $extra);
+ esc_html($long)) . $extra;
}
}
@@ -4800,7 +4814,7 @@ sub git_blame {
git_print_page_path($file_name, $ftype, $hash_base);
# page body
- my @rev_color = qw(light2 dark2);
+ my @rev_color = qw(light dark);
my $num_colors = scalar(@rev_color);
my $current_color = 0;
my %metainfo = ();
@@ -4818,15 +4832,18 @@ HTML
my ($full_rev, $orig_lineno, $lineno, $group_size) =
($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
if (!exists $metainfo{$full_rev}) {
- $metainfo{$full_rev} = {};
+ $metainfo{$full_rev} = { 'nprevious' => 0 };
}
my $meta = $metainfo{$full_rev};
my $data;
while ($data = <$fd>) {
chomp $data;
last if ($data =~ s/^\t//); # contents of line
- if ($data =~ /^(\S+) (.*)$/) {
- $meta->{$1} = $2;
+ if ($data =~ /^(\S+)(?: (.*))?$/) {
+ $meta->{$1} = $2 unless exists $meta->{$1};
+ }
+ if ($data =~ /^previous /) {
+ $meta->{'nprevious'}++;
}
}
my $short_rev = substr($full_rev, 0, 8);
@@ -4837,7 +4854,11 @@ HTML
if ($group_size) {
$current_color = ($current_color + 1) % $num_colors;
}
- print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
+ my $tr_class = $rev_color[$current_color];
+ $tr_class .= ' boundary' if (exists $meta->{'boundary'});
+ $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);
+ $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);
+ print "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
if ($group_size) {
print "<td class=\"sha1\"";
print " title=\"". esc_html($author) . ", $date\"";
@@ -4847,22 +4868,31 @@ HTML
hash=>$full_rev,
file_name=>$file_name)},
esc_html($short_rev));
+ if ($group_size >= 2) {
+ my @author_initials = ($author =~ /\b([[:upper:]])\B/g);
+ if (@author_initials) {
+ print "<br />" .
+ esc_html(join('', @author_initials));
+ # or join('.', ...)
+ }
+ }
print "</td>\n";
}
- my $parent_commit;
- if (!exists $meta->{'parent'}) {
- open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
- or die_error(500, "Open git-rev-parse failed");
- $parent_commit = <$dd>;
- close $dd;
- chomp($parent_commit);
- $meta->{'parent'} = $parent_commit;
- } else {
- $parent_commit = $meta->{'parent'};
- }
+ # 'previous' <sha1 of parent commit> <filename at commit>
+ if (exists $meta->{'previous'} &&
+ $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
+ $meta->{'parent'} = $1;
+ $meta->{'file_parent'} = unquote($2);
+ }
+ my $linenr_commit =
+ exists($meta->{'parent'}) ?
+ $meta->{'parent'} : $full_rev;
+ my $linenr_filename =
+ exists($meta->{'file_parent'}) ?
+ $meta->{'file_parent'} : unquote($meta->{'filename'});
my $blamed = href(action => 'blame',
- file_name => $meta->{'filename'},
- hash_base => $parent_commit);
+ file_name => $linenr_filename,
+ hash_base => $linenr_commit);
print "<td class=\"linenr\">";
print $cgi->a({ -href => "$blamed#l$orig_lineno",
-class => "linenr" },
@@ -5160,6 +5190,8 @@ sub git_snapshot {
die_error(400, "Invalid snapshot format parameter");
} elsif (!exists($known_snapshot_formats{$format})) {
die_error(400, "Unknown snapshot format");
+ } elsif ($known_snapshot_formats{$format}{'disabled'}) {
+ die_error(403, "Snapshot format not allowed");
} elsif (!grep($_ eq $format, @snapshot_fmts)) {
die_error(403, "Unsupported snapshot format");
}
diff --git a/graph.c b/graph.c
index e46677020..6746d422a 100644
--- a/graph.c
+++ b/graph.c
@@ -225,7 +225,12 @@ struct git_graph *graph_init(struct rev_info *opt)
graph->num_columns = 0;
graph->num_new_columns = 0;
graph->mapping_size = 0;
- graph->default_column_color = 0;
+ /*
+ * Start the column color at the maximum value, since we'll
+ * 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;
/*
* Allocate a reasonably large default number of columns
@@ -286,9 +291,10 @@ static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
}
/*
- * Uninteresting and pruned commits won't be printed
+ * Otherwise, use get_commit_action() to see if this commit is
+ * interesting
*/
- return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1;
+ return get_commit_action(graph->revs, commit) == commit_show;
}
static struct commit_list *next_interesting_parent(struct git_graph *graph,
@@ -499,11 +505,14 @@ static void graph_update_columns(struct git_graph *graph)
parent;
parent = next_interesting_parent(graph, parent)) {
/*
- * If this is a merge increment the current
+ * If this is a merge, or the start of a new
+ * childless column, increment the current
* color.
*/
- if (graph->num_parents > 1)
+ if (graph->num_parents > 1 ||
+ !is_commit_in_columns) {
graph_increment_column_color(graph);
+ }
graph_insert_into_new_columns(graph,
parent->item,
&mapping_idx);
diff --git a/grep.h b/grep.h
index 5581363c4..f6eecc62c 100644
--- a/grep.h
+++ b/grep.h
@@ -80,6 +80,7 @@ struct grep_opt {
int pathname;
int null_following_name;
int color;
+ int max_depth;
int funcname;
char color_match[COLOR_MAXLEN];
const char *color_external;
diff --git a/builtin-http-fetch.c b/http-fetch.c
index f3e63d720..e8f44babd 100644
--- a/builtin-http-fetch.c
+++ b/http-fetch.c
@@ -1,8 +1,9 @@
#include "cache.h"
#include "walker.h"
-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
{
+ const char *prefix;
struct walker *walker;
int commits_on_stdin = 0;
int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
int get_verbosely = 0;
int get_recover = 0;
+ prefix = setup_git_directory();
+
git_config(git_default_config, NULL);
while (arg < argc && argv[arg][0] == '-') {
diff --git a/http.c b/http.c
index 15926d8d6..23b2a1932 100644
--- a/http.c
+++ b/http.c
@@ -995,7 +995,6 @@ int finish_http_pack_request(struct http_pack_request *preq)
struct http_pack_request *new_http_pack_request(
struct packed_git *target, const char *base_url)
{
- char *url;
char *filename;
long prev_posn = 0;
char range[RANGE_HEADER_SIZE];
@@ -1009,8 +1008,7 @@ struct http_pack_request *new_http_pack_request(
end_url_with_slash(&buf, base_url);
strbuf_addf(&buf, "objects/pack/pack-%s.pack",
sha1_to_hex(target->sha1));
- url = strbuf_detach(&buf, NULL);
- preq->url = xstrdup(url);
+ preq->url = strbuf_detach(&buf, NULL);
filename = sha1_pack_name(target->sha1);
snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
@@ -1026,7 +1024,7 @@ struct http_pack_request *new_http_pack_request(
preq->slot->local = preq->packfile;
curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
- curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
no_pragma_header);
@@ -1050,6 +1048,8 @@ struct http_pack_request *new_http_pack_request(
abort:
free(filename);
+ free(preq->url);
+ free(preq);
return NULL;
}
@@ -1089,7 +1089,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
char *hex = sha1_to_hex(sha1);
char *filename;
char prevfile[PATH_MAX];
- char *url;
int prevlocal;
unsigned char prev_buf[PREV_BUF_SIZE];
ssize_t prev_read = 0;
@@ -1143,8 +1142,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
git_SHA1_Init(&freq->c);
- url = get_remote_object_url(base_url, hex, 0);
- freq->url = xstrdup(url);
+ freq->url = get_remote_object_url(base_url, hex, 0);
/*
* If a previous temp file is present, process what was already
@@ -1180,7 +1178,11 @@ struct http_object_request *new_http_object_request(const char *base_url,
if (prev_posn>0) {
prev_posn = 0;
lseek(freq->localfile, 0, SEEK_SET);
- ftruncate(freq->localfile, 0);
+ if (ftruncate(freq->localfile, 0) < 0) {
+ error("Couldn't truncate temporary file %s for %s: %s",
+ freq->tmpfile, freq->filename, strerror(errno));
+ goto abort;
+ }
}
}
@@ -1189,7 +1191,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
- curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
/*
@@ -1209,9 +1211,9 @@ struct http_object_request *new_http_object_request(const char *base_url,
return freq;
- free(url);
abort:
free(filename);
+ free(freq->url);
free(freq);
return NULL;
}
diff --git a/ll-merge.c b/ll-merge.c
index 0571564dd..2d6b6d6cb 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -192,10 +192,6 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
args[2] = cmd.buf;
status = run_command_v_opt(args, 0);
- if (status < -ERR_RUN_COMMAND_FORK)
- ; /* failure in run-command */
- else
- status = -status;
fd = open(temp[1], O_RDONLY);
if (fd < 0)
goto bad;
diff --git a/merge-recursive.c b/merge-recursive.c
index 10d7913b0..f55b7ebe1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -170,6 +170,18 @@ static int git_merge_trees(int index_only,
int rc;
struct tree_desc t[3];
struct unpack_trees_options opts;
+ static const 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,
+ };
memset(&opts, 0, sizeof(opts));
if (index_only)
@@ -181,6 +193,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 = msgs;
init_tree_desc_from_tree(t+0, common);
init_tree_desc_from_tree(t+1, head);
@@ -1188,10 +1201,14 @@ int merge_trees(struct merge_options *o,
code = git_merge_trees(o->call_depth, common, head, merge);
- if (code != 0)
- die("merging of trees %s and %s failed",
- sha1_to_hex(head->object.sha1),
- sha1_to_hex(merge->object.sha1));
+ if (code != 0) {
+ if (show(o, 4) || o->call_depth)
+ die("merging of trees %s and %s failed",
+ sha1_to_hex(head->object.sha1),
+ sha1_to_hex(merge->object.sha1));
+ else
+ exit(128);
+ }
if (unmerged_cache()) {
struct string_list *entries, *re_head, *re_merge;
diff --git a/mktag.c b/mktag.c
index a609e3ebd..a3b4270c1 100644
--- a/mktag.c
+++ b/mktag.c
@@ -19,16 +19,17 @@
/*
* We refuse to tag something we can't verify. Just because.
*/
-static int verify_object(unsigned char *sha1, const char *expected_type)
+static int verify_object(const unsigned char *sha1, const char *expected_type)
{
int ret = -1;
enum object_type type;
unsigned long size;
- void *buffer = read_sha1_file(sha1, &type, &size);
+ const unsigned char *repl;
+ void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
if (buffer) {
if (type == type_from_string(expected_type))
- ret = check_sha1_signature(sha1, buffer, size, expected_type);
+ ret = check_sha1_signature(repl, buffer, size, expected_type);
free(buffer);
}
return ret;
diff --git a/mozilla-sha1/sha1.c b/mozilla-sha1/sha1.c
deleted file mode 100644
index 95a4ebf49..000000000
--- a/mozilla-sha1/sha1.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Reference Implementation (Compact version)
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-#include "sha1.h"
-
-static void shaHashBlock(moz_SHA_CTX *ctx);
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx) {
- int i;
-
- ctx->lenW = 0;
- ctx->sizeHi = ctx->sizeLo = 0;
-
- /* Initialize H with the magic constants (see FIPS180 for constants)
- */
- ctx->H[0] = 0x67452301;
- ctx->H[1] = 0xefcdab89;
- ctx->H[2] = 0x98badcfe;
- ctx->H[3] = 0x10325476;
- ctx->H[4] = 0xc3d2e1f0;
-
- for (i = 0; i < 80; i++)
- ctx->W[i] = 0;
-}
-
-
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *_dataIn, int len) {
- const unsigned char *dataIn = _dataIn;
- int i;
-
- /* Read the data into W and process blocks as they get full
- */
- for (i = 0; i < len; i++) {
- ctx->W[ctx->lenW / 4] <<= 8;
- ctx->W[ctx->lenW / 4] |= (unsigned int)dataIn[i];
- if ((++ctx->lenW) % 64 == 0) {
- shaHashBlock(ctx);
- ctx->lenW = 0;
- }
- ctx->sizeLo += 8;
- ctx->sizeHi += (ctx->sizeLo < 8);
- }
-}
-
-
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx) {
- unsigned char pad0x80 = 0x80;
- unsigned char pad0x00 = 0x00;
- unsigned char padlen[8];
- int i;
-
- /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
- */
- padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
- padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
- padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
- padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
- padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
- padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
- padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
- padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
- moz_SHA1_Update(ctx, &pad0x80, 1);
- while (ctx->lenW != 56)
- moz_SHA1_Update(ctx, &pad0x00, 1);
- moz_SHA1_Update(ctx, padlen, 8);
-
- /* Output hash
- */
- for (i = 0; i < 20; i++) {
- hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
- ctx->H[i / 4] <<= 8;
- }
-
- /*
- * Re-initialize the context (also zeroizes contents)
- */
- moz_SHA1_Init(ctx);
-}
-
-
-#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n))))
-
-static void shaHashBlock(moz_SHA_CTX *ctx) {
- int t;
- unsigned int A,B,C,D,E,TEMP;
-
- for (t = 16; t <= 79; t++)
- ctx->W[t] =
- SHA_ROT(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
-
- A = ctx->H[0];
- B = ctx->H[1];
- C = ctx->H[2];
- D = ctx->H[3];
- E = ctx->H[4];
-
- for (t = 0; t <= 19; t++) {
- TEMP = SHA_ROT(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 20; t <= 39; t++) {
- TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 40; t <= 59; t++) {
- TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdc;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 60; t <= 79; t++) {
- TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
-
- ctx->H[0] += A;
- ctx->H[1] += B;
- ctx->H[2] += C;
- ctx->H[3] += D;
- ctx->H[4] += E;
-}
diff --git a/mozilla-sha1/sha1.h b/mozilla-sha1/sha1.h
deleted file mode 100644
index aa48a46cf..000000000
--- a/mozilla-sha1/sha1.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Header File
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-typedef struct {
- unsigned int H[5];
- unsigned int W[80];
- int lenW;
- unsigned int sizeHi,sizeLo;
-} moz_SHA_CTX;
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx);
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *dataIn, int len);
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx);
-
-#define git_SHA_CTX moz_SHA_CTX
-#define git_SHA1_Init moz_SHA1_Init
-#define git_SHA1_Update moz_SHA1_Update
-#define git_SHA1_Final moz_SHA1_Final
diff --git a/object.c b/object.c
index a6ef43919..fe8eaaf19 100644
--- a/object.c
+++ b/object.c
@@ -188,17 +188,18 @@ struct object *parse_object(const unsigned char *sha1)
unsigned long size;
enum object_type type;
int eaten;
- void *buffer = read_sha1_file(sha1, &type, &size);
+ const unsigned char *repl;
+ void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
if (buffer) {
struct object *obj;
- if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+ if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
free(buffer);
- error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+ error("sha1 mismatch %s\n", sha1_to_hex(repl));
return NULL;
}
- obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+ obj = parse_object_buffer(repl, type, size, buffer, &eaten);
if (!eaten)
free(buffer);
return obj;
diff --git a/pack-redundant.c b/pack-redundant.c
index 48a12bc13..69a7ab2e2 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -55,16 +55,15 @@ static inline struct llist_item *llist_item_get(void)
} else {
int i = 1;
new = xmalloc(sizeof(struct llist_item) * BLKSIZE);
- for(;i < BLKSIZE; i++) {
+ for (; i < BLKSIZE; i++)
llist_item_put(&new[i]);
- }
}
return new;
}
static void llist_free(struct llist *list)
{
- while((list->back = list->front)) {
+ while ((list->back = list->front)) {
list->front = list->front->next;
llist_item_put(list->back);
}
@@ -146,7 +145,7 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,
if (cmp > 0) { /* we insert before this entry */
return llist_insert(list, prev, sha1);
}
- if(!cmp) { /* already exists */
+ if (!cmp) { /* already exists */
return l;
}
prev = l;
@@ -168,7 +167,7 @@ redo_from_start:
int cmp = hashcmp(l->sha1, sha1);
if (cmp > 0) /* not in list, since sorted */
return prev;
- if(!cmp) { /* found */
+ if (!cmp) { /* found */
if (prev == NULL) {
if (hint != NULL && hint != list->front) {
/* we don't know the previous element */
@@ -218,7 +217,7 @@ static inline struct pack_list * pack_list_insert(struct pack_list **pl,
static inline size_t pack_list_size(struct pack_list *pl)
{
size_t ret = 0;
- while(pl) {
+ while (pl) {
ret++;
pl = pl->next;
}
@@ -396,7 +395,7 @@ static size_t get_pack_redundancy(struct pack_list *pl)
return 0;
while ((subset = pl->next)) {
- while(subset) {
+ while (subset) {
ret += sizeof_union(pl->pack, subset->pack);
subset = subset->next;
}
@@ -427,7 +426,7 @@ static void minimize(struct pack_list **min)
pl = local_packs;
while (pl) {
- if(pl->unique_objects->size)
+ if (pl->unique_objects->size)
pack_list_insert(&unique, pl);
else
pack_list_insert(&non_unique, pl);
@@ -479,7 +478,7 @@ static void minimize(struct pack_list **min)
*min = min_perm;
/* add the unique packs to the list */
pl = unique;
- while(pl) {
+ while (pl) {
pack_list_insert(min, pl);
pl = pl->next;
}
@@ -516,7 +515,7 @@ static void cmp_local_packs(void)
struct pack_list *subset, *pl = local_packs;
while ((subset = pl)) {
- while((subset = subset->next))
+ while ((subset = subset->next))
cmp_two_packs(pl, subset);
pl = pl->next;
}
@@ -608,23 +607,23 @@ int main(int argc, char **argv)
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
- if(!strcmp(arg, "--")) {
+ if (!strcmp(arg, "--")) {
i++;
break;
}
- if(!strcmp(arg, "--all")) {
+ if (!strcmp(arg, "--all")) {
load_all_packs = 1;
continue;
}
- if(!strcmp(arg, "--verbose")) {
+ if (!strcmp(arg, "--verbose")) {
verbose = 1;
continue;
}
- if(!strcmp(arg, "--alt-odb")) {
+ if (!strcmp(arg, "--alt-odb")) {
alt_odb = 1;
continue;
}
- if(*arg == '-')
+ if (*arg == '-')
usage(pack_redundant_usage);
else
break;
diff --git a/pager.c b/pager.c
index 492184357..f416d38ac 100644
--- a/pager.c
+++ b/pager.c
@@ -21,8 +21,6 @@ static void pager_preexec(void)
FD_ZERO(&in);
FD_SET(0, &in);
select(1, &in, NULL, &in, NULL);
-
- setenv("LESS", "FRSX", 0);
}
#endif
@@ -70,6 +68,10 @@ void setup_pager(void)
pager_argv[2] = pager;
pager_process.argv = pager_argv;
pager_process.in = -1;
+ if (!getenv("LESS")) {
+ static const char *env[] = { "LESS=FRSX", NULL };
+ pager_process.env = env;
+ }
#ifndef __MINGW32__
pager_process.preexec_cb = pager_preexec;
#endif
diff --git a/parse-options.c b/parse-options.c
index f7ce523a6..a64a4d6ee 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -511,7 +511,7 @@ static int usage_with_options_internal(const char * const *usagestr,
continue;
pos = fprintf(stderr, " ");
- if (opts->short_name) {
+ if (opts->short_name && !(opts->flags & PARSE_OPT_NEGHELP)) {
if (opts->flags & PARSE_OPT_NODASH)
pos += fprintf(stderr, "%c", opts->short_name);
else
@@ -520,7 +520,9 @@ static int usage_with_options_internal(const char * const *usagestr,
if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", ");
if (opts->long_name)
- pos += fprintf(stderr, "--%s", opts->long_name);
+ pos += fprintf(stderr, "--%s%s",
+ (opts->flags & PARSE_OPT_NEGHELP) ? "no-" : "",
+ opts->long_name);
if (opts->type == OPTION_NUMBER)
pos += fprintf(stderr, "-NUM");
@@ -547,6 +549,14 @@ void usage_with_options(const char * const *usagestr,
exit(129);
}
+void usage_msg_opt(const char *msg,
+ const char * const *usagestr,
+ const struct option *options)
+{
+ fprintf(stderr, "%s\n\n", msg);
+ usage_with_options(usagestr, options);
+}
+
int parse_options_usage(const char * const *usagestr,
const struct option *opts)
{
diff --git a/parse-options.h b/parse-options.h
index aba30671d..f295a2cf8 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -36,6 +36,7 @@ enum parse_opt_option_flags {
PARSE_OPT_LASTARG_DEFAULT = 16,
PARSE_OPT_NODASH = 32,
PARSE_OPT_LITERAL_ARGHELP = 64,
+ PARSE_OPT_NEGHELP = 128,
};
struct option;
@@ -80,6 +81,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
* PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets
* (i.e. '<argh>') in the help message.
* Useful for options with multiple parameters.
+ * PARSE_OPT_NEGHELP: says that the long option should always be shown with
+ * the --no prefix in the usage message. Sometimes
+ * useful for users of OPTION_NEGBIT.
*
* `callback`::
* pointer to the callback to use for OPTION_CALLBACK.
@@ -141,6 +145,10 @@ extern int parse_options(int argc, const char **argv, const char *prefix,
extern NORETURN void usage_with_options(const char * const *usagestr,
const struct option *options);
+extern NORETURN void usage_msg_opt(const char *msg,
+ const char * const *usagestr,
+ const struct option *options);
+
/*----- incremental advanced APIs -----*/
enum {
diff --git a/patch-delta.c b/patch-delta.c
index ef748ce96..e02e13bd4 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -2,7 +2,7 @@
* patch-delta.c:
* recreate a buffer from a source and the delta produced by diff-delta.c
*
- * (C) 2005 Nicolas Pitre <nico@cam.org>
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/progress.c b/progress.c
index 621c34edc..132ed95a3 100644
--- a/progress.c
+++ b/progress.c
@@ -1,7 +1,7 @@
/*
* Simple text-based progress display module for GIT
*
- * Copyright (c) 2007 by Nicolas Pitre <nico@cam.org>
+ * Copyright (c) 2007 by Nicolas Pitre <nico@fluxnic.net>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/read-cache.c b/read-cache.c
index 4e3e272ee..1bbaf1cff 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1065,7 +1065,18 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
return updated;
}
-int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec, char *seen)
+static void show_file(const char * fmt, const char * name, int in_porcelain,
+ int * first, char *header_msg)
+{
+ if (in_porcelain && *first && header_msg) {
+ printf("%s\n", header_msg);
+ *first=0;
+ }
+ printf(fmt, name);
+}
+
+int refresh_index(struct index_state *istate, unsigned int flags, const char **pathspec,
+ char *seen, char *header_msg)
{
int i;
int has_errors = 0;
@@ -1074,11 +1085,14 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
int quiet = (flags & REFRESH_QUIET) != 0;
int not_new = (flags & REFRESH_IGNORE_MISSING) != 0;
int ignore_submodules = (flags & REFRESH_IGNORE_SUBMODULES) != 0;
+ int first = 1;
+ int in_porcelain = (flags & REFRESH_IN_PORCELAIN);
unsigned int options = really ? CE_MATCH_IGNORE_VALID : 0;
- const char *needs_update_message;
+ const char *needs_update_fmt;
+ const char *needs_merge_fmt;
- needs_update_message = ((flags & REFRESH_SAY_CHANGED)
- ? "locally modified" : "needs update");
+ needs_update_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
+ needs_merge_fmt = (in_porcelain ? "U\t%s\n" : "%s: needs merge\n");
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *ce, *new;
int cache_errno = 0;
@@ -1094,7 +1108,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
i--;
if (allow_unmerged)
continue;
- printf("%s: needs merge\n", ce->name);
+ show_file(needs_merge_fmt, ce->name, in_porcelain, &first, header_msg);
has_errors = 1;
continue;
}
@@ -1117,7 +1131,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
}
if (quiet)
continue;
- printf("%s: %s\n", ce->name, needs_update_message);
+ show_file(needs_update_fmt, ce->name, in_porcelain, &first, header_msg);
has_errors = 1;
continue;
}
diff --git a/refs.c b/refs.c
index dd9c9ba3f..24865cf5a 100644
--- a/refs.c
+++ b/refs.c
@@ -668,6 +668,11 @@ 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_replace_ref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
+}
+
int for_each_rawref(each_ref_fn fn, void *cb_data)
{
return do_for_each_ref("refs/", fn, 0,
diff --git a/refs.h b/refs.h
index c11f6a6d5..777b5b7ca 100644
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,7 @@ extern int for_each_ref_in(const char *, each_ref_fn, void *);
extern int for_each_tag_ref(each_ref_fn, void *);
extern int for_each_branch_ref(each_ref_fn, void *);
extern int for_each_remote_ref(each_ref_fn, void *);
+extern int for_each_replace_ref(each_ref_fn, void *);
/* can be used to learn about broken ref and symref */
extern int for_each_rawref(each_ref_fn, void *);
diff --git a/remote-curl.c b/remote-curl.c
new file mode 100644
index 000000000..ad6a1637b
--- /dev/null
+++ b/remote-curl.c
@@ -0,0 +1,139 @@
+#include "cache.h"
+#include "remote.h"
+#include "strbuf.h"
+#include "walker.h"
+#include "http.h"
+
+static struct ref *get_refs(struct walker *walker, const char *url)
+{
+ struct strbuf buffer = STRBUF_INIT;
+ char *data, *start, *mid;
+ char *ref_name;
+ char *refs_url;
+ int i = 0;
+ int http_ret;
+
+ struct ref *refs = NULL;
+ struct ref *ref = NULL;
+ struct ref *last_ref = NULL;
+
+ refs_url = xmalloc(strlen(url) + 11);
+ sprintf(refs_url, "%s/info/refs", url);
+
+ http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
+ switch (http_ret) {
+ case HTTP_OK:
+ break;
+ case HTTP_MISSING_TARGET:
+ die("%s not found: did you run git update-server-info on the"
+ " server?", refs_url);
+ default:
+ http_error(refs_url, http_ret);
+ die("HTTP request failed");
+ }
+
+ data = buffer.buf;
+ start = NULL;
+ mid = data;
+ while (i < buffer.len) {
+ if (!start) {
+ start = &data[i];
+ }
+ if (data[i] == '\t')
+ mid = &data[i];
+ if (data[i] == '\n') {
+ data[i] = 0;
+ ref_name = mid + 1;
+ ref = xmalloc(sizeof(struct ref) +
+ strlen(ref_name) + 1);
+ memset(ref, 0, sizeof(struct ref));
+ strcpy(ref->name, ref_name);
+ get_sha1_hex(start, ref->old_sha1);
+ if (!refs)
+ refs = ref;
+ if (last_ref)
+ last_ref->next = ref;
+ last_ref = ref;
+ start = NULL;
+ }
+ i++;
+ }
+
+ strbuf_release(&buffer);
+
+ ref = alloc_ref("HEAD");
+ if (!walker->fetch_ref(walker, ref) &&
+ !resolve_remote_symref(ref, refs)) {
+ ref->next = refs;
+ refs = ref;
+ } else {
+ free(ref);
+ }
+
+ strbuf_release(&buffer);
+ free(refs_url);
+ return refs;
+}
+
+int main(int argc, const char **argv)
+{
+ struct remote *remote;
+ struct strbuf buf = STRBUF_INIT;
+ const char *url;
+ struct walker *walker = NULL;
+
+ setup_git_directory();
+ if (argc < 2) {
+ fprintf(stderr, "Remote needed\n");
+ return 1;
+ }
+
+ remote = remote_get(argv[1]);
+
+ if (argc > 2) {
+ url = argv[2];
+ } else {
+ url = remote->url[0];
+ }
+
+ do {
+ if (strbuf_getline(&buf, stdin, '\n') == EOF)
+ break;
+ if (!prefixcmp(buf.buf, "fetch ")) {
+ char *obj = buf.buf + strlen("fetch ");
+ if (!walker)
+ walker = get_http_walker(url, remote);
+ walker->get_all = 1;
+ walker->get_tree = 1;
+ walker->get_history = 1;
+ walker->get_verbosely = 0;
+ walker->get_recover = 0;
+ if (walker_fetch(walker, 1, &obj, NULL, NULL))
+ die("Fetch failed.");
+ printf("\n");
+ fflush(stdout);
+ } else if (!strcmp(buf.buf, "list")) {
+ struct ref *refs;
+ struct ref *posn;
+ if (!walker)
+ walker = get_http_walker(url, remote);
+ refs = get_refs(walker, url);
+ for (posn = refs; posn; posn = posn->next) {
+ if (posn->symref)
+ printf("@%s %s\n", posn->symref, posn->name);
+ else
+ printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+ }
+ printf("\n");
+ fflush(stdout);
+ } else if (!strcmp(buf.buf, "capabilities")) {
+ printf("fetch\n");
+ printf("\n");
+ fflush(stdout);
+ } else {
+ return 1;
+ }
+ strbuf_reset(&buf);
+ } while (1);
+ return 0;
+}
diff --git a/remote.c b/remote.c
index c3ada2d72..73d33f258 100644
--- a/remote.c
+++ b/remote.c
@@ -28,6 +28,11 @@ struct rewrite {
int instead_of_nr;
int instead_of_alloc;
};
+struct rewrites {
+ struct rewrite **rewrite;
+ int rewrite_alloc;
+ int rewrite_nr;
+};
static struct remote **remotes;
static int remotes_alloc;
@@ -41,14 +46,13 @@ static struct branch *current_branch;
static const char *default_remote_name;
static int explicit_default_remote_name;
-static struct rewrite **rewrite;
-static int rewrite_alloc;
-static int rewrite_nr;
+static struct rewrites rewrites;
+static struct rewrites rewrites_push;
#define BUF_SIZE (2048)
static char buffer[BUF_SIZE];
-static const char *alias_url(const char *url)
+static const char *alias_url(const char *url, struct rewrites *r)
{
int i, j;
char *ret;
@@ -57,14 +61,14 @@ static const char *alias_url(const char *url)
longest = NULL;
longest_i = -1;
- for (i = 0; i < rewrite_nr; i++) {
- if (!rewrite[i])
+ for (i = 0; i < r->rewrite_nr; i++) {
+ if (!r->rewrite[i])
continue;
- for (j = 0; j < rewrite[i]->instead_of_nr; j++) {
- if (!prefixcmp(url, rewrite[i]->instead_of[j].s) &&
+ for (j = 0; j < r->rewrite[i]->instead_of_nr; j++) {
+ if (!prefixcmp(url, r->rewrite[i]->instead_of[j].s) &&
(!longest ||
- longest->len < rewrite[i]->instead_of[j].len)) {
- longest = &(rewrite[i]->instead_of[j]);
+ longest->len < r->rewrite[i]->instead_of[j].len)) {
+ longest = &(r->rewrite[i]->instead_of[j]);
longest_i = i;
}
}
@@ -72,10 +76,10 @@ static const char *alias_url(const char *url)
if (!longest)
return url;
- ret = xmalloc(rewrite[longest_i]->baselen +
+ ret = xmalloc(r->rewrite[longest_i]->baselen +
(strlen(url) - longest->len) + 1);
- strcpy(ret, rewrite[longest_i]->base);
- strcpy(ret + rewrite[longest_i]->baselen, url + longest->len);
+ strcpy(ret, r->rewrite[longest_i]->base);
+ strcpy(ret + r->rewrite[longest_i]->baselen, url + longest->len);
return ret;
}
@@ -101,17 +105,25 @@ static void add_url(struct remote *remote, const char *url)
remote->url[remote->url_nr++] = url;
}
-static void add_url_alias(struct remote *remote, const char *url)
-{
- add_url(remote, alias_url(url));
-}
-
static void add_pushurl(struct remote *remote, const char *pushurl)
{
ALLOC_GROW(remote->pushurl, remote->pushurl_nr + 1, remote->pushurl_alloc);
remote->pushurl[remote->pushurl_nr++] = pushurl;
}
+static void add_pushurl_alias(struct remote *remote, const char *url)
+{
+ const char *pushurl = alias_url(url, &rewrites_push);
+ if (pushurl != url)
+ add_pushurl(remote, pushurl);
+}
+
+static void add_url_alias(struct remote *remote, const char *url)
+{
+ add_url(remote, alias_url(url, &rewrites));
+ add_pushurl_alias(remote, url);
+}
+
static struct remote *make_remote(const char *name, int len)
{
struct remote *ret;
@@ -169,22 +181,22 @@ static struct branch *make_branch(const char *name, int len)
return ret;
}
-static struct rewrite *make_rewrite(const char *base, int len)
+static struct rewrite *make_rewrite(struct rewrites *r, const char *base, int len)
{
struct rewrite *ret;
int i;
- for (i = 0; i < rewrite_nr; i++) {
+ for (i = 0; i < r->rewrite_nr; i++) {
if (len
- ? (len == rewrite[i]->baselen &&
- !strncmp(base, rewrite[i]->base, len))
- : !strcmp(base, rewrite[i]->base))
- return rewrite[i];
+ ? (len == r->rewrite[i]->baselen &&
+ !strncmp(base, r->rewrite[i]->base, len))
+ : !strcmp(base, r->rewrite[i]->base))
+ return r->rewrite[i];
}
- ALLOC_GROW(rewrite, rewrite_nr + 1, rewrite_alloc);
+ ALLOC_GROW(r->rewrite, r->rewrite_nr + 1, r->rewrite_alloc);
ret = xcalloc(1, sizeof(struct rewrite));
- rewrite[rewrite_nr++] = ret;
+ r->rewrite[r->rewrite_nr++] = ret;
if (len) {
ret->base = xstrndup(base, len);
ret->baselen = len;
@@ -355,8 +367,13 @@ static int handle_config(const char *key, const char *value, void *cb)
subkey = strrchr(name, '.');
if (!subkey)
return 0;
- rewrite = make_rewrite(name, subkey - name);
if (!strcmp(subkey, ".insteadof")) {
+ rewrite = make_rewrite(&rewrites, name, subkey - name);
+ if (!value)
+ return config_error_nonbool(key);
+ add_instead_of(rewrite, xstrdup(value));
+ } else if (!strcmp(subkey, ".pushinsteadof")) {
+ rewrite = make_rewrite(&rewrites_push, name, subkey - name);
if (!value)
return config_error_nonbool(key);
add_instead_of(rewrite, xstrdup(value));
@@ -430,13 +447,17 @@ static void alias_all_urls(void)
{
int i, j;
for (i = 0; i < remotes_nr; i++) {
+ int add_pushurl_aliases;
if (!remotes[i])
continue;
- for (j = 0; j < remotes[i]->url_nr; j++) {
- remotes[i]->url[j] = alias_url(remotes[i]->url[j]);
- }
for (j = 0; j < remotes[i]->pushurl_nr; j++) {
- remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j]);
+ remotes[i]->pushurl[j] = alias_url(remotes[i]->pushurl[j], &rewrites);
+ }
+ add_pushurl_aliases = remotes[i]->pushurl_nr == 0;
+ for (j = 0; j < remotes[i]->url_nr; j++) {
+ if (add_pushurl_aliases)
+ add_pushurl_alias(remotes[i], remotes[i]->url[j]);
+ remotes[i]->url[j] = alias_url(remotes[i]->url[j], &rewrites);
}
}
}
@@ -1038,7 +1059,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
case 0:
if (!memcmp(dst_value, "refs/", 5))
matched_dst = make_linked_ref(dst_value, dst_tail);
- else if((dst_guess = guess_ref(dst_value, matched_src)))
+ else if ((dst_guess = guess_ref(dst_value, matched_src)))
matched_dst = make_linked_ref(dst_guess, dst_tail);
else
error("unable to push to unqualified destination: %s\n"
diff --git a/replace_object.c b/replace_object.c
new file mode 100644
index 000000000..eb59604fd
--- /dev/null
+++ b/replace_object.c
@@ -0,0 +1,114 @@
+#include "cache.h"
+#include "sha1-lookup.h"
+#include "refs.h"
+
+static struct replace_object {
+ unsigned char sha1[2][20];
+} **replace_object;
+
+static int replace_object_alloc, replace_object_nr;
+
+static const unsigned char *replace_sha1_access(size_t index, void *table)
+{
+ struct replace_object **replace = table;
+ return replace[index]->sha1[0];
+}
+
+static int replace_object_pos(const unsigned char *sha1)
+{
+ return sha1_pos(sha1, replace_object, replace_object_nr,
+ replace_sha1_access);
+}
+
+static int register_replace_object(struct replace_object *replace,
+ int ignore_dups)
+{
+ int pos = replace_object_pos(replace->sha1[0]);
+
+ if (0 <= pos) {
+ if (ignore_dups)
+ free(replace);
+ else {
+ free(replace_object[pos]);
+ replace_object[pos] = replace;
+ }
+ return 1;
+ }
+ pos = -pos - 1;
+ if (replace_object_alloc <= ++replace_object_nr) {
+ replace_object_alloc = alloc_nr(replace_object_alloc);
+ replace_object = xrealloc(replace_object,
+ sizeof(*replace_object) *
+ replace_object_alloc);
+ }
+ if (pos < replace_object_nr)
+ memmove(replace_object + pos + 1,
+ replace_object + pos,
+ (replace_object_nr - pos - 1) *
+ sizeof(*replace_object));
+ replace_object[pos] = replace;
+ return 0;
+}
+
+static int register_replace_ref(const char *refname,
+ const unsigned char *sha1,
+ int flag, void *cb_data)
+{
+ /* Get sha1 from refname */
+ const char *slash = strrchr(refname, '/');
+ const char *hash = slash ? slash + 1 : refname;
+ struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
+
+ if (strlen(hash) != 40 || get_sha1_hex(hash, repl_obj->sha1[0])) {
+ free(repl_obj);
+ warning("bad replace ref name: %s", refname);
+ return 0;
+ }
+
+ /* Copy sha1 from the read ref */
+ hashcpy(repl_obj->sha1[1], sha1);
+
+ /* Register new object */
+ if (register_replace_object(repl_obj, 1))
+ die("duplicate replace ref: %s", refname);
+
+ return 0;
+}
+
+static void prepare_replace_object(void)
+{
+ static int replace_object_prepared;
+
+ if (replace_object_prepared)
+ return;
+
+ for_each_replace_ref(register_replace_ref, NULL);
+ replace_object_prepared = 1;
+}
+
+/* We allow "recursive" replacement. Only within reason, though */
+#define MAXREPLACEDEPTH 5
+
+const unsigned char *lookup_replace_object(const unsigned char *sha1)
+{
+ int pos, depth = MAXREPLACEDEPTH;
+ const unsigned char *cur = sha1;
+
+ if (!read_replace_refs)
+ return sha1;
+
+ prepare_replace_object();
+
+ /* Try to recursively replace the object */
+ do {
+ if (--depth < 0)
+ die("replace depth too high for object %s",
+ sha1_to_hex(sha1));
+
+ pos = replace_object_pos(cur);
+ if (0 <= pos)
+ cur = replace_object[pos]->sha1[1];
+ } while (0 <= pos);
+
+ return cur;
+}
diff --git a/rerere.c b/rerere.c
index 87360dc23..29f95f657 100644
--- a/rerere.c
+++ b/rerere.c
@@ -61,7 +61,7 @@ static int write_rr(struct string_list *rr, int out_fd)
path = rr->items[i].string;
length = strlen(path) + 1;
if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
- write_in_full(out_fd, "\t", 1) != 1 ||
+ write_str_in_full(out_fd, "\t") != 1 ||
write_in_full(out_fd, path, length) != length)
die("unable to write rerere record");
}
diff --git a/revision.c b/revision.c
index ce24ad9a8..35eca4a36 100644
--- a/revision.c
+++ b/revision.c
@@ -1664,7 +1664,7 @@ static inline int want_ancestry(struct rev_info *revs)
return (revs->rewrite_parents || revs->children.name);
}
-enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
{
if (commit->object.flags & SHOWN)
return commit_ignore;
@@ -1692,12 +1692,23 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
if (!commit->parents || !commit->parents->next)
return commit_ignore;
}
- if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
- return commit_error;
}
return commit_show;
}
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+ enum commit_action action = get_commit_action(revs, commit);
+
+ if (action == commit_show &&
+ !revs->show_all &&
+ revs->prune && revs->dense && want_ancestry(revs)) {
+ if (rewrite_parents(revs, commit) < 0)
+ return commit_error;
+ }
+ return action;
+}
+
static struct commit *get_revision_1(struct rev_info *revs)
{
if (!revs->commits)
diff --git a/revision.h b/revision.h
index b10984b60..9d0dddbcb 100644
--- a/revision.h
+++ b/revision.h
@@ -168,6 +168,7 @@ enum commit_action {
commit_error
};
+extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
#endif
diff --git a/run-command.c b/run-command.c
index ff3d8e2d8..ac314a5a8 100644
--- a/run-command.c
+++ b/run-command.c
@@ -19,6 +19,7 @@ int start_command(struct child_process *cmd)
{
int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2];
+ int failed_errno = failed_errno;
/*
* In case of errors we must keep the promise to close FDs
@@ -28,9 +29,10 @@ int start_command(struct child_process *cmd)
need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) {
if (pipe(fdin) < 0) {
+ failed_errno = errno;
if (cmd->out > 0)
close(cmd->out);
- return -ERR_RUN_COMMAND_PIPE;
+ goto fail_pipe;
}
cmd->in = fdin[1];
}
@@ -40,11 +42,12 @@ int start_command(struct child_process *cmd)
&& cmd->out < 0;
if (need_out) {
if (pipe(fdout) < 0) {
+ failed_errno = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
close(cmd->in);
- return -ERR_RUN_COMMAND_PIPE;
+ goto fail_pipe;
}
cmd->out = fdout[0];
}
@@ -52,6 +55,7 @@ int start_command(struct child_process *cmd)
need_err = !cmd->no_stderr && cmd->err < 0;
if (need_err) {
if (pipe(fderr) < 0) {
+ failed_errno = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
@@ -60,7 +64,11 @@ int start_command(struct child_process *cmd)
close_pair(fdout);
else if (cmd->out)
close(cmd->out);
- return -ERR_RUN_COMMAND_PIPE;
+fail_pipe:
+ error("cannot create pipe for %s: %s",
+ cmd->argv[0], strerror(failed_errno));
+ errno = failed_errno;
+ return -1;
}
cmd->err = fderr[0];
}
@@ -122,6 +130,9 @@ int start_command(struct child_process *cmd)
strerror(errno));
exit(127);
}
+ if (cmd->pid < 0)
+ error("cannot fork() for %s: %s", cmd->argv[0],
+ strerror(failed_errno = errno));
#else
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
const char **sargv = cmd->argv;
@@ -162,17 +173,17 @@ int start_command(struct child_process *cmd)
if (cmd->dir)
die("chdir in start_command() not implemented");
- if (cmd->env) {
- env = copy_environ();
- for (; *cmd->env; cmd->env++)
- env = env_setenv(env, *cmd->env);
- }
+ if (cmd->env)
+ env = make_augmented_environ(cmd->env);
if (cmd->git_cmd) {
cmd->argv = prepare_git_cmd(cmd->argv);
}
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
+ failed_errno = errno;
+ if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
+ error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
if (cmd->env)
free_environ(env);
@@ -189,7 +200,6 @@ int start_command(struct child_process *cmd)
#endif
if (cmd->pid < 0) {
- int err = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
@@ -200,9 +210,8 @@ int start_command(struct child_process *cmd)
close(cmd->out);
if (need_err)
close_pair(fderr);
- return err == ENOENT ?
- -ERR_RUN_COMMAND_EXEC :
- -ERR_RUN_COMMAND_FORK;
+ errno = failed_errno;
+ return -1;
}
if (need_in)
@@ -221,40 +230,51 @@ int start_command(struct child_process *cmd)
return 0;
}
-static int wait_or_whine(pid_t pid)
+static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{
- for (;;) {
- int status, code;
- pid_t waiting = waitpid(pid, &status, 0);
-
- if (waiting < 0) {
- if (errno == EINTR)
- continue;
- error("waitpid failed (%s)", strerror(errno));
- return -ERR_RUN_COMMAND_WAITPID;
- }
- if (waiting != pid)
- return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
- if (WIFSIGNALED(status))
- return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
-
- if (!WIFEXITED(status))
- return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
+ int status, code = -1;
+ pid_t waiting;
+ int failed_errno = 0;
+
+ while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+ ; /* nothing */
+
+ if (waiting < 0) {
+ failed_errno = errno;
+ error("waitpid for %s failed: %s", argv0, strerror(errno));
+ } else if (waiting != pid) {
+ error("waitpid is confused (%s)", argv0);
+ } else if (WIFSIGNALED(status)) {
+ code = WTERMSIG(status);
+ error("%s died of signal %d", argv0, code);
+ /*
+ * This return value is chosen so that code & 0xff
+ * mimics the exit code that a POSIX shell would report for
+ * a program that died from this signal.
+ */
+ code -= 128;
+ } else if (WIFEXITED(status)) {
code = WEXITSTATUS(status);
- switch (code) {
- case 127:
- return -ERR_RUN_COMMAND_EXEC;
- case 0:
- return 0;
- default:
- return -code;
+ /*
+ * Convert special exit code when execvp failed.
+ */
+ if (code == 127) {
+ code = -1;
+ failed_errno = ENOENT;
+ if (!silent_exec_failure)
+ error("cannot run %s: %s", argv0,
+ strerror(ENOENT));
}
+ } else {
+ error("waitpid is confused (%s)", argv0);
}
+ errno = failed_errno;
+ return code;
}
int finish_command(struct child_process *cmd)
{
- return wait_or_whine(cmd->pid);
+ return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
}
int run_command(struct child_process *cmd)
@@ -274,6 +294,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+ cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
}
int run_command_v_opt(const char **argv, int opt)
@@ -338,10 +359,7 @@ int start_async(struct async *async)
int finish_async(struct async *async)
{
#ifndef __MINGW32__
- int ret = 0;
-
- if (wait_or_whine(async->pid))
- ret = error("waitpid (async) failed");
+ int ret = wait_or_whine(async->pid, "child process", 0);
#else
DWORD ret = 0;
if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
@@ -385,15 +403,7 @@ int run_hook(const char *index_file, const char *name, ...)
hook.env = env;
}
- ret = start_command(&hook);
+ ret = run_command(&hook);
free(argv);
- if (ret) {
- warning("Could not spawn %s", argv[0]);
- return ret;
- }
- ret = finish_command(&hook);
- if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
- warning("%s exited due to uncaught signal", argv[0]);
-
return ret;
}
diff --git a/run-command.h b/run-command.h
index e34550284..0c00b25ff 100644
--- a/run-command.h
+++ b/run-command.h
@@ -1,17 +1,6 @@
#ifndef RUN_COMMAND_H
#define RUN_COMMAND_H
-enum {
- ERR_RUN_COMMAND_FORK = 10000,
- ERR_RUN_COMMAND_EXEC,
- ERR_RUN_COMMAND_PIPE,
- ERR_RUN_COMMAND_WAITPID,
- ERR_RUN_COMMAND_WAITPID_WRONG_PID,
- ERR_RUN_COMMAND_WAITPID_SIGNAL,
- ERR_RUN_COMMAND_WAITPID_NOEXIT,
-};
-#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
-
struct child_process {
const char **argv;
pid_t pid;
@@ -42,6 +31,7 @@ struct child_process {
unsigned no_stdout:1;
unsigned no_stderr:1;
unsigned git_cmd:1; /* if this is to be git sub-command */
+ unsigned silent_exec_failure:1;
unsigned stdout_to_stderr:1;
void (*preexec_cb)(void);
};
@@ -55,6 +45,7 @@ extern int run_hook(const char *index_file, const char *name, ...);
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4
+#define RUN_SILENT_EXEC_FAILURE 8
int run_command_v_opt(const char **argv, int opt);
/*
diff --git a/send-pack.h b/send-pack.h
index 1d7b1b3b4..8b3cf028e 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -3,6 +3,7 @@
struct send_pack_args {
unsigned verbose:1,
+ quiet:1,
send_mirror:1,
force_update:1,
use_thin_pack:1,
diff --git a/sha1_file.c b/sha1_file.c
index 1d996a199..4ea0b18d0 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2144,13 +2144,26 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
return read_packed_sha1(sha1, type, size);
}
-void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
- unsigned long *size)
+void *read_sha1_file_repl(const unsigned char *sha1,
+ enum object_type *type,
+ unsigned long *size,
+ const unsigned char **replacement)
{
- void *data = read_object(sha1, type, size);
+ const unsigned char *repl = lookup_replace_object(sha1);
+ void *data = read_object(repl, type, size);
+
+ /* die if we replaced an object with one that does not exist */
+ if (!data && repl != sha1)
+ die("replacement %s not found for %s",
+ sha1_to_hex(repl), sha1_to_hex(sha1));
+
/* legacy behavior is to die on corrupted objects */
- if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
- die("object %s is corrupted", sha1_to_hex(sha1));
+ if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
+ die("object %s is corrupted", sha1_to_hex(repl));
+
+ if (replacement)
+ *replacement = repl;
+
return data;
}
diff --git a/strbuf.c b/strbuf.c
index f03d11702..a6153dca2 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -322,7 +322,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
return -1;
}
-int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
{
int ch;
@@ -332,10 +332,10 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
strbuf_reset(sb);
while ((ch = fgetc(fp)) != EOF) {
- if (ch == term)
- break;
strbuf_grow(sb, 1);
sb->buf[sb->len++] = ch;
+ if (ch == term)
+ break;
}
if (ch == EOF && sb->len == 0)
return EOF;
@@ -344,6 +344,15 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
return 0;
}
+int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+{
+ if (strbuf_getwholeline(sb, fp, term))
+ return EOF;
+ if (sb->buf[sb->len-1] == term)
+ strbuf_setlen(sb, sb->len-1);
+ return 0;
+}
+
int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
{
int fd, len;
diff --git a/strbuf.h b/strbuf.h
index eaa8704d5..d05e056dd 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -126,6 +126,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
extern int strbuf_getline(struct strbuf *, FILE *, int);
extern void stripspace(struct strbuf *buf, int skip_comments);
diff --git a/t/Makefile b/t/Makefile
index bf816fc85..bd09390d3 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -3,6 +3,8 @@
# Copyright (c) 2005 Junio C Hamano
#
+-include ../config.mak
+
#GIT_TEST_OPTS=--verbose --debug
SHELL_PATH ?= $(SHELL)
TAR ?= $(TAR)
diff --git a/t/gitweb-lib.sh b/t/gitweb-lib.sh
new file mode 100644
index 000000000..845253274
--- /dev/null
+++ b/t/gitweb-lib.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Jakub Narebski
+#
+
+gitweb_init () {
+ safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
+ cat >gitweb_config.perl <<EOF
+#!/usr/bin/perl
+
+# gitweb configuration for tests
+
+our \$version = 'current';
+our \$GIT = 'git';
+our \$projectroot = "$safe_pwd";
+our \$project_maxdepth = 8;
+our \$home_link_str = 'projects';
+our \$site_name = '[localhost]';
+our \$site_header = '';
+our \$site_footer = '';
+our \$home_text = 'indextext.html';
+our @stylesheets = ('file:///$TEST_DIRECTORY/../gitweb/gitweb.css');
+our \$logo = 'file:///$TEST_DIRECTORY/../gitweb/git-logo.png';
+our \$favicon = 'file:///$TEST_DIRECTORY/../gitweb/git-favicon.png';
+our \$projects_list = '';
+our \$export_ok = '';
+our \$strict_export = '';
+
+EOF
+
+ cat >.git/description <<EOF
+$0 test repository
+EOF
+}
+
+gitweb_run () {
+ GATEWAY_INTERFACE='CGI/1.1'
+ HTTP_ACCEPT='*/*'
+ REQUEST_METHOD='GET'
+ SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
+ QUERY_STRING=""$1""
+ PATH_INFO=""$2""
+ export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
+ SCRIPT_NAME QUERY_STRING PATH_INFO
+
+ GITWEB_CONFIG=$(pwd)/gitweb_config.perl
+ export GITWEB_CONFIG
+
+ # some of git commands write to STDERR on error, but this is not
+ # written to web server logs, so we are not interested in that:
+ # we are interested only in properly formatted errors/warnings
+ rm -f gitweb.log &&
+ perl -- "$SCRIPT_NAME" \
+ >gitweb.output 2>gitweb.log &&
+ if grep '^[[]' gitweb.log >/dev/null 2>&1; then false; else true; fi
+
+ # gitweb.log is left for debugging
+ # gitweb.output is used to parse http output
+}
+
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ say 'skipping gitweb tests, perl not available'
+ test_done
+fi
+
+perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
+ say 'skipping gitweb tests, perl version is too old'
+ test_done
+}
+
+gitweb_init
diff --git a/t/lib-cvs.sh b/t/lib-cvs.sh
new file mode 100644
index 000000000..4b3b79373
--- /dev/null
+++ b/t/lib-cvs.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+. ./test-lib.sh
+
+unset CVS_SERVER
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
+if ! type cvs >/dev/null 2>&1
+then
+ say 'skipping cvsimport tests, cvs not found'
+ test_done
+fi
+
+CVS="cvs -f"
+export CVS
+
+cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+case "$cvsps_version" in
+2.1 | 2.2*)
+ ;;
+'')
+ say 'skipping cvsimport tests, cvsps not found'
+ test_done
+ ;;
+*)
+ say 'skipping cvsimport tests, unsupported cvsps version'
+ test_done
+ ;;
+esac
+
+test_cvs_co () {
+ # Usage: test_cvs_co BRANCH_NAME
+ rm -rf module-cvs-"$1"
+ if [ "$1" = "master" ]
+ then
+ $CVS co -P -d module-cvs-"$1" -A module
+ else
+ $CVS co -P -d module-cvs-"$1" -r "$1" module
+ fi
+}
+
+test_git_co () {
+ # Usage: test_git_co BRANCH_NAME
+ (cd module-git && git checkout "$1")
+}
+
+test_cmp_branch_file () {
+ # Usage: test_cmp_branch_file BRANCH_NAME PATH
+ # The branch must already be checked out of CVS and git.
+ test_cmp module-cvs-"$1"/"$2" module-git/"$2"
+}
+
+test_cmp_branch_tree () {
+ # Usage: test_cmp_branch_tree BRANCH_NAME
+ # Check BRANCH_NAME out of CVS and git and make sure that all
+ # of the files and directories are identical.
+
+ test_cvs_co "$1" &&
+ test_git_co "$1" &&
+ (
+ cd module-cvs-"$1"
+ find . -type d -name CVS -prune -o -type f -print
+ ) | sort >module-cvs-"$1".list &&
+ (
+ cd module-git
+ find . -type d -name .git -prune -o -type f -print
+ ) | sort >module-git-"$1".list &&
+ test_cmp module-cvs-"$1".list module-git-"$1".list &&
+ cat module-cvs-"$1".list | while read f
+ do
+ test_cmp_branch_file "$1" "$f" || return 1
+ done
+}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 565496234..fd8631f90 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then
fi
GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/svn/git-svn
+GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
svn >/dev/null 2>&1
diff --git a/t/lib-patch-mode.sh b/t/lib-patch-mode.sh
new file mode 100755
index 000000000..75a3ee283
--- /dev/null
+++ b/t/lib-patch-mode.sh
@@ -0,0 +1,41 @@
+. ./test-lib.sh
+
+if ! test_have_prereq PERL; then
+ say 'skipping --patch tests, perl not available'
+ test_done
+fi
+
+set_state () {
+ echo "$3" > "$1" &&
+ git add "$1" &&
+ echo "$2" > "$1"
+}
+
+save_state () {
+ noslash="$(echo "$1" | tr / _)" &&
+ cat "$1" > _worktree_"$noslash" &&
+ git show :"$1" > _index_"$noslash"
+}
+
+set_and_save_state () {
+ set_state "$@" &&
+ save_state "$1"
+}
+
+verify_state () {
+ test "$(cat "$1")" = "$2" &&
+ test "$(git show :"$1")" = "$3"
+}
+
+verify_saved_state () {
+ noslash="$(echo "$1" | tr / _)" &&
+ verify_state "$1" "$(cat _worktree_"$noslash")" "$(cat _index_"$noslash")"
+}
+
+save_head () {
+ git rev-parse HEAD > _head
+}
+
+verify_saved_head () {
+ test "$(cat _head)" = "$(git rev-parse HEAD)"
+}
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index e3d846420..538650479 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -208,4 +208,87 @@ test_expect_success 'init rejects insanely long --template' '
)
'
+test_expect_success 'init creates a new directory' '
+ rm -fr newdir &&
+ (
+ git init newdir &&
+ test -d newdir/.git/refs
+ )
+'
+
+test_expect_success 'init creates a new bare directory' '
+ rm -fr newdir &&
+ (
+ git init --bare newdir &&
+ test -d newdir/refs
+ )
+'
+
+test_expect_success 'init recreates a directory' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ git init newdir &&
+ test -d newdir/.git/refs
+ )
+'
+
+test_expect_success 'init recreates a new bare directory' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ git init --bare newdir &&
+ test -d newdir/refs
+ )
+'
+
+test_expect_success 'init creates a new deep directory' '
+ rm -fr newdir &&
+ git init newdir/a/b/c &&
+ test -d newdir/a/b/c/.git/refs
+'
+
+test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' '
+ rm -fr newdir &&
+ (
+ # Leading directories should honor umask while
+ # the repository itself should follow "shared"
+ umask 002 &&
+ git init --bare --shared=0660 newdir/a/b/c &&
+ test -d newdir/a/b/c/refs &&
+ ls -ld newdir/a newdir/a/b > lsab.out &&
+ ! grep -v "^drwxrw[sx]r-x" lsab.out &&
+ ls -ld newdir/a/b/c > lsc.out &&
+ ! grep -v "^drwxrw[sx]---" lsc.out
+ )
+'
+
+test_expect_success 'init notices EEXIST (1)' '
+ rm -fr newdir &&
+ (
+ >newdir &&
+ test_must_fail git init newdir &&
+ test -f newdir
+ )
+'
+
+test_expect_success 'init notices EEXIST (2)' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ >newdir/a
+ test_must_fail git init newdir/a/b &&
+ test -f newdir/a
+ )
+'
+
+test_expect_success POSIXPERM 'init notices EPERM' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ chmod -w newdir &&
+ test_must_fail git init newdir/a/b
+ )
+'
+
test_done
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
new file mode 100755
index 000000000..a4d8fa8fa
--- /dev/null
+++ b/t/t0006-date.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+test_description='test date parsing and printing'
+. ./test-lib.sh
+
+# arbitrary reference time: 2009-08-30 19:20:00
+TEST_DATE_NOW=1251660000; export TEST_DATE_NOW
+
+check_show() {
+ t=$(($TEST_DATE_NOW - $1))
+ echo "$t -> $2" >expect
+ test_expect_${3:-success} "relative date ($2)" "
+ test-date show $t >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_show 5 '5 seconds ago'
+check_show 300 '5 minutes ago'
+check_show 18000 '5 hours ago'
+check_show 432000 '5 days ago'
+check_show 1728000 '3 weeks ago'
+check_show 13000000 '5 months ago'
+check_show 37500000 '1 year, 2 months ago'
+check_show 55188000 '1 year, 9 months ago'
+check_show 630000000 '20 years ago'
+
+check_parse() {
+ echo "$1 -> $2" >expect
+ test_expect_${3:-success} "parse date ($1)" "
+ test-date parse '$1' >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_parse 2008 bad
+check_parse 2008-02 bad
+check_parse 2008-02-14 bad
+check_parse '2008-02-14 20:30:45' '2008-02-14 20:30:45 +0000'
+
+check_approxidate() {
+ echo "$1 -> $2 +0000" >expect
+ test_expect_${3:-success} "parse approxidate ($1)" "
+ test-date approxidate '$1' >actual &&
+ test_cmp expect actual
+ "
+}
+
+check_approxidate now '2009-08-30 19:20:00'
+check_approxidate '5 seconds ago' '2009-08-30 19:19:55'
+check_approxidate 5.seconds.ago '2009-08-30 19:19:55'
+check_approxidate 10.minutes.ago '2009-08-30 19:10:00'
+check_approxidate yesterday '2009-08-29 19:20:00'
+check_approxidate 3.days.ago '2009-08-27 19:20:00'
+check_approxidate 3.weeks.ago '2009-08-09 19:20:00'
+check_approxidate 3.months.ago '2009-05-30 19:20:00'
+check_approxidate 2.years.3.months.ago '2007-05-30 19:20:00'
+
+check_approxidate '6am yesterday' '2009-08-29 06:00:00'
+check_approxidate '6pm yesterday' '2009-08-29 18:00:00'
+check_approxidate '3:00' '2009-08-30 03:00:00'
+check_approxidate '15:00' '2009-08-30 15:00:00'
+check_approxidate 'noon today' '2009-08-30 12:00:00'
+check_approxidate 'noon yesterday' '2009-08-29 12:00:00'
+
+check_approxidate 'last tuesday' '2009-08-25 19:20:00'
+check_approxidate 'July 5th' '2009-07-05 19:20:00'
+check_approxidate '06/05/2009' '2009-06-05 19:20:00'
+check_approxidate '06.05.2009' '2009-05-06 19:20:00'
+
+check_approxidate 'Jun 6, 5AM' '2009-06-06 05:00:00'
+check_approxidate '5AM Jun 6' '2009-06-06 05:00:00'
+check_approxidate '6AM, June 7, 2009' '2009-06-07 06:00:00'
+
+test_done
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
new file mode 100755
index 000000000..59b3aa4bc
--- /dev/null
+++ b/t/t1009-read-tree-new-index.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='test read-tree into a fresh index file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >a &&
+ git add a &&
+ git commit -m initial
+'
+
+test_expect_success 'non-existent index file' '
+ rm -f new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_expect_success 'empty index file' '
+ rm -f new-index &&
+ > new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_done
+
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
new file mode 100755
index 000000000..4d1c2e9e0
--- /dev/null
+++ b/t/t2016-checkout-patch.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before dir/foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_and_save_state dir/foo work head &&
+ (echo n; echo n) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p' '
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p with staged changes' '
+ set_state dir/foo work index
+ (echo n; echo y) | git checkout -p &&
+ verify_saved_state bar &&
+ verify_state dir/foo index index
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: abort' '
+ set_and_save_state dir/foo work head &&
+ (echo n; echo y; echo n) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_saved_state dir/foo
+'
+
+test_expect_success 'git checkout -p HEAD with NO staged changes: apply' '
+ (echo n; echo y; echo y) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD with change already staged' '
+ set_state dir/foo index index
+ # the third n is to get out in case it mistakenly does not apply
+ (echo n; echo y; echo n) | git checkout -p HEAD &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'git checkout -p HEAD^' '
+ # the third n is to get out in case it mistakenly does not apply
+ (echo n; echo y; echo n) | git checkout -p HEAD^ &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent parent
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'path limiting works: dir' '
+ set_state dir/foo work head &&
+ (echo y; echo n) | git checkout -p dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: -- dir' '
+ set_state dir/foo work head &&
+ (echo y; echo n) | git checkout -p -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'path limiting works: HEAD^ -- dir' '
+ # the third n is to get out in case it mistakenly does not apply
+ (echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+ verify_saved_state bar &&
+ verify_state dir/foo parent parent
+'
+
+test_expect_success 'path limiting works: foo inside dir' '
+ set_state dir/foo work head &&
+ # the third n is to get out in case it mistakenly does not apply
+ (echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+ verify_saved_state bar &&
+ verify_state dir/foo head head
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index c5c29ccc4..4e6a44b62 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -3,9 +3,10 @@
# Copyright (c) 2005 Amos Waterland
#
-test_description='git rebase should not destroy author information
+test_description='git rebase assorted tests
-This test runs git rebase and checks that the author information is not lost.
+This test runs git rebase and checks that the author information is not lost
+among other things.
'
. ./test-lib.sh
@@ -133,4 +134,25 @@ test_expect_success 'rebase -q is quiet' '
test ! -s output.out
'
+q_to_cr () {
+ tr Q '\015'
+}
+
+test_expect_success 'Rebase a commit that sprinkles CRs in' '
+ (
+ echo "One"
+ echo "TwoQ"
+ echo "Three"
+ echo "FQur"
+ echo "Five"
+ ) | q_to_cr >CR &&
+ git add CR &&
+ test_tick &&
+ git commit -a -m "A file with a line with CR" &&
+ git tag file-with-cr &&
+ git checkout HEAD^0 &&
+ git rebase --onto HEAD^^ HEAD^ &&
+ git diff --exit-code file-with-cr:CR HEAD:CR
+'
+
test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index a973628e8..4cae01952 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -10,7 +10,7 @@ that the result still makes sense.
'
. ./test-lib.sh
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
set_fake_editor
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
index 653350521..14a23cd87 100755
--- a/t/t3411-rebase-preserve-around-merges.sh
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -10,7 +10,7 @@ a merge to before the merge.
'
. ./test-lib.sh
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
set_fake_editor
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
index 80019ee07..ee0a6cccf 100755
--- a/t/t3414-rebase-preserve-onto.sh
+++ b/t/t3414-rebase-preserve-onto.sh
@@ -10,7 +10,7 @@ aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
'
. ./test-lib.sh
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
# Set up branches like this:
# A1---B1---E1---F1---G1
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 7a3fb6795..5514f74b3 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -200,4 +200,23 @@ test_expect_success 'drop -q is quiet' '
test ! -s output.out
'
+test_expect_success 'stash -k' '
+ echo bar3 > file &&
+ echo bar4 > file2 &&
+ git add file2 &&
+ git stash -k &&
+ test bar,bar4 = $(cat file),$(cat file2)
+'
+
+test_expect_success 'stash --invalid-option' '
+ echo bar5 > file &&
+ echo bar6 > file2 &&
+ git add file2 &&
+ test_must_fail git stash --invalid-option &&
+ test_must_fail git stash save --invalid-option &&
+ test bar5,bar6 = $(cat file),$(cat file2) &&
+ git stash -- -message-starting-with-dash &&
+ test bar,bar2 = $(cat file),$(cat file2)
+'
+
test_done
diff --git a/t/t3904-stash-patch.sh b/t/t3904-stash-patch.sh
new file mode 100755
index 000000000..f37e3bc6e
--- /dev/null
+++ b/t/t3904-stash-patch.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='git checkout --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add bar dir/foo &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ echo index > dir/foo &&
+ git add dir/foo &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before dir, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_state dir/foo work index
+ (echo n; echo n) | test_must_fail git stash save -p &&
+ verify_state dir/foo work index &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git stash -p' '
+ (echo n; echo y) | git stash save -p &&
+ verify_state dir/foo head index &&
+ verify_saved_state bar &&
+ git reset --hard &&
+ git stash apply &&
+ verify_state dir/foo work head &&
+ verify_state bar dummy dummy
+'
+
+test_expect_success 'git stash -p --no-keep-index' '
+ set_state dir/foo work index &&
+ set_state bar bar_work bar_index &&
+ (echo n; echo y) | git stash save -p --no-keep-index &&
+ verify_state dir/foo head head &&
+ verify_state bar bar_work dummy &&
+ git reset --hard &&
+ git stash apply --index &&
+ verify_state dir/foo work index &&
+ verify_state bar dummy bar_index
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 922a8941e..531f5b795 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -493,13 +493,12 @@ test_expect_success 'format-patch from a subdirectory (2)' '
'
test_expect_success 'format-patch from a subdirectory (3)' '
- here="$TEST_DIRECTORY/$test" &&
rm -f 0* &&
filename=$(
rm -rf sub &&
mkdir -p sub/dir &&
cd sub/dir &&
- git format-patch -1 -o "$here"
+ git format-patch -1 -o "$TRASH_DIRECTORY"
) &&
basename=$(expr "$filename" : ".*/\(.*\)") &&
test -f "$basename"
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 4ea42e00d..a7602cf92 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -166,7 +166,7 @@ test_expect_success 'diff --cached' '
git update-index --assume-unchanged file &&
echo second >file &&
git diff --cached >actual &&
- test_cmp ../t4020/diff.NUL actual
+ test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
'
test_done
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
new file mode 100755
index 000000000..9d9498bd9
--- /dev/null
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='diff with assume-unchanged entries'
+
+. ./test-lib.sh
+
+# external diff has been tested in t4020-diff-external.sh
+
+test_expect_success 'setup' '
+ echo zero > zero &&
+ git add zero &&
+ git commit -m zero &&
+ echo one > one &&
+ echo two > two &&
+ git add one two &&
+ git commit -m onetwo &&
+ git update-index --assume-unchanged one &&
+ echo borked >> one &&
+ test "$(git ls-files -v one)" = "h one"
+'
+
+test_expect_success 'diff-index does not examine assume-unchanged entries' '
+ git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+'
+
+test_expect_success 'diff-files does not examine assume-unchanged entries' '
+ rm one &&
+ test -z "$(git diff-files -- one)"
+'
+
+test_done
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
new file mode 100755
index 000000000..484654d6e
--- /dev/null
+++ b/t/t4107-apply-ignore-whitespace.sh
@@ -0,0 +1,185 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Giuseppe Bilotta
+#
+
+test_description='git-apply --ignore-whitespace.
+
+'
+. ./test-lib.sh
+
+# This primes main.c file that indents without using HT at all.
+# Various patches with HT and other spaces are attempted in the test.
+
+cat > patch1.patch <<\EOF
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,22 @@
++#include <stdio.h>
++
++void print_int(int num);
++int func(int num);
++
++int main() {
++ int i;
++
++ for (i = 0; i < 10; i++) {
++ print_int(func(i)); /* stuff */
++ }
++
++ return 0;
++}
++
++int func(int num) {
++ return num * num;
++}
++
++void print_int(int num) {
++ printf("%d", num);
++}
+EOF
+
+# Since whitespace is very significant and we want to prevent whitespace
+# mangling when creating this test from a patch, we protect 'fixable'
+# whitespace by replacing spaces with Z and replacing them at patch
+# creation time, hence the sed trick.
+
+# This patch will fail unless whitespace differences are being ignored
+
+sed -e 's/Z/ /g' > patch2.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,6 +10,8 @@
+Z print_int(func(i)); /* stuff */
+Z }
+Z
++ printf("\n");
++
+Z return 0;
+Z}
+Z
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing string at EOL. TODO: this testcase should be
+# improved by creating a line that has the same hash with and without
+# the final string.
+
+sed -e 's/Z/ /g' > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,3 +10,4 @@
+Z for (i = 0; i < 10; i++) {
+Z print_int(func(i));Z
++ /* stuff */
+Z }
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing EOL at EOF.
+
+sed -e 's/Z/ /g' > patch4.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -21,1 +21,1 @@
+- };Z
+\ No newline at end of file
++ };
+EOF
+
+# This patch will fail unless whitespace differences are being ignored.
+
+sed -e 's/Z/ /g' > patch5.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -2,2 +2,3 @@
+Z void print_int(int num);
++ /* a comment */
+Z int func(int num);
+EOF
+
+# And this is how the final output should be. Patches introduce
+# HTs but the original SP indents are mostly kept.
+
+sed -e 's/T/ /g' > main.c.final <<\EOF
+#include <stdio.h>
+
+void print_int(int num);
+T/* a comment */
+int func(int num);
+
+int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ print_int(func(i)); /* stuff */
+ }
+
+Tprintf("\n");
+
+ return 0;
+}
+
+int func(int num) {
+ return num * num;
+}
+
+void print_int(int num) {
+ printf("%d", num);
+}
+EOF
+
+test_expect_success 'file creation' '
+ git-apply patch1.patch
+'
+
+test_expect_success 'patch2 fails (retab)' '
+ test_must_fail git-apply patch2.patch
+'
+
+test_expect_success 'patch2 applies with --ignore-whitespace' '
+ git-apply --ignore-whitespace patch2.patch
+'
+
+test_expect_success 'patch2 reverse applies with --ignore-space-change' '
+ git-apply -R --ignore-space-change patch2.patch
+'
+
+git config apply.ignorewhitespace change
+
+test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
+ git-apply patch2.patch
+'
+
+test_expect_success 'patch3 fails (missing string at EOL)' '
+ test_must_fail git-apply patch3.patch
+'
+
+test_expect_success 'patch4 fails (missing EOL at EOF)' '
+ test_must_fail git-apply patch4.patch
+'
+
+test_expect_success 'patch5 applies (leading whitespace)' '
+ git-apply patch5.patch
+'
+
+test_expect_success 'patches do not mangle whitespace' '
+ test_cmp main.c main.c.final
+'
+
+test_expect_success 're-create file (with --ignore-whitespace)' '
+ rm -f main.c &&
+ git-apply patch1.patch
+'
+
+test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
+ test_must_fail git-apply --no-ignore-whitespace patch5.patch
+'
+
+test_done
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index e70ea94a1..0279d07c8 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -11,18 +11,26 @@ test_expect_success 'split sample box' \
'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
last=`cat last` &&
echo total is $last &&
- test `cat last` = 13'
+ test `cat last` = 14'
+
+check_mailinfo () {
+ mail=$1 opt=$2
+ mo="$mail$opt"
+ git mailinfo -u $opt msg$mo patch$mo <$mail >info$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/msg$mo msg$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/patch$mo patch$mo &&
+ test_cmp "$TEST_DIRECTORY"/t5100/info$mo info$mo
+}
+
for mail in `echo 00*`
do
test_expect_success "mailinfo $mail" '
- git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
- echo msg &&
- test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
- echo patch &&
- test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
- echo info &&
- test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+ check_mailinfo $mail "" &&
+ if test -f "$TEST_DIRECTORY"/t5100/msg$mail--scissors
+ then
+ check_mailinfo $mail --scissors
+ fi
'
done
diff --git a/t/t5100/info0014 b/t/t5100/info0014
new file mode 100644
index 000000000..08566b34b
--- /dev/null
+++ b/t/t5100/info0014
@@ -0,0 +1,5 @@
+Author: Junio Hamano
+Email: junkio@cox.net
+Subject: BLAH ONE
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/info0014--scissors b/t/t5100/info0014--scissors
new file mode 100644
index 000000000..ab9c8d090
--- /dev/null
+++ b/t/t5100/info0014--scissors
@@ -0,0 +1,5 @@
+Author: Junio C Hamano
+Email: gitster@pobox.com
+Subject: Teach mailinfo to ignore everything before -- >8 -- mark
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+
diff --git a/t/t5100/msg0014 b/t/t5100/msg0014
new file mode 100644
index 000000000..62e5cd2ec
--- /dev/null
+++ b/t/t5100/msg0014
@@ -0,0 +1,18 @@
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/msg0014--scissors b/t/t5100/msg0014--scissors
new file mode 100644
index 000000000..259c6a46d
--- /dev/null
+++ b/t/t5100/msg0014--scissors
@@ -0,0 +1,4 @@
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff --git a/t/t5100/patch0014 b/t/t5100/patch0014
new file mode 100644
index 000000000..124efd234
--- /dev/null
+++ b/t/t5100/patch0014
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
diff --git a/t/t5100/patch0014--scissors b/t/t5100/patch0014--scissors
new file mode 100644
index 000000000..124efd234
--- /dev/null
+++ b/t/t5100/patch0014--scissors
@@ -0,0 +1,64 @@
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
diff --git a/t/t5100/sample.mbox b/t/t5100/sample.mbox
index c3074ac57..13fa4ae03 100644
--- a/t/t5100/sample.mbox
+++ b/t/t5100/sample.mbox
@@ -561,3 +561,92 @@ From: <a.u.thor@example.com> (A U Thor)
Date: Fri, 9 Jun 2006 00:44:16 -0700
Subject: [PATCH] a patch
+From nobody Mon Sep 17 00:00:00 2001
+From: Junio Hamano <junkio@cox.net>
+Date: Thu, 20 Aug 2009 17:18:22 -0700
+Subject: Why doesn't git-am does not like >8 scissors mark?
+
+Subject: [PATCH] BLAH ONE
+
+In real life, we will see a discussion that inspired this patch
+discussing related and unrelated things around >8 scissors mark
+in this part of the message.
+
+Subject: [PATCH] BLAH TWO
+
+And then we will see the scissors.
+
+ This line is not a scissors mark -- >8 -- but talks about it.
+ - - >8 - - please remove everything above this line - - >8 - -
+
+Subject: [PATCH] Teach mailinfo to ignore everything before -- >8 -- mark
+From: Junio C Hamano <gitster@pobox.com>
+
+This teaches mailinfo the scissors -- >8 -- mark; the command ignores
+everything before it in the message body.
+
+Signed-off-by: Junio C Hamano <gitster@pobox.com>
+---
+ builtin-mailinfo.c | 37 ++++++++++++++++++++++++++++++++++++-
+ 1 files changed, 36 insertions(+), 1 deletions(-)
+
+diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
+index b0b5d8f..461c47e 100644
+--- a/builtin-mailinfo.c
++++ b/builtin-mailinfo.c
+@@ -712,6 +712,34 @@ static inline int patchbreak(const struct strbuf *line)
+ return 0;
+ }
+
++static int scissors(const struct strbuf *line)
++{
++ size_t i, len = line->len;
++ int scissors_dashes_seen = 0;
++ const char *buf = line->buf;
++
++ for (i = 0; i < len; i++) {
++ if (isspace(buf[i]))
++ continue;
++ if (buf[i] == '-') {
++ scissors_dashes_seen |= 02;
++ continue;
++ }
++ if (i + 1 < len && !memcmp(buf + i, ">8", 2)) {
++ scissors_dashes_seen |= 01;
++ i++;
++ continue;
++ }
++ if (i + 7 < len && !memcmp(buf + i, "cut here", 8)) {
++ i += 7;
++ continue;
++ }
++ /* everything else --- not scissors */
++ break;
++ }
++ return scissors_dashes_seen == 03;
++}
++
+ static int handle_commit_msg(struct strbuf *line)
+ {
+ static int still_looking = 1;
+@@ -723,10 +751,17 @@ static int handle_commit_msg(struct strbuf *line)
+ strbuf_ltrim(line);
+ if (!line->len)
+ return 0;
+- if ((still_looking = check_header(line, s_hdr_data, 0)) != 0)
++ still_looking = check_header(line, s_hdr_data, 0);
++ if (still_looking)
+ return 0;
+ }
+
++ if (scissors(line)) {
++ fseek(cmitmsg, 0L, SEEK_SET);
++ still_looking = 1;
++ return 0;
++ }
++
+ /* normalize the log message to UTF-8. */
+ if (metainfo_charset)
+ convert_to_utf8(line, charset.buf);
+--
+1.6.4.1
diff --git a/t/t5304-prune.sh b/t/t5304-prune.sh
index 55ed7c793..3c6687abe 100755
--- a/t/t5304-prune.sh
+++ b/t/t5304-prune.sh
@@ -6,6 +6,17 @@
test_description='prune'
. ./test-lib.sh
+day=$((60*60*24))
+week=$(($day*7))
+
+add_blob() {
+ before=$(git count-objects | sed "s/ .*//") &&
+ BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
+ BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
+ test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE
+}
+
test_expect_success setup '
: > file &&
@@ -31,11 +42,7 @@ test_expect_success 'prune stale packs' '
test_expect_success 'prune --expire' '
- before=$(git count-objects | sed "s/ .*//") &&
- BLOB=$(echo aleph | git hash-object -w --stdin) &&
- BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
+ add_blob &&
git prune --expire=1.hour.ago &&
test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test -f $BLOB_FILE &&
@@ -48,16 +55,12 @@ test_expect_success 'prune --expire' '
test_expect_success 'gc: implicit prune --expire' '
- before=$(git count-objects | sed "s/ .*//") &&
- BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
- BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
- test-chmtime =-$((86400*14-30)) $BLOB_FILE &&
+ add_blob &&
+ test-chmtime =-$((2*$week-30)) $BLOB_FILE &&
git gc &&
test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
test -f $BLOB_FILE &&
- test-chmtime =-$((86400*14+1)) $BLOB_FILE &&
+ test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
git gc &&
test $before = $(git count-objects | sed "s/ .*//") &&
! test -f $BLOB_FILE
@@ -114,12 +117,8 @@ test_expect_success 'prune: do not prune heads listed as an argument' '
test_expect_success 'gc --no-prune' '
- before=$(git count-objects | sed "s/ .*//") &&
- BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
- BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- test $((1 + $before)) = $(git count-objects | sed "s/ .*//") &&
- test -f $BLOB_FILE &&
- test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+ add_blob &&
+ test-chmtime =-$((5001*$day)) $BLOB_FILE &&
git config gc.pruneExpire 2.days.ago &&
git gc --no-prune &&
test 1 = $(git count-objects | sed "s/ .*//") &&
@@ -140,9 +139,8 @@ test_expect_success 'gc respects gc.pruneExpire' '
test_expect_success 'gc --prune=<date>' '
- BLOB=$(echo aleph_0 | git hash-object -w --stdin) &&
- BLOB_FILE=.git/objects/$(echo $BLOB | sed "s/^../&\//") &&
- test-chmtime =-$((86400*5001)) $BLOB_FILE &&
+ add_blob &&
+ test-chmtime =-$((5001*$day)) $BLOB_FILE &&
git gc --prune=5002.days.ago &&
test -f $BLOB_FILE &&
git gc --prune=5000.days.ago &&
@@ -150,4 +148,18 @@ test_expect_success 'gc --prune=<date>' '
'
+test_expect_success 'gc: prune old objects after local clone' '
+ add_blob &&
+ test-chmtime =-$((2*$week+1)) $BLOB_FILE &&
+ git clone --no-hardlinks . aclone &&
+ (
+ cd aclone &&
+ test 1 = $(git count-objects | sed "s/ .*//") &&
+ test -f $BLOB_FILE &&
+ git gc --prune &&
+ test 0 = $(git count-objects | sed "s/ .*//") &&
+ ! test -f $BLOB_FILE
+ )
+'
+
test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index a8c2ca2a7..18376d660 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -139,6 +139,36 @@ test_expect_success 'fsck in shallow repo' '
)
'
+test_expect_success 'simple fetch in shallow repo' '
+ (
+ cd shallow &&
+ git fetch
+ )
+'
+
+test_expect_success 'no changes expected' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow.2 &&
+ cmp count.shallow count.shallow.2
+'
+
+test_expect_success 'fetch same depth in shallow repo' '
+ (
+ cd shallow &&
+ git fetch --depth=2
+ )
+'
+
+test_expect_success 'no changes expected' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow.3 &&
+ cmp count.shallow count.shallow.3
+'
+
test_expect_success 'add two more' '
add B66 $B65 &&
add B67 $B66
@@ -201,4 +231,21 @@ test_expect_success 'pull in shallow repo with missing merge base' '
)
'
+test_expect_success 'additional simple shallow deepenings' '
+ (
+ cd shallow &&
+ git fetch --depth=8 &&
+ git fetch --depth=10 &&
+ git fetch --depth=11
+ )
+'
+
+test_expect_success 'clone shallow object count' '
+ (
+ cd shallow &&
+ git count-objects -v
+ ) > count.shallow &&
+ grep "^count: 52" count.shallow
+'
+
test_done
diff --git a/t/t5501-post-upload-pack.sh b/t/t5501-post-upload-pack.sh
new file mode 100755
index 000000000..d89fb51ba
--- /dev/null
+++ b/t/t5501-post-upload-pack.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='post upload-hook'
+
+. ./test-lib.sh
+
+LOGFILE=".git/post-upload-pack-log"
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B &&
+ git reset --hard A &&
+ test_commit C &&
+ git branch prev B &&
+ mkdir -p .git/hooks &&
+ {
+ echo "#!$SHELL_PATH" &&
+ echo "cat >post-upload-pack-log"
+ } >".git/hooks/post-upload-pack" &&
+ chmod +x .git/hooks/post-upload-pack
+'
+
+test_expect_success initial '
+ rm -fr sub &&
+ git init sub &&
+ (
+ cd sub &&
+ git fetch --no-tags .. prev
+ ) &&
+ want=$(sed -n "s/^want //p" "$LOGFILE") &&
+ test "$want" = "$(git rev-parse --verify B)" &&
+ ! grep "^have " "$LOGFILE" &&
+ kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+ test "$kind" = fetch
+'
+
+test_expect_success second '
+ rm -fr sub &&
+ git init sub &&
+ (
+ cd sub &&
+ git fetch --no-tags .. prev:refs/remotes/prev &&
+ git fetch --no-tags .. master
+ ) &&
+ want=$(sed -n "s/^want //p" "$LOGFILE") &&
+ test "$want" = "$(git rev-parse --verify C)" &&
+ have=$(sed -n "s/^have //p" "$LOGFILE") &&
+ test "$have" = "$(git rev-parse --verify B)" &&
+ kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+ test "$kind" = fetch
+'
+
+test_expect_success all '
+ rm -fr sub &&
+ HERE=$(pwd) &&
+ git init sub &&
+ (
+ cd sub &&
+ git clone "file://$HERE/.git" new
+ ) &&
+ sed -n "s/^want //p" "$LOGFILE" | sort >actual &&
+ git rev-parse A B C | sort >expect &&
+ test_cmp expect actual &&
+ ! grep "^have " "$LOGFILE" &&
+ kind=$(sed -n "s/^kind //p" "$LOGFILE") &&
+ test "$kind" = clone
+'
+
+test_done
diff --git a/t/t5516-fetch-push.sh b/t/t5516-fetch-push.sh
index 2d2633f3f..6889a53cf 100755
--- a/t/t5516-fetch-push.sh
+++ b/t/t5516-fetch-push.sh
@@ -122,6 +122,23 @@ test_expect_success 'fetch with insteadOf' '
)
'
+test_expect_success 'fetch with pushInsteadOf (should not rewrite)' '
+ mk_empty &&
+ (
+ TRASH=$(pwd)/ &&
+ cd testrepo &&
+ git config "url.trash/.pushInsteadOf" "$TRASH" &&
+ git config remote.up.url "$TRASH." &&
+ git config remote.up.fetch "refs/heads/*:refs/remotes/origin/*" &&
+ git fetch up &&
+
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
test_expect_success 'push without wildcard' '
mk_empty &&
@@ -162,6 +179,36 @@ test_expect_success 'push with insteadOf' '
)
'
+test_expect_success 'push with pushInsteadOf' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.$TRASH.pushInsteadOf" trash/ &&
+ git push trash/testrepo refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
+test_expect_success 'push with pushInsteadOf and explicit pushurl (pushInsteadOf should not rewrite)' '
+ mk_empty &&
+ TRASH="$(pwd)/" &&
+ git config "url.trash2/.pushInsteadOf" trash/ &&
+ git config remote.r.url trash/wrong &&
+ git config remote.r.pushurl "$TRASH/testrepo" &&
+ git push r refs/heads/master:refs/remotes/origin/master &&
+ (
+ cd testrepo &&
+ r=$(git show-ref -s --verify refs/remotes/origin/master) &&
+ test "z$r" = "z$the_commit" &&
+
+ test 1 = $(git for-each-ref refs/remotes/origin | wc -l)
+ )
+'
+
test_expect_success 'push with matching heads' '
mk_test heads/master &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index e78d40242..dd2ee842e 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -149,4 +149,15 @@ test_expect_success 'pull --rebase dies early with dirty working directory' '
'
+test_expect_success 'pull --rebase works on branch yet to be born' '
+ git rev-parse master >expect &&
+ mkdir empty_repo &&
+ (cd empty_repo &&
+ git init &&
+ git pull --rebase .. master &&
+ git rev-parse HEAD >../actual
+ ) &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index f5102b902..a696b8791 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -30,11 +30,12 @@ test_expect_success 'fsck fails' '
test_must_fail git fsck
'
-test_expect_success 'upload-pack fails due to error in pack-objects' '
+test_expect_success 'upload-pack fails due to error in pack-objects packing' '
! echo "0032want $(git rev-parse HEAD)
00000009done
0000" | git upload-pack . > /dev/null 2> output.err &&
+ grep "unable to read" output.err &&
grep "pack-objects died" output.err
'
@@ -51,9 +52,21 @@ test_expect_success 'fsck fails' '
test_expect_success 'upload-pack fails due to error in rev-list' '
! echo "0032want $(git rev-parse HEAD)
+0034shallow $(git rev-parse HEAD^)00000009done
+0000" | git upload-pack . > /dev/null 2> output.err &&
+ # pack-objects survived
+ grep "Total.*, reused" output.err &&
+ # but there was an error, which must have been in rev-list
+ grep "bad tree object" output.err
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
+
+ ! echo "0032want $(git rev-parse HEAD)
00000009done
0000" | git upload-pack . > /dev/null 2> output.err &&
- grep "waitpid (async) failed" output.err
+ grep "bad tree object" output.err &&
+ grep "pack-objects died" output.err
'
test_expect_success 'create empty repository' '
diff --git a/t/t5706-clone-branch.sh b/t/t5706-clone-branch.sh
new file mode 100755
index 000000000..f3f9a7605
--- /dev/null
+++ b/t/t5706-clone-branch.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+
+test_description='clone --branch option'
+. ./test-lib.sh
+
+check_HEAD() {
+ echo refs/heads/"$1" >expect &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expect actual
+}
+
+check_file() {
+ echo "$1" >expect &&
+ test_cmp expect file
+}
+
+test_expect_success 'setup' '
+ mkdir parent &&
+ (cd parent && git init &&
+ echo one >file && git add file && git commit -m one &&
+ git checkout -b two &&
+ echo two >file && git add file && git commit -m two &&
+ git checkout master)
+'
+
+test_expect_success 'vanilla clone chooses HEAD' '
+ git clone parent clone &&
+ (cd clone &&
+ check_HEAD master &&
+ check_file one
+ )
+'
+
+test_expect_success 'clone -b chooses specified branch' '
+ git clone -b two parent clone-two &&
+ (cd clone-two &&
+ check_HEAD two &&
+ check_file two
+ )
+'
+
+test_expect_success 'clone -b sets up tracking' '
+ (cd clone-two &&
+ echo origin >expect &&
+ git config branch.two.remote >actual &&
+ echo refs/heads/two >>expect &&
+ git config branch.two.merge >>actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'clone -b does not munge remotes/origin/HEAD' '
+ (cd clone-two &&
+ echo refs/remotes/origin/master >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'clone -b with bogus branch chooses HEAD' '
+ git clone -b bogus parent clone-bogus &&
+ (cd clone-bogus &&
+ check_HEAD master &&
+ check_file one
+ )
+'
+
+test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 04e4b7c5c..0144d9e85 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -110,6 +110,18 @@ test_expect_success 'compute merge-base (all)' \
# Another set to demonstrate base between one commit and a merge
# in the documentation.
+#
+# * C (MMC) * B (MMB) * A (MMA)
+# * o * o * o
+# * o * o * o
+# * o * o * o
+# * o | _______/
+# | |/
+# | * 1 (MM1)
+# | _______/
+# |/
+# * root (MMR)
+
test_expect_success 'merge-base for octopus-step (setup)' '
test_tick && git commit --allow-empty -m root && git tag MMR &&
@@ -137,6 +149,12 @@ test_expect_success 'merge-base A B C' '
test "$MM1" = "$MB"
'
+test_expect_success 'merge-base A B C using show-branch' '
+ MB=$(git show-branch --merge-base MMA MMB MMC) &&
+ MMR=$(git rev-parse --verify MMR) &&
+ test "$MMR" = "$MB"
+'
+
test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
git reset --hard MMR &&
test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
new file mode 100755
index 000000000..8b146fb43
--- /dev/null
+++ b/t/t6015-rev-list-show-all-parents.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='--show-all --parents does not rewrite TREESAME commits'
+
+. ./test-lib.sh
+
+test_expect_success 'set up --show-all --parents test' '
+ test_commit one foo.txt &&
+ commit1=`git rev-list -1 HEAD` &&
+ test_commit two bar.txt &&
+ commit2=`git rev-list -1 HEAD` &&
+ test_commit three foo.txt &&
+ commit3=`git rev-list -1 HEAD`
+ '
+
+test_expect_success '--parents rewrites TREESAME parents correctly' '
+ echo $commit3 $commit1 > expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
+ echo $commit3 $commit2 > expected &&
+ echo $commit2 $commit1 >> expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents --show-all HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_done
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
new file mode 100755
index 000000000..27fd52b7b
--- /dev/null
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -0,0 +1,276 @@
+#!/bin/sh
+
+# There's more than one "correct" way to represent the history graphically.
+# These tests depend on the current behavior of the graphing code. If the
+# graphing code is ever changed to draw the output differently, these tests
+# cases will need to be updated to know about the new layout.
+
+test_description='--graph and simplified history'
+
+. ./test-lib.sh
+
+test_expect_success 'set up rev-list --graph test' '
+ # 3 commits on branch A
+ test_commit A1 foo.txt &&
+ test_commit A2 bar.txt &&
+ test_commit A3 bar.txt &&
+ git branch -m master A &&
+
+ # 2 commits on branch B, started from A1
+ git checkout -b B A1 &&
+ test_commit B1 foo.txt &&
+ test_commit B2 abc.txt &&
+
+ # 2 commits on branch C, started from A2
+ git checkout -b C A2 &&
+ test_commit C1 xyz.txt &&
+ test_commit C2 xyz.txt &&
+
+ # Octopus merge B and C into branch A
+ git checkout A &&
+ git merge B C &&
+ git tag A4
+
+ test_commit A5 bar.txt &&
+
+ # More commits on C, then merge C into A
+ git checkout C &&
+ test_commit C3 foo.txt &&
+ test_commit C4 bar.txt &&
+ git checkout A &&
+ git merge -s ours C &&
+ git tag A6
+
+ test_commit A7 bar.txt &&
+
+ # Store commit names in variables for later use
+ A1=$(git rev-parse --verify A1) &&
+ A2=$(git rev-parse --verify A2) &&
+ A3=$(git rev-parse --verify A3) &&
+ A4=$(git rev-parse --verify A4) &&
+ A5=$(git rev-parse --verify A5) &&
+ A6=$(git rev-parse --verify A6) &&
+ A7=$(git rev-parse --verify A7) &&
+ B1=$(git rev-parse --verify B1) &&
+ B2=$(git rev-parse --verify B2) &&
+ C1=$(git rev-parse --verify C1) &&
+ C2=$(git rev-parse --verify C2) &&
+ C3=$(git rev-parse --verify C3) &&
+ C4=$(git rev-parse --verify C4)
+ '
+
+test_expect_success '--graph --all' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all > actual &&
+ test_cmp expected actual
+ '
+
+# Make sure the graph_is_interesting() code still realizes
+# that undecorated merges are interesting, even with --simplify-by-decoration
+test_expect_success '--graph --simplify-by-decoration' '
+ rm -f expected &&
+ git tag -d A4
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all --simplify-by-decoration > actual &&
+ test_cmp expected actual
+ '
+
+# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success '--graph --simplify-by-decoration prune branch B' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --simplify-by-decoration --all > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --simplify-merges --all \
+ -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --sparse -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --sparse --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C4' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C4 > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+# I don't think the ordering of the boundary commits is really
+# that important, but this test depends on it. If the ordering ever changes
+# in the code, we'll need to update this test.
+test_expect_success '--graph --boundary ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| * | | $B2" >> expected &&
+ echo "| * | | $B1" >> expected &&
+ echo "* | | | $A3" >> expected &&
+ echo "o | | | $A2" >> expected &&
+ echo "|/ / / " >> expected &&
+ echo "o | | $A1" >> expected &&
+ echo " / / " >> expected &&
+ echo "| o $C3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "o $C2" >> expected &&
+ git rev-list --graph --boundary --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+test_done
diff --git a/t/t6020-merge-df.sh b/t/t6020-merge-df.sh
index a19d49de2..e71c687f2 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -22,4 +22,27 @@ 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' '
+ git reset --hard &&
+ git checkout master &&
+ rm .git/index &&
+
+ mkdir before &&
+ echo FILE >before/one &&
+ echo FILE >after &&
+ git add . &&
+ git commit -m first &&
+
+ rm -f after &&
+ git mv before after &&
+ git commit -m move &&
+
+ git checkout -b para HEAD^ &&
+ echo COMPLETELY ANOTHER FILE >another &&
+ git add . &&
+ git commit -m para &&
+
+ git merge master
+'
+
test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
new file mode 100755
index 000000000..8b8bd81c0
--- /dev/null
+++ b/t/t6050-replace.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='Tests replace refs functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_and_commit_file()
+{
+ _file="$1"
+ _msg="$2"
+
+ git add $_file || return $?
+ test_tick || return $?
+ git commit --quiet -m "$_file: $_msg"
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+HASH5=
+HASH6=
+HASH7=
+
+test_expect_success 'set up buggy branch' '
+ echo "line 1" >> hello &&
+ echo "line 2" >> hello &&
+ echo "line 3" >> hello &&
+ echo "line 4" >> hello &&
+ add_and_commit_file hello "4 lines" &&
+ HASH1=$(git rev-parse --verify HEAD) &&
+ echo "line BUG" >> hello &&
+ echo "line 6" >> hello &&
+ echo "line 7" >> hello &&
+ echo "line 8" >> hello &&
+ add_and_commit_file hello "4 more lines with a BUG" &&
+ HASH2=$(git rev-parse --verify HEAD) &&
+ echo "line 9" >> hello &&
+ echo "line 10" >> hello &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH3=$(git rev-parse --verify HEAD) &&
+ echo "line 11" >> hello &&
+ add_and_commit_file hello "1 more line" &&
+ HASH4=$(git rev-parse --verify HEAD) &&
+ sed -e "s/BUG/5/" hello > hello.new &&
+ mv hello.new hello &&
+ add_and_commit_file hello "BUG fixed" &&
+ HASH5=$(git rev-parse --verify HEAD) &&
+ echo "line 12" >> hello &&
+ echo "line 13" >> hello &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH6=$(git rev-parse --verify HEAD)
+ echo "line 14" >> hello &&
+ echo "line 15" >> hello &&
+ echo "line 16" >> hello &&
+ add_and_commit_file hello "again 3 more lines" &&
+ HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'replace the author' '
+ git cat-file commit $HASH2 | grep "author A U Thor" &&
+ R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $R | grep "author O Thor" &&
+ git update-ref refs/replace/$HASH2 $R &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor"
+'
+
+cat >tag.sig <<EOF
+object $HASH2
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success 'tag replaced commit' '
+ git mktag <tag.sig >.git/refs/tags/mytag 2>message
+'
+
+test_expect_success '"git fsck" works' '
+ git fsck master > fsck_master.out &&
+ grep "dangling commit $R" fsck_master.out &&
+ grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
+ test -z "$(git fsck)"
+'
+
+test_expect_success 'repack, clone and fetch work' '
+ git repack -a -d &&
+ git clone --no-hardlinks . clone_dir &&
+ cd clone_dir &&
+ git show HEAD~5 | grep "A U Thor" &&
+ git show $HASH2 | grep "A U Thor" &&
+ git cat-file commit $R &&
+ git repack -a -d &&
+ test_must_fail git cat-file commit $R &&
+ git fetch ../ "refs/replace/*:refs/replace/*" &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git cat-file commit $R &&
+ cd ..
+'
+
+test_expect_success '"git replace" listing and deleting' '
+ test "$HASH2" = "$(git replace -l)" &&
+ test "$HASH2" = "$(git replace)" &&
+ aa=${HASH2%??????????????????????????????????????} &&
+ test "$HASH2" = "$(git replace -l "$aa*")" &&
+ test_must_fail git replace -d $R &&
+ test_must_fail git replace -d &&
+ test_must_fail git replace -l -d $HASH2 &&
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ test -z "$(git replace -l)"
+'
+
+test_expect_success '"git replace" replacing' '
+ git replace $HASH2 $R &&
+ git show $HASH2 | grep "O Thor" &&
+ test_must_fail git replace $HASH2 $R &&
+ git replace -f $HASH2 $R &&
+ test_must_fail git replace -f &&
+ test "$HASH2" = "$(git replace)"
+'
+
+# This creates a side branch where the bug in H2
+# does not appear because P2 is created by applying
+# H2 and squashing H5 into it.
+# P3, P4 and P6 are created by cherry-picking H3, H4
+# and H6 respectively.
+#
+# At this point, we should have the following:
+#
+# P2--P3--P4--P6
+# /
+# H1-H2-H3-H4-H5-H6-H7
+#
+# Then we replace H6 with P6.
+#
+test_expect_success 'create parallel branch without the bug' '
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ git checkout $HASH1 &&
+ git cherry-pick $HASH2 &&
+ git show $HASH5 | git apply &&
+ git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+ PARA2=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH3 &&
+ PARA3=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH4 &&
+ PARA4=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH6 &&
+ PARA6=$(git rev-parse --verify HEAD) &&
+ git replace $HASH6 $PARA6 &&
+ git checkout master &&
+ cur=$(git rev-parse --verify HEAD) &&
+ test "$cur" = "$HASH7" &&
+ git log --pretty=oneline | grep $PARA2 &&
+ git remote add cloned ./clone_dir
+'
+
+test_expect_success 'push to cloned repo' '
+ git push cloned $HASH6^:refs/heads/parallel &&
+ cd clone_dir &&
+ git checkout parallel &&
+ git log --pretty=oneline | grep $PARA2 &&
+ cd ..
+'
+
+test_expect_success 'push branch with replacement' '
+ git cat-file commit $PARA3 | grep "author A U Thor" &&
+ S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $S | grep "author O Thor" &&
+ git replace $PARA3 $S &&
+ git show $HASH6~2 | grep "O Thor" &&
+ git show $PARA3 | grep "O Thor" &&
+ git push cloned $HASH6^:refs/heads/parallel2 &&
+ cd clone_dir &&
+ git checkout parallel2 &&
+ git log --pretty=oneline | grep $PARA3 &&
+ git show $PARA3 | grep "A U Thor" &&
+ cd ..
+'
+
+test_expect_success 'fetch branch with replacement' '
+ git branch tofetch $HASH6 &&
+ cd clone_dir &&
+ git fetch origin refs/heads/tofetch:refs/heads/parallel3
+ git log --pretty=oneline parallel3 | grep $PARA3
+ git show $PARA3 | grep "A U Thor"
+ cd ..
+'
+
+#
+#
+test_done
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
index 6ca11d714..ae56a36ea 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -25,13 +25,17 @@ test_expect_success setup '
echo foo mmap bar_mmap
echo foo_mmap bar mmap baz
} >file &&
+ echo vvv >v &&
echo ww w >w &&
echo x x xx x >x &&
echo y yy >y &&
echo zzz > z &&
mkdir t &&
echo test >t/t &&
- git add file w x y z t/t hello.c &&
+ echo vvv >t/v &&
+ mkdir t/a &&
+ echo vvv >t/a/v &&
+ git add . &&
test_tick &&
git commit -m initial
'
@@ -132,6 +136,51 @@ do
! git grep -c test $H | grep /dev/null
'
+ test_expect_success "grep --max-depth -1 $L" '
+ {
+ echo ${HC}t/a/v:1:vvv
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth -1 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 $L" '
+ {
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- '*' $L" '
+ {
+ echo ${HC}t/a/v:1:vvv
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 1 $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 1 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- t $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+ test_cmp expected actual
+ '
+
done
cat >expected <<EOF
diff --git a/t/t7060-wtstatus.sh b/t/t7060-wtstatus.sh
new file mode 100755
index 000000000..1044aa654
--- /dev/null
+++ b/t/t7060-wtstatus.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='basic work tree status reporting'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A &&
+ test_commit B oneside added &&
+ git checkout A^0 &&
+ test_commit C oneside created
+'
+
+test_expect_success 'A/A conflict' '
+ git checkout B^0 &&
+ test_must_fail git merge C
+'
+
+test_expect_success 'Report path with conflict' '
+ git diff --cached --name-status >actual &&
+ echo "U oneside" >expect &&
+ test_cmp expect actual
+'
+
+test_expect_success 'Report new path with conflict' '
+ git diff --cached --name-status HEAD^ >actual &&
+ echo "U oneside" >expect &&
+ test_cmp expect actual
+'
+
+cat >expect <<EOF
+# On branch side
+# Unmerged paths:
+# (use "git reset HEAD <file>..." to unstage)
+# (use "git add <file>..." to mark resolution)
+#
+# deleted by us: foo
+#
+no changes added to commit (use "git add" and/or "git commit -a")
+EOF
+
+test_expect_success 'M/D conflict does not segfault' '
+ mkdir mdconflict &&
+ (
+ cd mdconflict &&
+ git init &&
+ test_commit initial foo "" &&
+ test_commit modify foo foo &&
+ git checkout -b side HEAD^ &&
+ git rm foo &&
+ git commit -m delete &&
+ test_must_fail git merge master &&
+ test_must_fail git status > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
index e637c7d4d..e85ff02c3 100755
--- a/t/t7102-reset.sh
+++ b/t/t7102-reset.sh
@@ -419,7 +419,8 @@ test_expect_success 'resetting an unmodified path is a no-op' '
'
cat > expect << EOF
-file2: locally modified
+Unstaged changes after reset:
+M file2
EOF
test_expect_success '--mixed refreshes the index' '
diff --git a/t/t7105-reset-patch.sh b/t/t7105-reset-patch.sh
new file mode 100755
index 000000000..c1f4fc3c6
--- /dev/null
+++ b/t/t7105-reset-patch.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='git reset --patch'
+. ./lib-patch-mode.sh
+
+test_expect_success 'setup' '
+ mkdir dir &&
+ echo parent > dir/foo &&
+ echo dummy > bar &&
+ git add dir &&
+ git commit -m initial &&
+ test_tick &&
+ test_commit second dir/foo head &&
+ set_and_save_state bar bar_work bar_index &&
+ save_head
+'
+
+# note: bar sorts before foo, so the first 'n' is always to skip 'bar'
+
+test_expect_success 'saying "n" does nothing' '
+ set_and_save_state dir/foo work work
+ (echo n; echo n) | git reset -p &&
+ verify_saved_state dir/foo &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p' '
+ (echo n; echo y) | git reset -p &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^' '
+ (echo n; echo y) | git reset -p HEAD^ &&
+ verify_state dir/foo work parent &&
+ verify_saved_state bar
+'
+
+# The idea in the rest is that bar sorts first, so we always say 'y'
+# first and if the path limiter fails it'll apply to bar instead of
+# dir/foo. There's always an extra 'n' to reject edits to dir/foo in
+# the failure case (and thus get out of the loop).
+
+test_expect_success 'git reset -p dir' '
+ set_state dir/foo work work
+ (echo y; echo n) | git reset -p dir &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p -- foo (inside dir)' '
+ set_state dir/foo work work
+ (echo y; echo n) | (cd dir && git reset -p -- foo) &&
+ verify_state dir/foo work head &&
+ verify_saved_state bar
+'
+
+test_expect_success 'git reset -p HEAD^ -- dir' '
+ (echo y; echo n) | git reset -p HEAD^ -- dir &&
+ verify_state dir/foo work parent &&
+ verify_saved_state bar
+'
+
+test_expect_success 'none of this moved HEAD' '
+ verify_saved_head
+'
+
+
+test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 61498293b..6cc16c39f 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -56,6 +56,15 @@ test_expect_success 'modified submodule(forward)' "
EOF
"
+test_expect_success 'modified submodule(forward), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+ > Add foo3
+
+EOF
+"
+
commit_file sm1 &&
cd sm1 &&
git reset --hard HEAD~2 >/dev/null &&
@@ -114,6 +123,15 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' "
EOF
"
+test_expect_success 'typechanged submodule(submodule->blob), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head5(blob)->$head4(submodule) (3):
+ > Add foo5
+
+EOF
+"
+
rm -rf sm1 &&
git checkout-index sm1
test_expect_success 'typechanged submodule(submodule->blob)' "
@@ -205,4 +223,8 @@ test_expect_success '--for-status' "
EOF
"
+test_expect_success 'fail when using --files together with --cached' "
+ test_must_fail git submodule summary --files --cached
+"
+
test_done
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
new file mode 100755
index 000000000..2a527750c
--- /dev/null
+++ b/t/t7407-submodule-foreach.sh
@@ -0,0 +1,237 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='Test "git submodule foreach"
+
+This test verifies that "git submodule foreach" correctly visits all submodules
+that are currently checked out.
+'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup a submodule tree' '
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m upstream
+ git clone . super &&
+ git clone super submodule &&
+ (
+ cd super &&
+ git submodule add ../submodule sub1 &&
+ git submodule add ../submodule sub2 &&
+ git submodule add ../submodule sub3 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub1 submodule.foo1 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub2 submodule.foo2 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub3 submodule.foo3 &&
+ git add .gitmodules
+ test_tick &&
+ git commit -m "submodules" &&
+ git submodule init sub1 &&
+ git submodule init sub2 &&
+ git submodule init sub3
+ ) &&
+ (
+ cd submodule &&
+ echo different > file &&
+ git add file &&
+ test_tick &&
+ git commit -m "different"
+ ) &&
+ (
+ cd super &&
+ (
+ cd sub3 &&
+ git pull
+ ) &&
+ git add sub3 &&
+ test_tick &&
+ git commit -m "update sub3"
+ )
+'
+
+sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
+sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
+
+cat > expect <<EOF
+Entering 'sub1'
+foo1-sub1-$sub1sha1
+Entering 'sub3'
+foo3-sub3-$sub3sha1
+EOF
+
+test_expect_success 'test basic "submodule foreach" usage' '
+ git clone super clone &&
+ (
+ cd clone &&
+ git submodule update --init -- sub1 sub3 &&
+ git submodule foreach "echo \$name-\$path-\$sha1" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup nested submodules' '
+ git clone submodule nested1 &&
+ git clone submodule nested2 &&
+ git clone submodule nested3 &&
+ (
+ cd nested3 &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule" &&
+ git submodule init submodule
+ ) &&
+ (
+ cd nested2 &&
+ git submodule add ../nested3 nested3 &&
+ test_tick &&
+ git commit -m "nested3" &&
+ git submodule init nested3
+ ) &&
+ (
+ cd nested1 &&
+ git submodule add ../nested2 nested2 &&
+ test_tick &&
+ git commit -m "nested2" &&
+ git submodule init nested2
+ ) &&
+ (
+ cd super &&
+ git submodule add ../nested1 nested1 &&
+ test_tick &&
+ git commit -m "nested1" &&
+ git submodule init nested1
+ )
+'
+
+test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
+ git clone super clone2 &&
+ (
+ cd clone2 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test ! -d nested1/nested2/.git &&
+ git submodule foreach "git submodule update --init" &&
+ test -d nested1/nested2/.git &&
+ test ! -d nested1/nested2/nested3/.git
+ )
+'
+
+test_expect_success 'use "foreach --recursive" to checkout all submodules' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "git submodule update --init" &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+cat > expect <<EOF
+Entering 'nested1'
+Entering 'nested1/nested2'
+Entering 'nested1/nested2/nested3'
+Entering 'nested1/nested2/nested3/submodule'
+Entering 'sub1'
+Entering 'sub2'
+Entering 'sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "true" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+cat > expect <<EOF
+nested1-nested1
+nested2-nested2
+nested3-nested3
+submodule-submodule
+foo1-sub1
+foo2-sub2
+foo3-sub3
+EOF
+
+test_expect_success 'test "foreach --quiet --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach -q --recursive "echo \$name-\$path" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "update --recursive" to checkout all submodules' '
+ git clone super clone3 &&
+ (
+ cd clone3 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init --recursive &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test -d nested1/nested2/.git &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD)
+sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD)
+sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
+ $nested2sha1 nested1/nested2 (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 sub1 ($sub1sha1_short)
+ $sub2sha1 sub2 ($sub2sha1_short)
+ $sub3sha1 sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive"' '
+ (
+ cd clone3 &&
+ git submodule status --recursive > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "git clone --recursive" to checkout all submodules' '
+ git clone --recursive super clone4 &&
+ test -d clone4/.git &&
+ test -d clone4/sub1/.git &&
+ test -d clone4/sub2/.git &&
+ test -d clone4/sub3/.git &&
+ test -d clone4/nested1/.git &&
+ test -d clone4/nested1/nested2/.git &&
+ test -d clone4/nested1/nested2/nested3/.git &&
+ test -d clone4/nested1/nested2/nested3/submodule/.git
+'
+
+test_done
diff --git a/t/t7406-submodule-reference.sh b/t/t7408-submodule-reference.sh
index cc16d3f05..cc16d3f05 100755
--- a/t/t7406-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 9da4178c9..929499e99 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -142,7 +142,9 @@ test_expect_success 'test show-ignore' "
touch deeply/nested/directory/.keep &&
svn_cmd add deeply &&
svn_cmd up &&
- svn_cmd propset -R svn:ignore 'no-such-file*' .
+ svn_cmd propset -R svn:ignore '
+no-such-file*
+' .
svn_cmd commit -m 'propset svn:ignore'
cd .. &&
git svn show-ignore > show-ignore.got &&
@@ -171,6 +173,7 @@ test_expect_success 'test create-ignore' "
"
cat >prop.expect <<\EOF
+
no-such-file*
EOF
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 78610b61e..bbfd7f479 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -172,11 +172,11 @@ test_expect_success "follow-parent is atomic" '
git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 &&
git update-ref -d refs/remotes/stunk &&
git config --unset svn-remote.svn.fetch stunk &&
- mkdir -p "$GIT_DIR"/svn/flunk@18 &&
- rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) &&
- dd if="$GIT_DIR"/svn/stunk/$rev_map \
- of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 &&
- rm -rf "$GIT_DIR"/svn/stunk &&
+ mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 &&
+ rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) &&
+ dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \
+ of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 &&
+ rm -rf "$GIT_DIR"/svn/refs/remotes/stunk &&
git svn init --minimize-url -i flunk "$svnrepo"/flunk &&
git svn fetch -i flunk &&
git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index 3a9e07768..901b8e09f 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -16,9 +16,7 @@ test_expect_success 'setup old-looking metadata' '
cd .. &&
git svn init "$svnrepo" &&
git svn fetch &&
- mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
- mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
- rmdir "$GIT_DIR"/svn &&
+ rm -rf "$GIT_DIR"/svn &&
git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
@@ -56,7 +54,15 @@ test_expect_success 'initialize a multi-repository repo' '
git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
for i in tags/0.1 tags/0.2 tags/0.3; do
git config --add svn-remote.svn.fetch \
- $i:refs/remotes/$i || exit 1; done
+ $i:refs/remotes/$i || exit 1; done &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
+ grep "^trunk:refs/remotes/trunk$" fetch.out &&
+ grep "^branches/a:refs/remotes/a$" fetch.out &&
+ grep "^branches/b:refs/remotes/b$" fetch.out &&
+ grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+ grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+ grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
+ grep "^:refs/${remotes_git_svn}" fetch.out
'
# refs should all be different, but the trees should all be the same:
@@ -79,36 +85,36 @@ test_expect_success 'migrate --minimize on old inited layout' '
rm -rf "$GIT_DIR"/svn &&
for i in `cat fetch.out`; do
path=`expr $i : "\([^:]*\):.*$"`
- ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
+ ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"`
if test -z "$ref"; then continue; fi
if test -n "$path"; then path="/$path"; fi
( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
done &&
git svn migrate --minimize &&
- test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
+ test -z "`git config -l | grep "^svn-remote\.git-svn\."`" &&
git config --get-all svn-remote.svn.fetch > fetch.out &&
grep "^trunk:refs/remotes/trunk$" fetch.out &&
grep "^branches/a:refs/remotes/a$" fetch.out &&
grep "^branches/b:refs/remotes/b$" fetch.out &&
grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
- grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
+ grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
grep "^:refs/${remotes_git_svn}" fetch.out
'
test_expect_success ".rev_db auto-converted to .rev_map.UUID" '
git svn fetch -i trunk &&
- test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
- expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
+ test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+ expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" &&
test -n "$expect" &&
rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
convert_to_rev_db "$expect" "$rev_db" &&
rm -f "$expect" &&
test -f "$rev_db" &&
git svn fetch -i trunk &&
- test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
- test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
+ test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+ test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db &&
test -f "$expect"
'
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index f159ab689..9d9ebd533 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -10,6 +10,10 @@ test_expect_success 'setup svnrepo' '
mkdir project project/trunk project/branches project/tags &&
echo foo > project/trunk/foo &&
svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
+ svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/b" &&
+ svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/tags/v1" &&
rm -rf project &&
start_httpd
'
@@ -21,6 +25,54 @@ test_expect_success 'test clone with percent escapes' '
cd ..
'
+# SVN works either way, so should we...
+
+test_expect_success 'svn checkout with percent escapes' '
+ svn_cmd checkout "$svnrepo/pr%20ject" svn.percent &&
+ svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk
+'
+
+test_expect_success 'svn checkout with space' '
+ svn_cmd checkout "$svnrepo/pr ject" svn.space &&
+ svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk
+'
+
+test_expect_success 'test clone trunk with percent escapes and minimize-url' '
+ git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
+ (
+ cd minimize &&
+ git rev-parse refs/${remotes_git_svn}
+ )
+'
+
+test_expect_success 'test clone trunk with percent escapes' '
+ git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
+ (
+ cd trunk &&
+ git rev-parse refs/${remotes_git_svn}
+ )
+'
+
+test_expect_success 'test clone --stdlayout with percent escapes' '
+ git svn clone --stdlayout "$svnrepo/pr%20ject" percent &&
+ (
+ cd percent &&
+ git rev-parse refs/remotes/trunk^0 &&
+ git rev-parse refs/remotes/b^0 &&
+ git rev-parse refs/remotes/tags/v1^0
+ )
+'
+
+test_expect_success 'test clone -s with unescaped space' '
+ git svn clone -s "$svnrepo/pr ject" space &&
+ (
+ cd space &&
+ git rev-parse refs/remotes/trunk^0 &&
+ git rev-parse refs/remotes/b^0 &&
+ git rev-parse refs/remotes/tags/v1^0
+ )
+'
+
stop_httpd
test_done
diff --git a/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
index 03705fa4c..5280e5f1e 100755
--- a/t/t9135-git-svn-moved-branch-empty-file.sh
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -10,7 +10,12 @@ test_expect_success 'load svn dumpfile' '
test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
test_expect_success 'test that b1 exists and is empty' '
- (cd x && test -f b1 && ! test -s b1)
+ (
+ cd x &&
+ git reset --hard branch-c &&
+ test -f b1 &&
+ ! test -s b1
+ )
'
test_done
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
index f2ba2d1da..99f69c6a0 100755
--- a/t/t9143-git-svn-gc.sh
+++ b/t/t9143-git-svn-gc.sh
@@ -28,26 +28,26 @@ test_expect_success 'Setup repo' 'git svn init "$svnrepo"'
test_expect_success 'Fetch repo' 'git svn fetch'
test_expect_success 'make backup copy of unhandled.log' '
- cp .git/svn/git-svn/unhandled.log tmp
+ cp .git/svn/refs/remotes/git-svn/unhandled.log tmp
'
-test_expect_success 'create leftover index' '> .git/svn/git-svn/index'
+test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index'
test_expect_success 'git svn gc runs' 'git svn gc'
-test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index'
+test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index'
if perl -MCompress::Zlib -e 0 2>/dev/null
then
test_expect_success 'git svn gc produces a valid gzip file' '
- gunzip .git/svn/git-svn/unhandled.log.gz
+ gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
'
else
say "Perl Compress::Zlib unavailable, skipping gunzip test"
fi
test_expect_success 'git svn gc does not change unhandled.log files' '
- test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log
+ test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log
'
test_done
diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh
new file mode 100755
index 000000000..7600a35cd
--- /dev/null
+++ b/t/t9144-git-svn-old-rev_map.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn old rev_map preservd'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository with old layout' '
+ mkdir i &&
+ (cd i && > a) &&
+ svn_cmd import -m- i "$svnrepo" &&
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ test -d .git/svn/refs/remotes/git-svn/ &&
+ ! test -e .git/svn/git-svn/ &&
+ mv .git/svn/refs/remotes/git-svn .git/svn/ &&
+ rm -r .git/svn/refs
+'
+
+test_expect_success 'old layout continues to work' '
+ svn_cmd import -m- i "$svnrepo/b" &&
+ git svn rebase &&
+ echo a >> b/a &&
+ git add b/a &&
+ git commit -m- -a &&
+ git svn dcommit &&
+ ! test -d .git/svn/refs/ &&
+ test -e .git/svn/git-svn/
+'
+
+test_done
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
new file mode 100755
index 000000000..16852d26a
--- /dev/null
+++ b/t/t9145-git-svn-master-branch.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+test_description='git svn initial master branch is "trunk" if possible'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ mkdir i &&
+ > i/a &&
+ svn_cmd import -m trunk i "$svnrepo/trunk" &&
+ svn_cmd import -m b/a i "$svnrepo/branches/a" &&
+ svn_cmd import -m b/b i "$svnrepo/branches/b"
+'
+
+test_expect_success 'git svn clone --stdlayout sets up trunk as master' '
+ git svn clone -s "$svnrepo" g &&
+ (
+ cd g &&
+ test x`git rev-parse --verify refs/remotes/trunk^0` = \
+ x`git rev-parse --verify refs/heads/master^0`
+ )
+'
+
+test_done
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index 627518108..2fc7fdb12 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -9,73 +9,8 @@ This test runs gitweb (git web interface) as CGI script from
commandline, and checks that it would not write any errors
or warnings to log.'
-gitweb_init () {
- safe_pwd="$(perl -MPOSIX=getcwd -e 'print quotemeta(getcwd)')"
- cat >gitweb_config.perl <<EOF
-#!/usr/bin/perl
-
-# gitweb configuration for tests
-
-our \$version = "current";
-our \$GIT = "git";
-our \$projectroot = "$safe_pwd";
-our \$project_maxdepth = 8;
-our \$home_link_str = "projects";
-our \$site_name = "[localhost]";
-our \$site_header = "";
-our \$site_footer = "";
-our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
-our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
-our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
-our \$projects_list = "";
-our \$export_ok = "";
-our \$strict_export = "";
-EOF
-
- cat >.git/description <<EOF
-$0 test repository
-EOF
-}
-
-gitweb_run () {
- GATEWAY_INTERFACE="CGI/1.1"
- HTTP_ACCEPT="*/*"
- REQUEST_METHOD="GET"
- SCRIPT_NAME="$TEST_DIRECTORY/../gitweb/gitweb.perl"
- QUERY_STRING=""$1""
- PATH_INFO=""$2""
- export GATEWAY_INTERFACE HTTP_ACCEPT REQUEST_METHOD \
- SCRIPT_NAME QUERY_STRING PATH_INFO
-
- GITWEB_CONFIG=$(pwd)/gitweb_config.perl
- export GITWEB_CONFIG
-
- # some of git commands write to STDERR on error, but this is not
- # written to web server logs, so we are not interested in that:
- # we are interested only in properly formatted errors/warnings
- rm -f gitweb.log &&
- perl -- "$SCRIPT_NAME" \
- >/dev/null 2>gitweb.log &&
- if grep "^[[]" gitweb.log >/dev/null 2>&1; then false; else true; fi
-
- # gitweb.log is left for debugging
-}
-
-. ./test-lib.sh
-
-if ! test_have_prereq PERL; then
- say 'skipping gitweb tests, perl not available'
- test_done
-fi
-
-perl -MEncode -e 'decode_utf8("", Encode::FB_CROAK)' >/dev/null 2>&1 || {
- say 'skipping gitweb tests, perl version is too old'
- test_done
-}
-
-gitweb_init
+. ./gitweb-lib.sh
# ----------------------------------------------------------------------
# no commits (empty, just initialized repository)
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
new file mode 100644
index 000000000..d0ff21d42
--- /dev/null
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Mark Rada
+#
+
+test_description='gitweb as standalone script (http status tests).
+
+This test runs gitweb (git web interface) as a CGI script from the
+commandline, and checks that it returns the expected HTTP status
+code and message.'
+
+
+. ./gitweb-lib.sh
+
+# ----------------------------------------------------------------------
+# snapshot settings
+
+test_commit \
+ 'SnapshotTests' \
+ 'i can has snapshot?'
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'override'} = 0;
+EOF
+
+test_expect_success \
+ 'snapshots: tgz only default format enabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+ grep "403 - Unsupported snapshot format" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+ grep "403 - Snapshot format not allowed" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "403 - Unsupported snapshot format" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$feature{'snapshot'}{'default'} = ['tgz','tbz2','txz','zip'];
+EOF
+
+test_expect_success \
+ 'snapshots: all enabled in default, use default disabled value' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tbz2" &&
+ grep "Status: 200 OK" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=txz" &&
+ grep "403 - Snapshot format not allowed" gitweb.output &&
+ gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'zip'}{'disabled'} = 1;
+EOF
+
+test_expect_success \
+ 'snapshots: zip explicitly disabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=zip" &&
+ grep "403 - Snapshot format not allowed" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+cat >>gitweb_config.perl <<\EOF
+$known_snapshot_formats{'tgz'}{'disabled'} = 0;
+EOF
+
+test_expect_success \
+ 'snapshots: tgz explicitly enabled' \
+ 'gitweb_run "p=.git;a=snapshot;h=HEAD;sf=tgz" &&
+ grep "Status: 200 OK" gitweb.output'
+test_debug 'cat gitweb.output'
+
+
+test_done
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 4322a0c1e..363345fae 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -1,7 +1,7 @@
#!/bin/sh
test_description='git cvsimport basic tests'
-. ./test-lib.sh
+. ./lib-cvs.sh
if ! test_have_prereq PERL; then
say 'skipping git cvsimport tests, perl not available'
@@ -10,37 +10,13 @@ fi
CVSROOT=$(pwd)/cvsroot
export CVSROOT
-unset CVS_SERVER
-# for clean cvsps cache
-HOME=$(pwd)
-export HOME
-
-if ! type cvs >/dev/null 2>&1
-then
- say 'skipping cvsimport tests, cvs not found'
- test_done
-fi
-
-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
-case "$cvsps_version" in
-2.1 | 2.2*)
- ;;
-'')
- say 'skipping cvsimport tests, cvsps not found'
- test_done
- ;;
-*)
- say 'skipping cvsimport tests, unsupported cvsps version'
- test_done
- ;;
-esac
-test_expect_success 'setup cvsroot' 'cvs init'
+test_expect_success 'setup cvsroot' '$CVS init'
test_expect_success 'setup a cvs module' '
mkdir "$CVSROOT/module" &&
- cvs co -d module-cvs module &&
+ $CVS co -d module-cvs module &&
cd module-cvs &&
cat <<EOF >o_fortuna &&
O Fortuna
@@ -59,13 +35,13 @@ egestatem,
potestatem
dissolvit ut glaciem.
EOF
- cvs add o_fortuna &&
+ $CVS add o_fortuna &&
cat <<EOF >message &&
add "O Fortuna" lyrics
These public domain lyrics make an excellent sample text.
EOF
- cvs commit -F message &&
+ $CVS commit -F message &&
cd ..
'
@@ -103,7 +79,7 @@ translate to English
My Latin is terrible.
EOF
- cvs commit -F message &&
+ $CVS commit -F message &&
cd ..
'
@@ -121,8 +97,8 @@ test_expect_success 'update cvs module' '
cd module-cvs &&
echo 1 >tick &&
- cvs add tick &&
- cvs commit -m 1
+ $CVS add tick &&
+ $CVS commit -m 1
cd ..
'
@@ -140,7 +116,7 @@ test_expect_success 'cvsimport.module config works' '
test_expect_success 'import from a CVS working tree' '
- cvs co -d import-from-wt module &&
+ $CVS co -d import-from-wt module &&
cd import-from-wt &&
git cvsimport -a -z0 &&
echo 1 >expect &&
@@ -150,4 +126,6 @@ test_expect_success 'import from a CVS working tree' '
'
+test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
+
test_done
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
new file mode 100755
index 000000000..3afaf5652
--- /dev/null
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+# Description of the files in the repository:
+#
+# imported-once.txt:
+#
+# Imported once. 1.1 and 1.1.1.1 should be identical.
+#
+# imported-twice.txt:
+#
+# Imported twice. HEAD should reflect the contents of the
+# second import (i.e., have the same contents as 1.1.1.2).
+#
+# imported-modified.txt:
+#
+# Imported, then modified on HEAD. HEAD should reflect the
+# modification.
+#
+# imported-modified-imported.txt:
+#
+# Imported, then modified on HEAD, then imported again.
+#
+# added-imported.txt,v:
+#
+# Added with 'cvs add' to create 1.1, then imported with
+# completely different contents to create 1.1.1.1, therefore the
+# vendor branch was never the default branch.
+#
+# imported-anonymously.txt:
+#
+# Like imported-twice.txt, but with a vendor branch whose branch
+# tag has been removed.
+
+test_description='git cvsimport handling of vendor branches'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
+export CVSROOT
+
+test_expect_success 'import a module with a vendor branch' '
+
+ git cvsimport -C module-git module
+
+'
+
+test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master'
+
+test_expect_success 'check master out of git repository' 'test_git_co master'
+
+test_expect_success 'check a file that was imported once' '
+
+ test_cmp_branch_file master imported-once.txt
+
+'
+
+test_expect_failure 'check a file that was imported twice' '
+
+ test_cmp_branch_file master imported-twice.txt
+
+'
+
+test_expect_success 'check a file that was imported then modified on HEAD' '
+
+ test_cmp_branch_file master imported-modified.txt
+
+'
+
+test_expect_success 'check a file that was imported, modified, then imported again' '
+
+ test_cmp_branch_file master imported-modified-imported.txt
+
+'
+
+test_expect_success 'check a file that was added to HEAD then imported' '
+
+ test_cmp_branch_file master added-imported.txt
+
+'
+
+test_expect_success 'a vendor branch whose tag has been removed' '
+
+ test_cmp_branch_file master imported-anonymously.txt
+
+'
+
+test_done
diff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes
new file mode 100644
index 000000000..562b12e16
--- /dev/null
+++ b/t/t9601/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 000000000..3bb9b3417
--- /dev/null
+++ b/t/t9601/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v
new file mode 100644
index 000000000..5f83072ea
--- /dev/null
+++ b/t/t9601/cvsroot/module/added-imported.txt,v
@@ -0,0 +1,44 @@
+head 1.1;
+access;
+symbols
+ vtag-4:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.15; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.16; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Add a file to the working copy.
+@
+text
+@Adding this file, before importing it with different contents.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-4).
+@
+text
+@d1 1
+a1 1
+This is vtag-4 (on vbranchA) of added-then-imported.txt.
+@
+
diff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v
new file mode 100644
index 000000000..55e1b0ca5
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-anonymously.txt,v
@@ -0,0 +1,42 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-1:1.1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-anonymously.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
new file mode 100644
index 000000000..e5830aeb3
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
@@ -0,0 +1,76 @@
+head 1.2;
+access;
+symbols
+ vtag-2:1.1.1.2
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2004.02.09.15.43.14; author kfogel; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next 1.1.1.2;
+
+1.1.1.2
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@First regular commit, to imported-modified-imported.txt, on HEAD.
+@
+text
+@This is a modification of imported-modified-imported.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v
new file mode 100644
index 000000000..bbcfe447b
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-modified.txt,v
@@ -0,0 +1,59 @@
+head 1.2;
+access;
+symbols
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2004.02.09.15.43.14; author kfogel; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Commit on HEAD.
+@
+text
+@This is a modification of imported-modified.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v
new file mode 100644
index 000000000..c5dd82b12
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-once.txt,v
@@ -0,0 +1,43 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-once.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v
new file mode 100644
index 000000000..d1f3f1b34
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-twice.txt,v
@@ -0,0 +1,60 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-2:1.1.1.2
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next 1.1.1.2;
+
+1.1.1.2
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-twice.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-twice.txt.
+@
+
+
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
new file mode 100755
index 000000000..67878b2d0
--- /dev/null
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# A description of the repository used for this test can be found in
+# t9602/README.
+
+test_description='git cvsimport handling of branches and tags'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
+export CVSROOT
+
+test_expect_success 'import module' '
+
+ git cvsimport -C module-git module
+
+'
+
+test_expect_success 'test branch master' '
+
+ test_cmp_branch_tree master
+
+'
+
+test_expect_success 'test branch vendorbranch' '
+
+ test_cmp_branch_tree vendorbranch
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS' '
+
+ test_cmp_branch_tree B_FROM_INITIALS
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+
+ test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
+
+'
+
+test_expect_failure 'test branch B_MIXED' '
+
+ test_cmp_branch_tree B_MIXED
+
+'
+
+test_expect_success 'test branch B_SPLIT' '
+
+ test_cmp_branch_tree B_SPLIT
+
+'
+
+test_expect_failure 'test tag vendortag' '
+
+ test_cmp_branch_tree vendortag
+
+'
+
+test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+
+ test_cmp_branch_tree T_ALL_INITIAL_FILES
+
+'
+
+test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '
+
+ test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE
+
+'
+
+test_expect_failure 'test tag T_MIXED' '
+
+ test_cmp_branch_tree T_MIXED
+
+'
+
+
+test_done
diff --git a/t/t9602/README b/t/t9602/README
new file mode 100644
index 000000000..c231e0f26
--- /dev/null
+++ b/t/t9602/README
@@ -0,0 +1,62 @@
+This repository is for testing the ability to group revisions
+correctly along tags and branches. Here is its history:
+
+ 1. The initial import (revision 1.1 of everybody) created a
+ directory structure with a file named `default' in each dir:
+
+ ./
+ default
+ sub1/default
+ subsubA/default
+ subsubB/default
+ sub2/default
+ subsubA/default
+ sub3/default
+
+ 2. Then tagged everyone with T_ALL_INITIAL_FILES.
+
+ 3. Then tagged everyone except sub1/subsubB/default with
+ T_ALL_INITIAL_FILES_BUT_ONE.
+
+ 4. Then created branch B_FROM_INITIALS on everyone.
+
+ 5. Then created branch B_FROM_INITIALS_BUT_ONE on everyone except
+ /sub1/subsubB/default.
+
+ 6. Then committed modifications to two files: sub3/default, and
+ sub1/subsubA/default.
+
+ 7. Then committed a modification to all 7 files.
+
+ 8. Then backdated sub3/default to revision 1.2, and
+ sub2/subsubA/default to revision 1.1, and tagged with T_MIXED.
+
+ 9. Same as 8, but tagged with -b to create branch B_MIXED.
+
+ 10. Switched the working copy to B_MIXED, and added
+ sub2/branch_B_MIXED_only. (That's why the RCS file is in
+ sub2/Attic/ -- it never existed on trunk.)
+
+ 11. In one commit, modified default, sub1/default, and
+ sub2/subsubA/default, on branch B_MIXED.
+
+ 12. Did "cvs up -A" on sub2/default, then in one commit, made a
+ change to sub2/default and sub2/branch_B_MIXED_only. So this
+ commit should be spread between the branch and the trunk.
+
+ 13. Do "cvs up -A" to get everyone back to trunk, then make a new
+ branch B_SPLIT on everyone except sub1/subsubB/default,v.
+
+ 14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear)
+ and commit a change that affects everyone except sub3/default.
+
+ 15. An hour or so later, "cvs up -A" to get sub1/subsubB/default
+ back, then commit a change on that file, on trunk. (It's
+ important that this change happened after the previous commits
+ on B_SPLIT.)
+
+ 16. Branch sub1/subsubB/default to B_SPLIT, then "cvs up -r B_SPLIT"
+ to switch the whole working copy to the branch.
+
+ 17. Commit a change on B_SPLIT, to sub1/subsubB/default and
+ sub3/default.
diff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes
new file mode 100644
index 000000000..562b12e16
--- /dev/null
+++ b/t/t9602/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 000000000..3bb9b3417
--- /dev/null
+++ b/t/t9602/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v
new file mode 100644
index 000000000..3b68382a3
--- /dev/null
+++ b/t/t9602/cvsroot/module/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.4
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1
+ 1.2.4.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is the file `default' in the top level of the project.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v
new file mode 100644
index 000000000..b7fdccdfd
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.4
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1
+ 1.2.4.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v
new file mode 100644
index 000000000..472b7b2bd
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/subsubA/default,v
@@ -0,0 +1,101 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.4
+ B_MIXED:1.3.0.2
+ T_MIXED:1.3
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.3.4.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.15.26; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/subsubA/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a7 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v
new file mode 100644
index 000000000..fe6efa455
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/subsubB/default,v
@@ -0,0 +1,107 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.06.03.04.29.14; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.04.33.13; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A trunk change to sub1/subsubB/default. This was committed about an
+hour after an earlier change that affected most files on branch
+B_SPLIT. This file is not on that branch yet, but after this commit,
+we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist
+at the time the rest of B_SPLIT was created.
+@
+text
+@This is sub1/subsubB/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+This bit was committed on trunk about an hour after an earlier change
+to everyone else on branch B_SPLIT. Afterwards, we'll finally branch
+this file to B_SPLIT, but rooted in a revision that didn't exist at
+the time the rest of B_SPLIT was created.
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a10 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 5
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
new file mode 100644
index 000000000..34c9789f2
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
@@ -0,0 +1,59 @@
+head 1.1;
+access;
+symbols
+ B_MIXED:1.1.0.2;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2003.05.23.00.25.26; author jrandom; state dead;
+branches
+ 1.1.2.1;
+next ;
+
+1.1.2.1
+date 2003.05.23.00.25.26; author jrandom; state Exp;
+branches;
+next 1.1.2.2;
+
+1.1.2.2
+date 2003.05.23.00.48.51; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@file branch_B_MIXED_only was initially added on branch B_MIXED.
+@
+text
+@@
+
+
+1.1.2.1
+log
+@Add a file on branch B_MIXED.
+@
+text
+@a0 1
+This file was added on branch B_MIXED. It never existed on trunk.
+@
+
+
+1.1.2.2
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@a1 3
+
+The same commit added these two lines here on branch B_MIXED, and two
+similar lines to ./default on trunk.
+@
+
+
diff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v
new file mode 100644
index 000000000..018f7f8ec
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/default,v
@@ -0,0 +1,102 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.48.51; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@This is sub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+The same commit added these two lines here on trunk, and two similar
+lines to ./branch_B_MIXED_only on branch B_MIXED.
+@
+
+
+1.3.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a8 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 3
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v
new file mode 100644
index 000000000..d13242cb0
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/subsubA/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.2
+ B_MIXED:1.1.0.2
+ T_MIXED:1.1
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1
+ 1.1.2.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.1.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub2/subsub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a3 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v
new file mode 100644
index 000000000..88e456743
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub3/default,v
@@ -0,0 +1,102 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.15.26; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.04.33.13; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub3/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a7 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
new file mode 100755
index 000000000..958bdce4d
--- /dev/null
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Structure of the test cvs repository
+#
+# Message File:Content Commit Time
+# Rev 1 a: 1.1 2009-02-21 19:11:43 +0100
+# Rev 2 a: 1.2 b: 1.1 2009-02-21 19:11:14 +0100
+# Rev 3 b: 1.2 2009-02-21 19:11:43 +0100
+#
+# As you can see the commit of Rev 3 has the same time as
+# Rev 1 this leads to a broken import because of a cvsps
+# bug.
+
+test_description='git cvsimport testing for correct patchset estimation'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
+export CVSROOT
+
+test_expect_failure 'import with criss cross times on revisions' '
+
+ git cvsimport -p"-x" -C module-git module &&
+ cd module-git &&
+ git log --pretty=format:%s > ../actual-master &&
+ git log A~2..A --pretty="format:%s %ad" -- > ../actual-A &&
+ echo "" >> ../actual-master &&
+ echo "" >> ../actual-A &&
+ cd .. &&
+ echo "Rev 4
+Rev 3
+Rev 2
+Rev 1" > expect-master &&
+ test_cmp actual-master expect-master &&
+
+ echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000
+Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A &&
+ test_cmp actual-A expect-A
+'
+
+test_done
diff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes
new file mode 100644
index 000000000..562b12e16
--- /dev/null
+++ b/t/t9603/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 000000000..3bb9b3417
--- /dev/null
+++ b/t/t9603/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v
new file mode 100644
index 000000000..ba8fd5af2
--- /dev/null
+++ b/t/t9603/cvsroot/module/a,v
@@ -0,0 +1,74 @@
+head 1.2;
+access;
+symbols
+ A:1.2.0.2;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2009.02.21.18.11.14; author tester; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2009.02.21.18.11.43; author tester; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2009.03.11.19.03.52; author tester; state Exp;
+branches;
+next 1.2.2.2;
+
+1.2.2.2
+date 2009.03.11.19.09.10; author tester; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Rev 2
+@
+text
+@1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.2
+@
+
+
+1.1
+log
+@Rev 1
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v
new file mode 100644
index 000000000..d26885518
--- /dev/null
+++ b/t/t9603/cvsroot/module/b,v
@@ -0,0 +1,90 @@
+head 1.3;
+access;
+symbols
+ A:1.2.0.2;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2009.03.11.19.05.08; author tester; state Exp;
+branches;
+next 1.2;
+
+1.2
+date 2009.02.21.18.11.43; author tester; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2009.02.21.18.11.14; author tester; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2009.03.11.19.03.52; author tester; state Exp;
+branches;
+next 1.2.2.2;
+
+1.2.2.2
+date 2009.03.11.19.09.10; author tester; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Rev 4
+@
+text
+@1.3
+@
+
+
+1.2
+log
+@Rev 3
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.1
+log
+@Rev 2
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5fdc5d94a..f2ca53647 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -114,6 +114,9 @@ do
valgrind=t; verbose=t; shift ;;
--tee)
shift ;; # was handled already
+ --root=*)
+ root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
@@ -645,7 +648,12 @@ fi
# Test repository
test="trash directory.$(basename "$0" .sh)"
-test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TRASH_DIRECTORY="$test" ;;
+ *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
rm -fr "$test" || {
GIT_EXIT_OK=t
echo >&5 "FATAL: Cannot prepare test area"
@@ -677,6 +685,21 @@ do
esac
done
+# Provide an implementation of the 'yes' utility
+yes () {
+ if test $# = 0
+ then
+ y=y
+ else
+ y="$*"
+ fi
+
+ while echo "$y"
+ do
+ :
+ done
+}
+
# Fix some commands on Windows
case $(uname -s) in
*MINGW*)
diff --git a/test-date.c b/test-date.c
index 62e8f2387..a9e705f79 100644
--- a/test-date.c
+++ b/test-date.c
@@ -1,20 +1,67 @@
#include "cache.h"
-int main(int argc, char **argv)
+static const char *usage_msg = "\n"
+" test-date show [time_t]...\n"
+" test-date parse [date]...\n"
+" test-date approxidate [date]...\n";
+
+static void show_dates(char **argv, struct timeval *now)
{
- int i;
+ char buf[128];
+
+ for (; *argv; argv++) {
+ time_t t = atoi(*argv);
+ show_date_relative(t, 0, now, buf, sizeof(buf));
+ printf("%s -> %s\n", *argv, buf);
+ }
+}
- for (i = 1; i < argc; i++) {
+static void parse_dates(char **argv, struct timeval *now)
+{
+ for (; *argv; argv++) {
char result[100];
time_t t;
- memcpy(result, "bad", 4);
- parse_date(argv[i], result, sizeof(result));
+ result[0] = 0;
+ parse_date(*argv, result, sizeof(result));
t = strtoul(result, NULL, 0);
- printf("%s -> %s -> %s", argv[i], result, ctime(&t));
+ printf("%s -> %s\n", *argv,
+ t ? show_date(t, 0, DATE_ISO8601) : "bad");
+ }
+}
- t = approxidate(argv[i]);
- printf("%s -> %s\n", argv[i], ctime(&t));
+static void parse_approxidate(char **argv, struct timeval *now)
+{
+ for (; *argv; argv++) {
+ time_t t;
+ t = approxidate_relative(*argv, now);
+ printf("%s -> %s\n", *argv, show_date(t, 0, DATE_ISO8601));
+ }
+}
+
+int main(int argc, char **argv)
+{
+ struct timeval now;
+ const char *x;
+
+ x = getenv("TEST_DATE_NOW");
+ if (x) {
+ now.tv_sec = atoi(x);
+ now.tv_usec = 0;
}
+ else
+ gettimeofday(&now, NULL);
+
+ argv++;
+ if (!*argv)
+ usage(usage_msg);
+ if (!strcmp(*argv, "show"))
+ show_dates(argv+1, &now);
+ else if (!strcmp(*argv, "parse"))
+ parse_dates(argv+1, &now);
+ else if (!strcmp(*argv, "approxidate"))
+ parse_approxidate(argv+1, &now);
+ else
+ usage(usage_msg);
return 0;
}
diff --git a/test-delta.c b/test-delta.c
index 3d885ff37..af40a3c49 100644
--- a/test-delta.c
+++ b/test-delta.c
@@ -1,7 +1,7 @@
/*
* test-delta.c: test code to exercise diff-delta.c and patch-delta.c
*
- * (C) 2005 Nicolas Pitre <nico@cam.org>
+ * (C) 2005 Nicolas Pitre <nico@fluxnic.net>
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
diff --git a/transport-helper.c b/transport-helper.c
new file mode 100644
index 000000000..f57e84c67
--- /dev/null
+++ b/transport-helper.c
@@ -0,0 +1,168 @@
+#include "cache.h"
+#include "transport.h"
+
+#include "run-command.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+
+struct helper_data
+{
+ const char *name;
+ struct child_process *helper;
+ unsigned fetch : 1;
+};
+
+static struct child_process *get_helper(struct transport *transport)
+{
+ struct helper_data *data = transport->data;
+ struct strbuf buf = STRBUF_INIT;
+ struct child_process *helper;
+ FILE *file;
+
+ if (data->helper)
+ return data->helper;
+
+ helper = xcalloc(1, sizeof(*helper));
+ helper->in = -1;
+ helper->out = -1;
+ helper->err = 0;
+ helper->argv = xcalloc(4, sizeof(*helper->argv));
+ strbuf_addf(&buf, "remote-%s", data->name);
+ helper->argv[0] = strbuf_detach(&buf, NULL);
+ helper->argv[1] = transport->remote->name;
+ helper->argv[2] = transport->url;
+ helper->git_cmd = 1;
+ if (start_command(helper))
+ die("Unable to run helper: git %s", helper->argv[0]);
+ data->helper = helper;
+
+ write_str_in_full(helper->in, "capabilities\n");
+
+ file = xfdopen(helper->out, "r");
+ while (1) {
+ if (strbuf_getline(&buf, file, '\n') == EOF)
+ exit(128); /* child died, message supplied already */
+
+ if (!*buf.buf)
+ break;
+ if (!strcmp(buf.buf, "fetch"))
+ data->fetch = 1;
+ }
+ return data->helper;
+}
+
+static int disconnect_helper(struct transport *transport)
+{
+ struct helper_data *data = transport->data;
+ if (data->helper) {
+ write_str_in_full(data->helper->in, "\n");
+ close(data->helper->in);
+ finish_command(data->helper);
+ free((char *)data->helper->argv[0]);
+ free(data->helper->argv);
+ free(data->helper);
+ data->helper = NULL;
+ }
+ return 0;
+}
+
+static int fetch_with_fetch(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
+{
+ struct child_process *helper = get_helper(transport);
+ FILE *file = xfdopen(helper->out, "r");
+ int i;
+ struct strbuf buf = STRBUF_INIT;
+
+ for (i = 0; i < nr_heads; i++) {
+ const struct ref *posn = to_fetch[i];
+ if (posn->status & REF_STATUS_UPTODATE)
+ continue;
+
+ strbuf_addf(&buf, "fetch %s %s\n",
+ sha1_to_hex(posn->old_sha1), posn->name);
+ write_in_full(helper->in, buf.buf, buf.len);
+ strbuf_reset(&buf);
+
+ if (strbuf_getline(&buf, file, '\n') == EOF)
+ exit(128); /* child died, message supplied already */
+ }
+ return 0;
+}
+
+static int fetch(struct transport *transport,
+ int nr_heads, const struct ref **to_fetch)
+{
+ struct helper_data *data = transport->data;
+ int i, count;
+
+ count = 0;
+ for (i = 0; i < nr_heads; i++)
+ if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
+ count++;
+
+ if (!count)
+ return 0;
+
+ if (data->fetch)
+ return fetch_with_fetch(transport, nr_heads, to_fetch);
+
+ return -1;
+}
+
+static struct ref *get_refs_list(struct transport *transport, int for_push)
+{
+ struct child_process *helper;
+ struct ref *ret = NULL;
+ struct ref **tail = &ret;
+ struct ref *posn;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *file;
+
+ helper = get_helper(transport);
+
+ write_str_in_full(helper->in, "list\n");
+
+ file = xfdopen(helper->out, "r");
+ while (1) {
+ char *eov, *eon;
+ if (strbuf_getline(&buf, file, '\n') == EOF)
+ exit(128); /* child died, message supplied already */
+
+ if (!*buf.buf)
+ break;
+
+ eov = strchr(buf.buf, ' ');
+ if (!eov)
+ die("Malformed response in ref list: %s", buf.buf);
+ eon = strchr(eov + 1, ' ');
+ *eov = '\0';
+ if (eon)
+ *eon = '\0';
+ *tail = alloc_ref(eov + 1);
+ if (buf.buf[0] == '@')
+ (*tail)->symref = xstrdup(buf.buf + 1);
+ else if (buf.buf[0] != '?')
+ get_sha1_hex(buf.buf, (*tail)->old_sha1);
+ tail = &((*tail)->next);
+ }
+ strbuf_release(&buf);
+
+ for (posn = ret; posn; posn = posn->next)
+ resolve_remote_symref(posn, ret);
+
+ return ret;
+}
+
+int transport_helper_init(struct transport *transport, const char *name)
+{
+ struct helper_data *data = xcalloc(sizeof(*data), 1);
+ data->name = name;
+
+ transport->data = data;
+ transport->get_refs_list = get_refs_list;
+ transport->fetch = fetch;
+ transport->disconnect = disconnect_helper;
+ return 0;
+}
diff --git a/transport.c b/transport.c
index f231b355f..4cb807700 100644
--- a/transport.c
+++ b/transport.c
@@ -1,9 +1,6 @@
#include "cache.h"
#include "transport.h"
#include "run-command.h"
-#ifndef NO_CURL
-#include "http.h"
-#endif
#include "pkt-line.h"
#include "fetch-pack.h"
#include "send-pack.h"
@@ -352,51 +349,11 @@ static int rsync_transport_push(struct transport *transport,
return result;
}
-/* Generic functions for using commit walkers */
-
-#ifndef NO_CURL /* http fetch is the only user */
-static int fetch_objs_via_walker(struct transport *transport,
- int nr_objs, const struct ref **to_fetch)
-{
- char *dest = xstrdup(transport->url);
- struct walker *walker = transport->data;
- char **objs = xmalloc(nr_objs * sizeof(*objs));
- int i;
-
- walker->get_all = 1;
- walker->get_tree = 1;
- walker->get_history = 1;
- walker->get_verbosely = transport->verbose >= 0;
- walker->get_recover = 0;
-
- for (i = 0; i < nr_objs; i++)
- objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
-
- if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
- die("Fetch failed.");
-
- for (i = 0; i < nr_objs; i++)
- free(objs[i]);
- free(objs);
- free(dest);
- return 0;
-}
-#endif /* NO_CURL */
-
-static int disconnect_walker(struct transport *transport)
-{
- struct walker *walker = transport->data;
- if (walker)
- walker_free(walker);
- return 0;
-}
-
#ifndef NO_CURL
static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
{
const char **argv;
int argc;
- int err;
if (flags & TRANSPORT_PUSH_MIRROR)
return error("http transport does not support mirror mode");
@@ -416,110 +373,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
while (refspec_nr--)
argv[argc++] = *refspec++;
argv[argc] = NULL;
- err = run_command_v_opt(argv, RUN_GIT_CMD);
- switch (err) {
- case -ERR_RUN_COMMAND_FORK:
- error("unable to fork for %s", argv[0]);
- case -ERR_RUN_COMMAND_EXEC:
- error("unable to exec %s", argv[0]);
- break;
- case -ERR_RUN_COMMAND_WAITPID:
- case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
- case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
- case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
- error("%s died with strange error", argv[0]);
- }
- return !!err;
-}
-
-static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
-{
- struct strbuf buffer = STRBUF_INIT;
- char *data, *start, *mid;
- char *ref_name;
- char *refs_url;
- int i = 0;
- int http_ret;
-
- struct ref *refs = NULL;
- struct ref *ref = NULL;
- struct ref *last_ref = NULL;
-
- struct walker *walker;
-
- if (for_push)
- return NULL;
-
- if (!transport->data)
- transport->data = get_http_walker(transport->url,
- transport->remote);
-
- walker = transport->data;
-
- refs_url = xmalloc(strlen(transport->url) + 11);
- sprintf(refs_url, "%s/info/refs", transport->url);
-
- http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
- switch (http_ret) {
- case HTTP_OK:
- break;
- case HTTP_MISSING_TARGET:
- die("%s not found: did you run git update-server-info on the"
- " server?", refs_url);
- default:
- http_error(refs_url, http_ret);
- die("HTTP request failed");
- }
-
- data = buffer.buf;
- start = NULL;
- mid = data;
- while (i < buffer.len) {
- if (!start)
- start = &data[i];
- if (data[i] == '\t')
- mid = &data[i];
- if (data[i] == '\n') {
- data[i] = 0;
- ref_name = mid + 1;
- ref = xmalloc(sizeof(struct ref) +
- strlen(ref_name) + 1);
- memset(ref, 0, sizeof(struct ref));
- strcpy(ref->name, ref_name);
- get_sha1_hex(start, ref->old_sha1);
- if (!refs)
- refs = ref;
- if (last_ref)
- last_ref->next = ref;
- last_ref = ref;
- start = NULL;
- }
- i++;
- }
-
- strbuf_release(&buffer);
-
- ref = alloc_ref("HEAD");
- if (!walker->fetch_ref(walker, ref) &&
- !resolve_remote_symref(ref, refs)) {
- ref->next = refs;
- refs = ref;
- } else {
- free(ref);
- }
-
- strbuf_release(&buffer);
- free(refs_url);
- return refs;
-}
-
-static int fetch_objs_via_curl(struct transport *transport,
- int nr_objs, const struct ref **to_fetch)
-{
- if (!transport->data)
- transport->data = get_http_walker(transport->url,
- transport->remote);
- return fetch_objs_via_walker(transport, nr_objs, to_fetch);
+ return !!run_command_v_opt(argv, RUN_GIT_CMD);
}
#endif
@@ -681,6 +535,21 @@ static int fetch_refs_via_pack(struct transport *transport,
return (refs ? 0 : -1);
}
+static int push_had_errors(struct ref *ref)
+{
+ for (; ref; ref = ref->next) {
+ switch (ref->status) {
+ case REF_STATUS_NONE:
+ case REF_STATUS_UPTODATE:
+ case REF_STATUS_OK:
+ break;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+}
+
static int refs_pushed(struct ref *ref)
{
for (; ref; ref = ref->next) {
@@ -895,6 +764,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
args.use_thin_pack = data->thin;
args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
+ args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
ret = send_pack(&args, data->fd, data->conn, remote_refs,
@@ -953,14 +823,12 @@ struct transport *transport_get(struct remote *remote, const char *url)
} else if (!prefixcmp(url, "http://")
|| !prefixcmp(url, "https://")
|| !prefixcmp(url, "ftp://")) {
+ transport_helper_init(ret, "curl");
#ifdef NO_CURL
error("git was compiled without libcurl support.");
#else
- ret->get_refs_list = get_refs_via_curl;
- ret->fetch = fetch_objs_via_curl;
ret->push = curl_transport_push;
#endif
- ret->disconnect = disconnect_walker;
} else if (is_local(url) && is_file(url)) {
struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
@@ -1013,6 +881,7 @@ int transport_push(struct transport *transport,
struct ref *local_refs = get_local_heads();
int match_flags = MATCH_REFS_NONE;
int verbose = flags & TRANSPORT_PUSH_VERBOSE;
+ int quiet = flags & TRANSPORT_PUSH_QUIET;
int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
int ret;
@@ -1028,7 +897,10 @@ int transport_push(struct transport *transport,
ret = transport->push_refs(transport, remote_refs, flags);
- print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain, nonfastforward);
+ if (!quiet || push_had_errors(remote_refs))
+ print_push_status(transport->url, remote_refs,
+ verbose | porcelain, porcelain,
+ nonfastforward);
if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
struct ref *ref;
@@ -1036,7 +908,7 @@ int transport_push(struct transport *transport,
update_tracking_ref(transport->remote, ref, verbose);
}
- if (!ret && !refs_pushed(remote_refs))
+ if (!quiet && !ret && !refs_pushed(remote_refs))
fprintf(stderr, "Everything up-to-date\n");
return ret;
}
@@ -1053,11 +925,12 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
int transport_fetch_refs(struct transport *transport, const struct ref *refs)
{
int rc;
- int nr_heads = 0, nr_alloc = 0;
+ int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
const struct ref **heads = NULL;
const struct ref *rm;
for (rm = refs; rm; rm = rm->next) {
+ nr_refs++;
if (rm->peer_ref &&
!hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
continue;
@@ -1065,6 +938,19 @@ int transport_fetch_refs(struct transport *transport, const struct ref *refs)
heads[nr_heads++] = rm;
}
+ if (!nr_heads) {
+ /*
+ * When deepening of a shallow repository is requested,
+ * then local and remote refs are likely to still be equal.
+ * Just feed them all to the fetch method in that case.
+ * This condition shouldn't be met in a non-deepening fetch
+ * (see builtin-fetch.c:quickfetch()).
+ */
+ heads = xmalloc(nr_refs * sizeof(*heads));
+ for (rm = refs; rm; rm = rm->next)
+ heads[nr_heads++] = rm;
+ }
+
rc = transport->fetch(transport, nr_heads, heads);
free(heads);
return rc;
diff --git a/transport.h b/transport.h
index 639f13dcf..c14da6f1e 100644
--- a/transport.h
+++ b/transport.h
@@ -36,6 +36,7 @@ struct transport {
#define TRANSPORT_PUSH_MIRROR 8
#define TRANSPORT_PUSH_VERBOSE 16
#define TRANSPORT_PUSH_PORCELAIN 32
+#define TRANSPORT_PUSH_QUIET 64
/* Returns a transport suitable for the url */
struct transport *transport_get(struct remote *, const char *);
@@ -78,4 +79,7 @@ void transport_unlock_pack(struct transport *transport);
int transport_disconnect(struct transport *transport);
char *transport_anonymize_url(const char *url);
+/* Transport methods defined outside transport.c */
+int transport_helper_init(struct transport *transport, const char *name);
+
#endif
diff --git a/unpack-trees.h b/unpack-trees.h
index 1e0e2325f..d19df44f4 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -17,18 +17,18 @@ struct unpack_trees_error_msgs {
};
struct unpack_trees_options {
- unsigned int reset:1,
- merge:1,
- update:1,
- index_only:1,
- nontrivial_merge:1,
- trivial_merges_only:1,
- verbose_update:1,
- aggressive:1,
- skip_unmerged:1,
- initial_checkout:1,
- diff_index_cached:1,
- gently:1;
+ unsigned int reset,
+ merge,
+ update,
+ index_only,
+ nontrivial_merge,
+ trivial_merges_only,
+ verbose_update,
+ aggressive,
+ skip_unmerged,
+ initial_checkout,
+ diff_index_cached,
+ gently;
const char *prefix;
int pos;
struct dir_struct *dir;
diff --git a/update-server-info.c b/update-server-info.c
deleted file mode 100644
index 7b38fd867..000000000
--- a/update-server-info.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "cache.h"
-#include "exec_cmd.h"
-
-static const char update_server_info_usage[] =
-"git update-server-info [--force]";
-
-int main(int ac, char **av)
-{
- int i;
- int force = 0;
- for (i = 1; i < ac; i++) {
- if (av[i][0] == '-') {
- if (!strcmp("--force", av[i]) ||
- !strcmp("-f", av[i]))
- force = 1;
- else
- usage(update_server_info_usage);
- }
- }
- if (i != ac)
- usage(update_server_info_usage);
-
- git_extract_argv0_path(av[0]);
-
- setup_git_directory();
-
- return !!update_server_info(force);
-}
diff --git a/upload-pack.c b/upload-pack.c
index 841ebb534..38ddac2e8 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -29,8 +29,10 @@ static unsigned long oldest_have;
static int multi_ack, nr_our_refs;
static int use_thin_pack, use_ofs_delta, use_include_tag;
static int no_progress, daemon_mode;
+static int shallow_nr;
static struct object_array have_obj;
static struct object_array want_obj;
+static struct object_array extra_edge_obj;
static unsigned int timeout;
/* 0 for no sideband,
* otherwise maximum packet size (up to 65520 bytes).
@@ -106,9 +108,7 @@ static int do_rev_list(int fd, void *create_full_pack)
int i;
struct rev_info revs;
- pack_pipe = fdopen(fd, "w");
- if (create_full_pack)
- use_thin_pack = 0; /* no point doing it */
+ pack_pipe = xfdopen(fd, "w");
init_revisions(&revs, NULL);
revs.tag_objects = 1;
revs.tree_objects = 1;
@@ -136,14 +136,76 @@ static int do_rev_list(int fd, void *create_full_pack)
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits, &revs, show_edge);
+ if (use_thin_pack)
+ for (i = 0; i < extra_edge_obj.nr; i++)
+ fprintf(pack_pipe, "-%s\n", sha1_to_hex(
+ extra_edge_obj.objects[i].item->sha1));
traverse_commit_list(&revs, show_commit, show_object, NULL);
fflush(pack_pipe);
fclose(pack_pipe);
return 0;
}
+static int feed_msg_to_hook(int fd, const char *fmt, ...)
+{
+ int cnt;
+ char buf[1024];
+ va_list params;
+
+ va_start(params, fmt);
+ cnt = vsprintf(buf, fmt, params);
+ va_end(params);
+ return write_in_full(fd, buf, cnt) != cnt;
+}
+
+static int feed_obj_to_hook(const char *label, struct object_array *oa, int i, int fd)
+{
+ return feed_msg_to_hook(fd, "%s %s\n", label,
+ sha1_to_hex(oa->objects[i].item->sha1));
+}
+
+static int run_post_upload_pack_hook(size_t total, struct timeval *tv)
+{
+ const char *argv[2];
+ struct child_process proc;
+ int err, i;
+
+ argv[0] = "hooks/post-upload-pack";
+ argv[1] = NULL;
+
+ if (access(argv[0], X_OK) < 0)
+ return 0;
+
+ memset(&proc, 0, sizeof(proc));
+ proc.argv = argv;
+ proc.in = -1;
+ proc.stdout_to_stderr = 1;
+ err = start_command(&proc);
+ if (err)
+ return err;
+ for (i = 0; !err && i < want_obj.nr; i++)
+ err |= feed_obj_to_hook("want", &want_obj, i, proc.in);
+ for (i = 0; !err && i < have_obj.nr; i++)
+ err |= feed_obj_to_hook("have", &have_obj, i, proc.in);
+ if (!err)
+ err |= feed_msg_to_hook(proc.in, "time %ld.%06ld\n",
+ (long)tv->tv_sec, (long)tv->tv_usec);
+ if (!err)
+ err |= feed_msg_to_hook(proc.in, "size %ld\n", (long)total);
+ if (!err)
+ err |= feed_msg_to_hook(proc.in, "kind %s\n",
+ (nr_our_refs == want_obj.nr && !have_obj.nr)
+ ? "clone" : "fetch");
+ if (close(proc.in))
+ err = 1;
+ if (finish_command(&proc))
+ err = 1;
+ return err;
+}
+
static void create_pack_file(void)
{
+ struct timeval start_tv, tv;
struct async rev_list;
struct child_process pack_objects;
int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
@@ -151,17 +213,27 @@ static void create_pack_file(void)
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
int buffered = -1;
- ssize_t sz;
+ ssize_t sz, total_sz;
const char *argv[10];
int arg = 0;
- rev_list.proc = do_rev_list;
- /* .data is just a boolean: any non-NULL value will do */
- rev_list.data = create_full_pack ? &rev_list : NULL;
- if (start_async(&rev_list))
- die("git upload-pack: unable to fork git-rev-list");
+ gettimeofday(&start_tv, NULL);
+ total_sz = 0;
+ if (shallow_nr) {
+ rev_list.proc = do_rev_list;
+ rev_list.data = 0;
+ if (start_async(&rev_list))
+ die("git upload-pack: unable to fork git-rev-list");
+ argv[arg++] = "pack-objects";
+ } else {
+ argv[arg++] = "pack-objects";
+ argv[arg++] = "--revs";
+ if (create_full_pack)
+ argv[arg++] = "--all";
+ else if (use_thin_pack)
+ argv[arg++] = "--thin";
+ }
- argv[arg++] = "pack-objects";
argv[arg++] = "--stdout";
if (!no_progress)
argv[arg++] = "--progress";
@@ -172,7 +244,7 @@ static void create_pack_file(void)
argv[arg++] = NULL;
memset(&pack_objects, 0, sizeof(pack_objects));
- pack_objects.in = rev_list.out; /* start_command closes it */
+ pack_objects.in = shallow_nr ? rev_list.out : -1;
pack_objects.out = -1;
pack_objects.err = -1;
pack_objects.git_cmd = 1;
@@ -181,6 +253,24 @@ static void create_pack_file(void)
if (start_command(&pack_objects))
die("git upload-pack: unable to fork git-pack-objects");
+ /* pass on revisions we (don't) want */
+ if (!shallow_nr) {
+ FILE *pipe_fd = xfdopen(pack_objects.in, "w");
+ if (!create_full_pack) {
+ int i;
+ for (i = 0; i < want_obj.nr; i++)
+ fprintf(pipe_fd, "%s\n", sha1_to_hex(want_obj.objects[i].item->sha1));
+ fprintf(pipe_fd, "--not\n");
+ for (i = 0; i < have_obj.nr; i++)
+ fprintf(pipe_fd, "%s\n", sha1_to_hex(have_obj.objects[i].item->sha1));
+ }
+
+ fprintf(pipe_fd, "\n");
+ fflush(pipe_fd);
+ fclose(pipe_fd);
+ }
+
+
/* We read from pack_objects.err to capture stderr output for
* progress bar, and pack_objects.out to capture the pack data.
*/
@@ -237,7 +327,7 @@ static void create_pack_file(void)
sz = xread(pack_objects.out, cp,
sizeof(data) - outsz);
if (0 < sz)
- ;
+ total_sz += sz;
else if (sz == 0) {
close(pack_objects.out);
pack_objects.out = -1;
@@ -276,7 +366,7 @@ static void create_pack_file(void)
error("git upload-pack: git-pack-objects died with error.");
goto fail;
}
- if (finish_async(&rev_list))
+ if (shallow_nr && finish_async(&rev_list))
goto fail; /* error was already reported */
/* flush the data */
@@ -289,6 +379,16 @@ static void create_pack_file(void)
}
if (use_sideband)
packet_flush(1);
+
+ gettimeofday(&tv, NULL);
+ tv.tv_sec -= start_tv.tv_sec;
+ if (tv.tv_usec < start_tv.tv_usec) {
+ tv.tv_sec--;
+ tv.tv_usec += 1000000;
+ }
+ tv.tv_usec -= start_tv.tv_usec;
+ if (run_post_upload_pack_hook(total_sz, &tv))
+ warning("post-upload-hook failed");
return;
fail:
@@ -402,7 +502,7 @@ static int get_common_commits(void)
save_commit_buffer = 0;
- for(;;) {
+ for (;;) {
int len = packet_read_line(0, line, sizeof(line));
reset_timeout();
@@ -451,8 +551,9 @@ static void receive_needs(void)
static char line[1000];
int len, depth = 0;
+ shallow_nr = 0;
if (debug_fd)
- write_in_full(debug_fd, "#S\n", 3);
+ write_str_in_full(debug_fd, "#S\n");
for (;;) {
struct object *o;
unsigned char sha1_buf[20];
@@ -466,7 +567,6 @@ static void receive_needs(void)
if (!prefixcmp(line, "shallow ")) {
unsigned char sha1[20];
struct object *object;
- use_thin_pack = 0;
if (get_sha1(line + 8, sha1))
die("invalid shallow line: %s", line);
object = parse_object(sha1);
@@ -478,7 +578,6 @@ static void receive_needs(void)
}
if (!prefixcmp(line, "deepen ")) {
char *end;
- use_thin_pack = 0;
depth = strtol(line + 7, &end, 0);
if (end == line + 7 || depth <= 0)
die("Invalid deepen: %s", line);
@@ -520,7 +619,7 @@ static void receive_needs(void)
}
}
if (debug_fd)
- write_in_full(debug_fd, "#E\n", 3);
+ write_str_in_full(debug_fd, "#E\n");
if (!use_sideband && daemon_mode)
no_progress = 1;
@@ -538,6 +637,7 @@ static void receive_needs(void)
packet_write(1, "shallow %s",
sha1_to_hex(object->sha1));
register_shallow(object->sha1);
+ shallow_nr++;
}
result = result->next;
}
@@ -560,6 +660,7 @@ static void receive_needs(void)
NULL, &want_obj);
parents = parents->next;
}
+ add_object_array(object, NULL, &extra_edge_obj);
}
/* make sure commit traversal conforms to client */
register_shallow(object->sha1);
@@ -571,6 +672,8 @@ static void receive_needs(void)
for (i = 0; i < shallows.nr; i++)
register_shallow(shallows.objects[i].item->sha1);
}
+
+ shallow_nr += shallows.nr;
free(shallows.objects);
}
@@ -622,6 +725,7 @@ int main(int argc, char **argv)
int strict = 0;
git_extract_argv0_path(argv[0]);
+ read_replace_refs = 0;
for (i = 1; i < argc; i++) {
char *arg = argv[i];
diff --git a/var.c b/var.c
index 7362ed873..125c0d167 100644
--- a/var.c
+++ b/var.c
@@ -21,9 +21,8 @@ static struct git_var git_vars[] = {
static void list_vars(void)
{
struct git_var *ptr;
- for(ptr = git_vars; ptr->read; ptr++) {
+ for (ptr = git_vars; ptr->read; ptr++)
printf("%s=%s\n", ptr->name, ptr->read(IDENT_WARN_ON_NO_NAME));
- }
}
static const char *read_var(const char *var)
@@ -31,7 +30,7 @@ static const char *read_var(const char *var)
struct git_var *ptr;
const char *val;
val = NULL;
- for(ptr = git_vars; ptr->read; ptr++) {
+ for (ptr = git_vars; ptr->read; ptr++) {
if (strcmp(var, ptr->name) == 0) {
val = ptr->read(IDENT_ERROR_ON_NO_NAME);
break;
diff --git a/wt-status.c b/wt-status.c
index 47735d812..38eb24536 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,6 +1,5 @@
#include "cache.h"
#include "wt-status.h"
-#include "color.h"
#include "object.h"
#include "dir.h"
#include "commit.h"
@@ -11,38 +10,18 @@
#include "run-command.h"
#include "remote.h"
-int wt_status_relative_paths = 1;
-int wt_status_use_color = -1;
-static int wt_status_submodule_summary;
-static char wt_status_colors[][COLOR_MAXLEN] = {
+static char default_wt_status_colors[][COLOR_MAXLEN] = {
GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */
GIT_COLOR_GREEN, /* WT_STATUS_UPDATED */
GIT_COLOR_RED, /* WT_STATUS_CHANGED */
GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */
GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */
+ GIT_COLOR_RED, /* WT_STATUS_UNMERGED */
};
-enum untracked_status_type show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
-
-static int parse_status_slot(const char *var, int offset)
-{
- if (!strcasecmp(var+offset, "header"))
- return WT_STATUS_HEADER;
- if (!strcasecmp(var+offset, "updated")
- || !strcasecmp(var+offset, "added"))
- return WT_STATUS_UPDATED;
- if (!strcasecmp(var+offset, "changed"))
- return WT_STATUS_CHANGED;
- if (!strcasecmp(var+offset, "untracked"))
- return WT_STATUS_UNTRACKED;
- if (!strcasecmp(var+offset, "nobranch"))
- return WT_STATUS_NOBRANCH;
- die("bad config variable '%s'", var);
-}
-
-static const char *color(int slot)
+static const char *color(int slot, struct wt_status *s)
{
- return wt_status_use_color > 0 ? wt_status_colors[slot] : "";
+ return s->use_color > 0 ? s->color_palette[slot] : "";
}
void wt_status_prepare(struct wt_status *s)
@@ -51,17 +30,40 @@ void wt_status_prepare(struct wt_status *s)
const char *head;
memset(s, 0, sizeof(*s));
+ memcpy(s->color_palette, default_wt_status_colors,
+ sizeof(default_wt_status_colors));
+ s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
+ s->use_color = -1;
+ s->relative_paths = 1;
head = resolve_ref("HEAD", sha1, 0, NULL);
s->branch = head ? xstrdup(head) : NULL;
s->reference = "HEAD";
s->fp = stdout;
s->index_file = get_index_file();
+ s->change.strdup_strings = 1;
+ s->untracked.strdup_strings = 1;
+}
+
+static void wt_status_print_unmerged_header(struct wt_status *s)
+{
+ const char *c = color(WT_STATUS_HEADER, s);
+ color_fprintf_ln(s->fp, c, "# Unmerged paths:");
+ if (!advice_status_hints)
+ return;
+ if (!s->is_initial)
+ color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
+ else
+ color_fprintf_ln(s->fp, c, "# (use \"git rm --cached <file>...\" to unstage)");
+ color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to mark resolution)");
+ color_fprintf_ln(s->fp, c, "#");
}
static void wt_status_print_cached_header(struct wt_status *s)
{
- const char *c = color(WT_STATUS_HEADER);
+ const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Changes to be committed:");
+ if (!advice_status_hints)
+ return;
if (!s->is_initial) {
color_fprintf_ln(s->fp, c, "# (use \"git reset %s <file>...\" to unstage)", s->reference);
} else {
@@ -73,8 +75,10 @@ static void wt_status_print_cached_header(struct wt_status *s)
static void wt_status_print_dirty_header(struct wt_status *s,
int has_deleted)
{
- const char *c = color(WT_STATUS_HEADER);
+ const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Changed but not updated:");
+ if (!advice_status_hints)
+ return;
if (!has_deleted)
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to update what will be committed)");
else
@@ -85,31 +89,73 @@ static void wt_status_print_dirty_header(struct wt_status *s,
static void wt_status_print_untracked_header(struct wt_status *s)
{
- const char *c = color(WT_STATUS_HEADER);
+ const char *c = color(WT_STATUS_HEADER, s);
color_fprintf_ln(s->fp, c, "# Untracked files:");
+ if (!advice_status_hints)
+ return;
color_fprintf_ln(s->fp, c, "# (use \"git add <file>...\" to include in what will be committed)");
color_fprintf_ln(s->fp, c, "#");
}
static void wt_status_print_trailer(struct wt_status *s)
{
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
#define quote_path quote_path_relative
-static void wt_status_print_filepair(struct wt_status *s,
- int t, struct diff_filepair *p)
+static void wt_status_print_unmerged_data(struct wt_status *s,
+ struct string_list_item *it)
{
- const char *c = color(t);
+ const char *c = color(WT_STATUS_UNMERGED, s);
+ struct wt_status_change_data *d = it->util;
+ struct strbuf onebuf = STRBUF_INIT;
+ const char *one, *how = "bug";
+
+ one = quote_path(it->string, -1, &onebuf, s->prefix);
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+ switch (d->stagemask) {
+ case 1: how = "both deleted:"; break;
+ case 2: how = "added by us:"; break;
+ case 3: how = "deleted by them:"; break;
+ case 4: how = "added by them:"; break;
+ case 5: how = "deleted by us:"; break;
+ case 6: how = "both added:"; break;
+ case 7: how = "both modified:"; break;
+ }
+ color_fprintf(s->fp, c, "%-20s%s\n", how, one);
+ strbuf_release(&onebuf);
+}
+
+static void wt_status_print_change_data(struct wt_status *s,
+ int change_type,
+ struct string_list_item *it)
+{
+ struct wt_status_change_data *d = it->util;
+ const char *c = color(change_type, s);
+ int status = status;
+ char *one_name;
+ char *two_name;
const char *one, *two;
struct strbuf onebuf = STRBUF_INIT, twobuf = STRBUF_INIT;
- one = quote_path(p->one->path, -1, &onebuf, s->prefix);
- two = quote_path(p->two->path, -1, &twobuf, s->prefix);
+ one_name = two_name = it->string;
+ switch (change_type) {
+ case WT_STATUS_UPDATED:
+ status = d->index_status;
+ if (d->head_path)
+ one_name = d->head_path;
+ break;
+ case WT_STATUS_CHANGED:
+ status = d->worktree_status;
+ break;
+ }
+
+ one = quote_path(one_name, -1, &onebuf, s->prefix);
+ two = quote_path(two_name, -1, &twobuf, s->prefix);
- color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
- switch (p->status) {
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+ switch (status) {
case DIFF_STATUS_ADDED:
color_fprintf(s->fp, c, "new file: %s", one);
break;
@@ -135,64 +181,114 @@ static void wt_status_print_filepair(struct wt_status *s,
color_fprintf(s->fp, c, "unmerged: %s", one);
break;
default:
- die("bug: unhandled diff status %c", p->status);
+ die("bug: unhandled diff status %c", status);
}
fprintf(s->fp, "\n");
strbuf_release(&onebuf);
strbuf_release(&twobuf);
}
-static void wt_status_print_updated_cb(struct diff_queue_struct *q,
- struct diff_options *options,
- void *data)
+static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
{
struct wt_status *s = data;
- int shown_header = 0;
int i;
+
+ if (!q->nr)
+ return;
+ s->workdir_dirty = 1;
for (i = 0; i < q->nr; i++) {
- if (q->queue[i]->status == 'U')
- continue;
- if (!shown_header) {
- wt_status_print_cached_header(s);
- s->commitable = 1;
- shown_header = 1;
+ struct diff_filepair *p;
+ struct string_list_item *it;
+ struct wt_status_change_data *d;
+
+ p = q->queue[i];
+ it = string_list_insert(p->one->path, &s->change);
+ d = it->util;
+ if (!d) {
+ d = xcalloc(1, sizeof(*d));
+ it->util = d;
}
- wt_status_print_filepair(s, WT_STATUS_UPDATED, q->queue[i]);
+ if (!d->worktree_status)
+ d->worktree_status = p->status;
}
- if (shown_header)
- wt_status_print_trailer(s);
}
-static void wt_status_print_changed_cb(struct diff_queue_struct *q,
- struct diff_options *options,
- void *data)
+static int unmerged_mask(const char *path)
+{
+ int pos, mask;
+ struct cache_entry *ce;
+
+ pos = cache_name_pos(path, strlen(path));
+ if (0 <= pos)
+ return 0;
+
+ mask = 0;
+ pos = -pos-1;
+ while (pos < active_nr) {
+ ce = active_cache[pos++];
+ if (strcmp(ce->name, path) || !ce_stage(ce))
+ break;
+ mask |= (1 << (ce_stage(ce) - 1));
+ }
+ return mask;
+}
+
+static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
{
struct wt_status *s = data;
int i;
- if (q->nr) {
- int has_deleted = 0;
- s->workdir_dirty = 1;
- for (i = 0; i < q->nr; i++)
- if (q->queue[i]->status == DIFF_STATUS_DELETED) {
- has_deleted = 1;
- break;
- }
- wt_status_print_dirty_header(s, has_deleted);
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p;
+ struct string_list_item *it;
+ struct wt_status_change_data *d;
+
+ p = q->queue[i];
+ it = string_list_insert(p->two->path, &s->change);
+ d = it->util;
+ if (!d) {
+ d = xcalloc(1, sizeof(*d));
+ it->util = d;
+ }
+ if (!d->index_status)
+ d->index_status = p->status;
+ switch (p->status) {
+ case DIFF_STATUS_COPIED:
+ case DIFF_STATUS_RENAMED:
+ d->head_path = xstrdup(p->one->path);
+ break;
+ case DIFF_STATUS_UNMERGED:
+ d->stagemask = unmerged_mask(p->two->path);
+ break;
+ }
}
- for (i = 0; i < q->nr; i++)
- wt_status_print_filepair(s, WT_STATUS_CHANGED, q->queue[i]);
- if (q->nr)
- wt_status_print_trailer(s);
}
-static void wt_status_print_updated(struct wt_status *s)
+static void wt_status_collect_changes_worktree(struct wt_status *s)
{
struct rev_info rev;
+
+ init_revisions(&rev, NULL);
+ setup_revisions(0, NULL, &rev, NULL);
+ rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+ rev.diffopt.format_callback = wt_status_collect_changed_cb;
+ rev.diffopt.format_callback_data = s;
+ run_diff_files(&rev, 0);
+}
+
+static void wt_status_collect_changes_index(struct wt_status *s)
+{
+ struct rev_info rev;
+
init_revisions(&rev, NULL);
setup_revisions(0, NULL, &rev,
s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = wt_status_print_updated_cb;
+ rev.diffopt.format_callback = wt_status_collect_updated_cb;
rev.diffopt.format_callback_data = s;
rev.diffopt.detect_rename = 1;
rev.diffopt.rename_limit = 200;
@@ -200,15 +296,155 @@ static void wt_status_print_updated(struct wt_status *s)
run_diff_index(&rev, 1);
}
+static void wt_status_collect_changes_initial(struct wt_status *s)
+{
+ int i;
+
+ for (i = 0; i < active_nr; i++) {
+ struct string_list_item *it;
+ struct wt_status_change_data *d;
+ struct cache_entry *ce = active_cache[i];
+
+ it = string_list_insert(ce->name, &s->change);
+ d = it->util;
+ if (!d) {
+ d = xcalloc(1, sizeof(*d));
+ it->util = d;
+ }
+ if (ce_stage(ce)) {
+ d->index_status = DIFF_STATUS_UNMERGED;
+ d->stagemask |= (1 << (ce_stage(ce) - 1));
+ }
+ else
+ d->index_status = DIFF_STATUS_ADDED;
+ }
+}
+
+static void wt_status_collect_untracked(struct wt_status *s)
+{
+ int i;
+ struct dir_struct dir;
+
+ if (!s->show_untracked_files)
+ return;
+ memset(&dir, 0, sizeof(dir));
+ if (s->show_untracked_files != SHOW_ALL_UNTRACKED_FILES)
+ dir.flags |=
+ DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
+ setup_standard_excludes(&dir);
+
+ fill_directory(&dir, NULL);
+ for (i = 0; i < dir.nr; i++) {
+ struct dir_entry *ent = dir.entries[i];
+ if (!cache_name_is_other(ent->name, ent->len))
+ continue;
+ s->workdir_untracked = 1;
+ string_list_insert(ent->name, &s->untracked);
+ }
+}
+
+void wt_status_collect(struct wt_status *s)
+{
+ wt_status_collect_changes_worktree(s);
+
+ if (s->is_initial)
+ wt_status_collect_changes_initial(s);
+ else
+ wt_status_collect_changes_index(s);
+ wt_status_collect_untracked(s);
+}
+
+static void wt_status_print_unmerged(struct wt_status *s)
+{
+ int shown_header = 0;
+ int i;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ struct string_list_item *it;
+ it = &(s->change.items[i]);
+ d = it->util;
+ if (!d->stagemask)
+ continue;
+ if (!shown_header) {
+ wt_status_print_unmerged_header(s);
+ shown_header = 1;
+ }
+ wt_status_print_unmerged_data(s, it);
+ }
+ if (shown_header)
+ wt_status_print_trailer(s);
+
+}
+
+static void wt_status_print_updated(struct wt_status *s)
+{
+ int shown_header = 0;
+ int i;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ struct string_list_item *it;
+ it = &(s->change.items[i]);
+ d = it->util;
+ if (!d->index_status ||
+ d->index_status == DIFF_STATUS_UNMERGED)
+ continue;
+ if (!shown_header) {
+ wt_status_print_cached_header(s);
+ s->commitable = 1;
+ shown_header = 1;
+ }
+ wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
+ }
+ if (shown_header)
+ wt_status_print_trailer(s);
+}
+
+/*
+ * -1 : has delete
+ * 0 : no change
+ * 1 : some change but no delete
+ */
+static int wt_status_check_worktree_changes(struct wt_status *s)
+{
+ int i;
+ int changes = 0;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ d = s->change.items[i].util;
+ if (!d->worktree_status ||
+ d->worktree_status == DIFF_STATUS_UNMERGED)
+ continue;
+ changes = 1;
+ if (d->worktree_status == DIFF_STATUS_DELETED)
+ return -1;
+ }
+ return changes;
+}
+
static void wt_status_print_changed(struct wt_status *s)
{
- struct rev_info rev;
- init_revisions(&rev, "");
- setup_revisions(0, NULL, &rev, NULL);
- rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = wt_status_print_changed_cb;
- rev.diffopt.format_callback_data = s;
- run_diff_files(&rev, 0);
+ int i;
+ int worktree_changes = wt_status_check_worktree_changes(s);
+
+ if (!worktree_changes)
+ return;
+
+ wt_status_print_dirty_header(s, worktree_changes < 0);
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ struct string_list_item *it;
+ it = &(s->change.items[i]);
+ d = it->util;
+ if (!d->worktree_status ||
+ d->worktree_status == DIFF_STATUS_UNMERGED)
+ continue;
+ wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
+ }
+ wt_status_print_trailer(s);
}
static void wt_status_print_submodule_summary(struct wt_status *s)
@@ -228,7 +464,7 @@ static void wt_status_print_submodule_summary(struct wt_status *s)
NULL
};
- sprintf(summary_limit, "%d", wt_status_submodule_summary);
+ sprintf(summary_limit, "%d", s->submodule_summary);
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file);
memset(&sm_summary, 0, sizeof(sm_summary));
@@ -243,32 +479,20 @@ static void wt_status_print_submodule_summary(struct wt_status *s)
static void wt_status_print_untracked(struct wt_status *s)
{
- struct dir_struct dir;
int i;
- int shown_header = 0;
struct strbuf buf = STRBUF_INIT;
- memset(&dir, 0, sizeof(dir));
-
- if (!s->untracked)
- dir.flags |=
- DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
- setup_standard_excludes(&dir);
+ if (!s->untracked.nr)
+ return;
- fill_directory(&dir, NULL);
- for(i = 0; i < dir.nr; i++) {
- struct dir_entry *ent = dir.entries[i];
- if (!cache_name_is_other(ent->name, ent->len))
- continue;
- if (!shown_header) {
- s->workdir_untracked = 1;
- wt_status_print_untracked_header(s);
- shown_header = 1;
- }
- color_fprintf(s->fp, color(WT_STATUS_HEADER), "#\t");
- color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED), "%s",
- quote_path(ent->name, ent->len,
- &buf, s->prefix));
+ wt_status_print_untracked_header(s);
+ for (i = 0; i < s->untracked.nr; i++) {
+ struct string_list_item *it;
+ it = &(s->untracked.items[i]);
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t");
+ color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s",
+ quote_path(it->string, strlen(it->string),
+ &buf, s->prefix));
}
strbuf_release(&buf);
}
@@ -310,15 +534,15 @@ static void wt_status_print_tracking(struct wt_status *s)
return;
for (cp = sb.buf; (ep = strchr(cp, '\n')) != NULL; cp = ep + 1)
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER),
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s),
"# %.*s", (int)(ep - cp), cp);
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
void wt_status_print(struct wt_status *s)
{
unsigned char sha1[20];
- const char *branch_color = color(WT_STATUS_HEADER);
+ const char *branch_color = color(WT_STATUS_HEADER, s);
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
if (s->branch) {
@@ -328,26 +552,29 @@ void wt_status_print(struct wt_status *s)
branch_name += 11;
else if (!strcmp(branch_name, "HEAD")) {
branch_name = "";
- branch_color = color(WT_STATUS_NOBRANCH);
+ branch_color = color(WT_STATUS_NOBRANCH, s);
on_what = "Not currently on any branch.";
}
- color_fprintf(s->fp, color(WT_STATUS_HEADER), "# ");
+ color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# ");
color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name);
if (!s->is_initial)
wt_status_print_tracking(s);
}
+ wt_status_collect(s);
+
if (s->is_initial) {
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "# Initial commit");
- color_fprintf_ln(s->fp, color(WT_STATUS_HEADER), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit");
+ color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#");
}
wt_status_print_updated(s);
+ wt_status_print_unmerged(s);
wt_status_print_changed(s);
- if (wt_status_submodule_summary)
+ if (s->submodule_summary)
wt_status_print_submodule_summary(s);
- if (show_untracked_files)
+ if (s->show_untracked_files)
wt_status_print_untracked(s);
else if (s->commitable)
fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n");
@@ -361,53 +588,13 @@ void wt_status_print(struct wt_status *s)
; /* nothing */
else if (s->workdir_dirty)
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
- else if (s->workdir_untracked)
+ else if (s->untracked.nr)
printf("nothing added to commit but untracked files present (use \"git add\" to track)\n");
else if (s->is_initial)
printf("nothing to commit (create/copy files and use \"git add\" to track)\n");
- else if (!show_untracked_files)
+ else if (!s->show_untracked_files)
printf("nothing to commit (use -u to show untracked files)\n");
else
printf("nothing to commit (working directory clean)\n");
}
}
-
-int git_status_config(const char *k, const char *v, void *cb)
-{
- if (!strcmp(k, "status.submodulesummary")) {
- int is_bool;
- wt_status_submodule_summary = git_config_bool_or_int(k, v, &is_bool);
- if (is_bool && wt_status_submodule_summary)
- wt_status_submodule_summary = -1;
- return 0;
- }
- if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
- wt_status_use_color = git_config_colorbool(k, v, -1);
- return 0;
- }
- if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
- int slot = parse_status_slot(k, 13);
- if (!v)
- return config_error_nonbool(k);
- color_parse(v, k, wt_status_colors[slot]);
- return 0;
- }
- if (!strcmp(k, "status.relativepaths")) {
- wt_status_relative_paths = git_config_bool(k, v);
- return 0;
- }
- if (!strcmp(k, "status.showuntrackedfiles")) {
- if (!v)
- return config_error_nonbool(k);
- else if (!strcmp(v, "no"))
- show_untracked_files = SHOW_NO_UNTRACKED_FILES;
- else if (!strcmp(v, "normal"))
- show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
- else if (!strcmp(v, "all"))
- show_untracked_files = SHOW_ALL_UNTRACKED_FILES;
- else
- return error("Invalid untracked files mode '%s'", v);
- return 0;
- }
- return git_diff_ui_config(k, v, cb);
-}
diff --git a/wt-status.h b/wt-status.h
index 78add09bd..a0e75177b 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -2,13 +2,16 @@
#define STATUS_H
#include <stdio.h>
+#include "string-list.h"
+#include "color.h"
enum color_wt_status {
- WT_STATUS_HEADER,
+ WT_STATUS_HEADER = 0,
WT_STATUS_UPDATED,
WT_STATUS_CHANGED,
WT_STATUS_UNTRACKED,
WT_STATUS_NOBRANCH,
+ WT_STATUS_UNMERGED,
};
enum untracked_status_type {
@@ -16,7 +19,13 @@ enum untracked_status_type {
SHOW_NORMAL_UNTRACKED_FILES,
SHOW_ALL_UNTRACKED_FILES
};
-extern enum untracked_status_type show_untracked_files;
+
+struct wt_status_change_data {
+ int worktree_status;
+ int index_status;
+ int stagemask;
+ char *head_path;
+};
struct wt_status {
int is_initial;
@@ -24,8 +33,13 @@ struct wt_status {
const char *reference;
int verbose;
int amend;
- int untracked;
int nowarn;
+ int use_color;
+ int relative_paths;
+ int submodule_summary;
+ enum untracked_status_type show_untracked_files;
+ char color_palette[WT_STATUS_UNMERGED+1][COLOR_MAXLEN];
+
/* These are computed during processing of the individual sections */
int commitable;
int workdir_dirty;
@@ -33,12 +47,12 @@ struct wt_status {
const char *index_file;
FILE *fp;
const char *prefix;
+ struct string_list change;
+ struct string_list untracked;
};
-int git_status_config(const char *var, const char *value, void *cb);
-extern int wt_status_use_color;
-extern int wt_status_relative_paths;
void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);
+void wt_status_collect(struct wt_status *s);
#endif /* STATUS_H */