summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.mailmap1
-rw-r--r--Documentation/RelNotes/1.8.3.2.txt5
-rw-r--r--Documentation/RelNotes/1.8.3.3.txt26
-rw-r--r--Documentation/RelNotes/1.8.4.txt39
-rw-r--r--Documentation/config.txt108
-rw-r--r--Documentation/diff-options.txt3
-rw-r--r--Documentation/git-am.txt4
-rw-r--r--Documentation/git-check-ignore.txt2
-rw-r--r--Documentation/git-clone.txt19
-rw-r--r--Documentation/git-config.txt2
-rw-r--r--Documentation/git-fast-export.txt2
-rw-r--r--Documentation/git-ls-remote.txt11
-rw-r--r--Documentation/git-name-rev.txt3
-rw-r--r--Documentation/git-rebase.txt4
-rw-r--r--Documentation/git-remote.txt25
-rw-r--r--Documentation/git-rev-parse.txt16
-rw-r--r--Documentation/git.txt3
-rw-r--r--Documentation/rev-list-options.txt4
-rw-r--r--Documentation/technical/racy-git.txt6
-rw-r--r--Documentation/urls.txt3
-rw-r--r--Documentation/user-manual.txt36
-rw-r--r--Makefile6
-rw-r--r--builtin/checkout.c11
-rw-r--r--builtin/clone.c5
-rw-r--r--builtin/commit.c83
-rw-r--r--builtin/log.c2
-rw-r--r--builtin/ls-files.c12
-rw-r--r--builtin/name-rev.c36
-rw-r--r--builtin/push.c43
-rw-r--r--builtin/rev-parse.c24
-rw-r--r--builtin/show-branch.c14
-rw-r--r--cache.h60
-rw-r--r--commit-slab.h98
-rw-r--r--commit.c180
-rw-r--r--commit.h17
-rw-r--r--configure.ac2
-rw-r--r--contrib/completion/git-completion.bash6
-rw-r--r--contrib/completion/git-prompt.sh332
-rwxr-xr-xcontrib/mw-to-git/git-remote-mediawiki.perl4
-rw-r--r--diff.c12
-rwxr-xr-xfixup-builtins16
-rwxr-xr-xgit-am.sh8
-rw-r--r--git-rebase--interactive.sh25
-rwxr-xr-xgit-submodule.sh135
-rw-r--r--lockfile.c10
-rw-r--r--prio-queue.c84
-rw-r--r--prio-queue.h48
-rw-r--r--read-cache.c181
-rw-r--r--refs.c422
-rw-r--r--refs.h26
-rw-r--r--remote.c23
-rw-r--r--revision.c13
-rw-r--r--revision.h6
-rw-r--r--t/README10
-rw-r--r--t/lib-rebase.sh90
-rw-r--r--t/lib-t6000.sh104
-rw-r--r--t/perf/README2
-rw-r--r--t/perf/perf-lib.sh3
-rwxr-xr-xt/t0000-basic.sh61
-rwxr-xr-xt/t0009-prio-queue.sh50
-rwxr-xr-xt/t0020-crlf.sh8
-rwxr-xr-xt/t1513-rev-parse-prefix.sh96
-rwxr-xr-xt/t2012-checkout-last.sh34
-rwxr-xr-xt/t3415-rebase-autosquash.sh57
-rwxr-xr-xt/t4015-diff-whitespace.sh345
-rwxr-xr-xt/t5505-remote.sh848
-rwxr-xr-xt/t5528-push-default.sh65
-rwxr-xr-xt/t6002-rev-list-bisect.sh84
-rwxr-xr-xt/t6003-rev-list-topo-order.sh101
-rwxr-xr-xt/t7011-skip-worktree-reading.sh4
-rwxr-xr-xt/t7400-submodule-basic.sh80
-rwxr-xr-xt/t7401-submodule-summary.sh116
-rwxr-xr-xt/t7403-submodule-sync.sh388
-rwxr-xr-xt/t7406-submodule-update.sh15
-rwxr-xr-xt/t7407-submodule-foreach.sh16
-rwxr-xr-xt/t7500-commit.sh6
-rwxr-xr-xt/t7508-status.sh62
-rwxr-xr-xt/t7512-status-help.sh51
-rwxr-xr-xt/t9902-completion.sh135
-rwxr-xr-xt/t9903-bash-prompt.sh464
-rw-r--r--t/test-lib-functions.sh6
-rw-r--r--t/test-lib.sh246
-rwxr-xr-xt/valgrind/valgrind.sh3
-rw-r--r--test-prio-queue.c39
-rw-r--r--wt-status.c10
-rw-r--r--xdiff/xdiff.h2
-rw-r--r--xdiff/xdiffi.c29
-rw-r--r--xdiff/xdiffi.h1
-rw-r--r--xdiff/xemit.c49
-rw-r--r--xdiff/xemit.h2
-rw-r--r--xdiff/xutils.c13
-rw-r--r--xdiff/xutils.h1
93 files changed, 4320 insertions, 1643 deletions
diff --git a/.gitignore b/.gitignore
index c0e00eb37b..efa8db0035 100644
--- a/.gitignore
+++ b/.gitignore
@@ -191,6 +191,7 @@
/test-mktemp
/test-parse-options
/test-path-utils
+/test-prio-queue
/test-read-cache
/test-regex
/test-revision-walking
diff --git a/.mailmap b/.mailmap
index 48d7acf013..345cce698a 100644
--- a/.mailmap
+++ b/.mailmap
@@ -24,6 +24,7 @@ Eric S. Raymond <esr@thyrsus.com>
Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
Fredrik Kuivinen <freku045@student.liu.se>
Frédéric Heitzmann <frederic.heitzmann@gmail.com>
+H. Merijn Brand <h.m.brand@xs4all.nl> H.Merijn Brand <h.m.brand@xs4all.nl>
H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
H. Peter Anvin <hpa@trantor.hos.anvin.org>
diff --git a/Documentation/RelNotes/1.8.3.2.txt b/Documentation/RelNotes/1.8.3.2.txt
index 47ae496b7c..26ae142c3d 100644
--- a/Documentation/RelNotes/1.8.3.2.txt
+++ b/Documentation/RelNotes/1.8.3.2.txt
@@ -4,6 +4,11 @@ Git v1.8.3.2 Release Notes
Fixes since v1.8.3.1
--------------------
+ * Cloning with "git clone --depth N" while fetch.fsckobjects (or
+ transfer.fsckobjects) is set to true did not tell the cut-off
+ points of the shallow history to the process that validates the
+ objects and the history received, causing the validation to fail.
+
* "git checkout foo" DWIMs the intended "upstream" and turns it into
"git checkout -t -b foo remotes/origin/foo". This codepath has been
updated to correctly take existing remote definitions into account.
diff --git a/Documentation/RelNotes/1.8.3.3.txt b/Documentation/RelNotes/1.8.3.3.txt
new file mode 100644
index 0000000000..04289e793c
--- /dev/null
+++ b/Documentation/RelNotes/1.8.3.3.txt
@@ -0,0 +1,26 @@
+Git v1.8.3.3 Release Notes
+==========================
+
+Fixes since v1.8.3.2
+--------------------
+
+ * "gitweb" forgot to clear a global variable $search_regexp upon each
+ request, mistakenly carrying over the previous search to a new one
+ when used as a persistent CGI.
+
+ * The wildmatch engine did not honor WM_CASEFOLD option correctly.
+
+ * "git log -c --follow $path" segfaulted upon hitting the commit that
+ renamed the $path being followed.
+
+ * When a reflog notation is used for implicit "current branch",
+ e.g. "git log @{u}", we did not say which branch and worse said
+ "branch ''" in the error messages.
+
+ * Mac OS X does not like to write(2) more than INT_MAX number of
+ bytes; work it around by chopping write(2) into smaller pieces.
+
+ * Newer MacOS X encourages the programs to compile and link with
+ their CommonCrypto, not with OpenSSL.
+
+Also contains various minor documentation updates.
diff --git a/Documentation/RelNotes/1.8.4.txt b/Documentation/RelNotes/1.8.4.txt
index 1854d1723d..b85f16b378 100644
--- a/Documentation/RelNotes/1.8.4.txt
+++ b/Documentation/RelNotes/1.8.4.txt
@@ -35,6 +35,21 @@ Foreign interfaces, subsystems and ports.
UI, Workflows & Features
+ * Having multiple "fixup!" on a line in the rebase instruction sheet
+ did not work very well with "git rebase -i --autosquash".
+
+ * "git log" learned the "--author-date-order" option, with which the
+ output is topologically sorted and commits in parallel histories
+ are shown intermixed together based on the author timestamp.
+
+ * Various subcommands of "git submodule" refused to run from anywhere
+ other than the top of the working tree of the superproject, but
+ they have been taught to let you run from a subdirectory.
+
+ * "git diff" learned a mode that ignores hunks whose change consists
+ only of additions and removals of blank lines, which is the same as
+ "diff -B" (ignore blank lines) of GNU diff.
+
* "git rm" gives a single message followed by list of paths to report
multiple paths that cannot be removed.
@@ -108,6 +123,22 @@ UI, Workflows & Features
Performance, Internal Implementation, etc.
+ * The original way to specify remote repository using .git/branches/
+ used to have a nifty feature. The code to support the feature was
+ still in a function but the caller was changed not to call it 5
+ years ago, breaking that feature and leaving the supporting code
+ unreachable. The dead code has been removed.
+
+ * "git pack-refs" that races with new ref creation or deletion have
+ been susceptible to lossage of refs under right conditions, which
+ has been tightened up.
+
+ * We read loose and packed rerferences in two steps, but after
+ deciding to read a loose ref but before actually opening it to read
+ it, another process racing with us can unlink it, which would cause
+ us to barf. The codepath has been updated to retry when such a
+ race is detected, instead of outright failing.
+
* Uses of the platform fnmatch(3) function (many places in the code,
matching pathspec, .gitignore and .gitattributes to name a few)
have been replaced with wildmatch, allowing "foo/**/bar" that would
@@ -157,6 +188,14 @@ Unless otherwise noted, all the fixes since v1.8.3 in the maintenance
track are contained in this release (see release notes to them for
details).
+ * The configuration variable column.ui was poorly documented.
+ (merge 5e62cc1 rr/column-doc later to maint).
+
+ * "git name-rev --refs=tags/v*" were forbidden, which was a bit
+ inconvenient (you had to give a pattern to match refs fully, like
+ --refs=refs/tags/v*).
+ (merge 98c5c4a nk/name-rev-abbreviated-refs later to maint).
+
* "git apply" parsed patches that add new files, generated by
programs other than Git, incorrectly. This is an old breakage in
v1.7.11 and will need to be merged down to the maintanance tracks.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 1153585aa2..81856dd5a2 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -934,6 +934,9 @@ column.ui::
This variable consists of a list of tokens separated by spaces
or commas:
+
+These options control when the feature should be enabled
+(defaults to 'never'):
++
--
`always`;;
always show in columns
@@ -941,19 +944,30 @@ column.ui::
never show in columns
`auto`;;
show in columns if the output is to the terminal
+--
++
+These options control layout (defaults to 'column'). Setting any
+of these implies 'always' if none of 'always', 'never', or 'auto' are
+specified.
++
+--
`column`;;
- fill columns before rows (default)
+ fill columns before rows
`row`;;
fill rows before columns
`plain`;;
show in one column
+--
++
+Finally, these options can be combined with a layout option (defaults
+to 'nodense'):
++
+--
`dense`;;
make unequal size columns to utilize more space
`nodense`;;
make equal size columns
--
-+
-This option defaults to 'never'.
column.branch::
Specify whether to output branch listing in `git branch` in columns.
@@ -1830,39 +1844,59 @@ pull.twohead::
The default merge strategy to use when pulling a single branch.
push.default::
- Defines the action `git push` should take if no refspec is given
- on the command line, no refspec is configured in the remote, and
- no refspec is implied by any of the options given on the command
- line. Possible values are:
+ Defines the action `git push` should take if no refspec is
+ explicitly given. Different values are well-suited for
+ specific workflows; for instance, in a purely central workflow
+ (i.e. the fetch source is equal to the push destination),
+ `upstream` is probably what you want. Possible values are:
+
--
-* `nothing` - do not push anything.
-* `matching` - push all branches having the same name in both ends.
- This is for those who prepare all the branches into a publishable
- shape and then push them out with a single command. It is not
- appropriate for pushing into a repository shared by multiple users,
- since locally stalled branches will attempt a non-fast forward push
- if other users updated the branch.
- +
- This is currently the default, but Git 2.0 will change the default
- to `simple`.
-* `upstream` - push the current branch to its upstream branch
- (`tracking` is a deprecated synonym for this).
- With this, `git push` will update the same remote ref as the one which
- is merged by `git pull`, making `push` and `pull` symmetrical.
- See "branch.<name>.merge" for how to configure the upstream branch.
-* `simple` - like `upstream`, but refuses to push if the upstream
- branch's name is different from the local one. This is the safest
- option and is well-suited for beginners. It will become the default
- in Git 2.0.
-* `current` - push the current branch to a branch of the same name.
---
+
+* `nothing` - do not push anything (error out) unless a refspec is
+ explicitly given. This is primarily meant for people who want to
+ avoid mistakes by always being explicit.
+
+* `current` - push the current branch to update a branch with the same
+ name on the receiving end. Works in both central and non-central
+ workflows.
+
+* `upstream` - push the current branch back to the branch whose
+ changes are usually integrated into the current branch (which is
+ called `@{upstream}`). This mode only makes sense if you are
+ pushing to the same repository you would normally pull from
+ (i.e. central workflow).
+
+* `simple` - in centralized workflow, work like `upstream` with an
+ added safety to refuse to push if the upstream branch's name is
+ different from the local one.
++
+When pushing to a remote that is different from the remote you normally
+pull from, work as `current`. This is the safest option and is suited
+for beginners.
++
+This mode will become the default in Git 2.0.
+
+* `matching` - push all branches having the same name on both ends.
+ This makes the repository you are pushing to remember the set of
+ branches that will be pushed out (e.g. if you always push 'maint'
+ and 'master' there and no other branches, the repository you push
+ to will have these two branches, and your local 'maint' and
+ 'master' will be pushed there).
+
-The `simple`, `current` and `upstream` modes are for those who want to
-push out a single branch after finishing work, even when the other
-branches are not yet ready to be pushed out. If you are working with
-other people to push into the same shared repository, you would want
-to use one of these.
+To use this mode effectively, you have to make sure _all_ the
+branches you would push out are ready to be pushed out before
+running 'git push', as the whole point of this mode is to allow you
+to push all of the branches in one go. If you usually finish work
+on only one branch and push out the result, while other branches are
+unfinished, this mode is not for you. Also this mode is not
+suitable for pushing into a shared central repository, as other
+people may add new branches there, or update the tip of existing
+branches outside your control.
++
+This is currently the default, but Git 2.0 will change the default
+to `simple`.
+
+--
rebase.stat::
Whether to show a diffstat of what changed upstream since the last
@@ -2078,6 +2112,14 @@ status.relativePaths::
relative to the repository root (this was the default for Git
prior to v1.5.4).
+status.short::
+ Set to true to enable --short by default in linkgit:git-status[1].
+ The option --no-short takes precedence over this variable.
+
+status.branch::
+ Set to true to enable --branch by default in linkgit:git-status[1].
+ The option --no-branch takes precedence over this variable.
+
status.showUntrackedFiles::
By default, linkgit:git-status[1] and linkgit:git-commit[1] show
files which are not currently tracked by Git. Directories which
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index a85288f23e..19f78a7d5c 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -461,6 +461,9 @@ endif::git-format-patch[]
differences even if one line has whitespace where the other
line has none.
+--ignore-blank-lines::
+ Ignore changes whose lines are all blank.
+
--inter-hunk-context=<lines>::
Show the context between diff hunks, up to the specified number
of lines, thereby fusing hunks that are close to each other.
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 5bbe7b6d10..54d8461d61 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -132,7 +132,7 @@ default. You can use `--no-utf8` to override this.
--resolvemsg=<msg>::
When a patch failure occurs, <msg> will be printed
to the screen before exiting. This overrides the
- standard message informing you to use `--resolved`
+ standard message informing you to use `--continue`
or `--skip` to handle the failure. This is solely
for internal use between 'git rebase' and 'git am'.
@@ -176,7 +176,7 @@ aborts in the middle. You can recover from this in one of two ways:
. hand resolve the conflict in the working directory, and update
the index file to bring it into a state that the patch should
- have produced. Then run the command with the '--resolved' option.
+ have produced. Then run the command with the '--continue' option.
The command refuses to process new mailboxes until the current
operation is finished, so if you decide to start over from scratch,
diff --git a/Documentation/git-check-ignore.txt b/Documentation/git-check-ignore.txt
index 8e1f7ab7ea..d2df487aa2 100644
--- a/Documentation/git-check-ignore.txt
+++ b/Documentation/git-check-ignore.txt
@@ -102,7 +102,7 @@ SEE ALSO
--------
linkgit:gitignore[5]
linkgit:gitconfig[5]
-linkgit:git-ls-files[5]
+linkgit:git-ls-files[1]
GIT
---
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index a0727d7759..450f158779 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -239,8 +239,8 @@ Examples
* Clone from upstream:
+
------------
-$ git clone git://git.kernel.org/pub/scm/.../linux-2.6 my2.6
-$ cd my2.6
+$ git clone git://git.kernel.org/pub/scm/.../linux.git my-linux
+$ cd my-linux
$ make
------------
@@ -257,10 +257,10 @@ $ git show-branch
* Clone from upstream while borrowing from an existing local directory:
+
------------
-$ git clone --reference my2.6 \
- git://git.kernel.org/pub/scm/.../linux-2.7 \
- my2.7
-$ cd my2.7
+$ git clone --reference /git/linux.git \
+ git://git.kernel.org/pub/scm/.../linux.git \
+ my-linux
+$ cd my-linux
------------
@@ -271,13 +271,6 @@ $ git clone --bare -l /home/proj/.git /pub/scm/proj.git
------------
-* Create a repository on the kernel.org machine that borrows from Linus:
-+
-------------
-$ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
- /pub/scm/.../me/subsys-2.6.git
-------------
-
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 19a7be0856..fbad05e46f 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -82,7 +82,7 @@ OPTIONS
--get::
Get the value for a given key (optionally filtered by a regex
matching the value). Returns error code 1 if the key was not
- found and error code 2 if multiple key values were found.
+ found and the last value if multiple key values were found.
--get-all::
Like get, but does not fail if the number of values for the key
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index efb03806f5..85f1f30fdf 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -141,7 +141,7 @@ Limitations
-----------
Since 'git fast-import' cannot tag trees, you will not be
-able to export the linux-2.6.git repository completely, as it contains
+able to export the linux.git repository completely, as it contains
a tag referencing a tree instead of a commit.
GIT
diff --git a/Documentation/git-ls-remote.txt b/Documentation/git-ls-remote.txt
index 774de5e9d9..2e22915eb8 100644
--- a/Documentation/git-ls-remote.txt
+++ b/Documentation/git-ls-remote.txt
@@ -48,9 +48,9 @@ OPTIONS
exit without talking to the remote.
<repository>::
- Location of the repository. The shorthand defined in
- $GIT_DIR/branches/ can be used. Use "." (dot) to list references in
- the local repository.
+ The "remote" repository to query. This parameter can be
+ either a URL or the name of a remote (see the GIT URLS and
+ REMOTES sections of linkgit:git-fetch[1]).
<refs>...::
When unspecified, all references, after filtering done
@@ -70,9 +70,8 @@ EXAMPLES
$ git ls-remote http://www.kernel.org/pub/scm/git/git.git master pu rc
5fe978a5381f1fbad26a80e682ddd2a401966740 refs/heads/master
c781a84b5204fb294c9ccc79f8b3baceeb32c061 refs/heads/pu
- b1d096f2926c4e37c9c0b6a7bf2119bedaa277cb refs/heads/rc
- $ echo http://www.kernel.org/pub/scm/git/git.git >.git/branches/public
- $ git ls-remote --tags public v\*
+ $ git remote add korg http://www.kernel.org/pub/scm/git/git.git
+ $ git ls-remote --tags korg v\*
d6602ec5194c87b0fc87103ca4d67251c76f233a refs/tags/v0.99
f25a265a342aed6041ab0cc484224d9ca54b6f41 refs/tags/v0.99.1
c5db5456ae3b0873fc659c19fafdde22313cc441 refs/tags/v0.99.2
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index ad1d1468c9..6b0f1ba75f 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -25,7 +25,8 @@ OPTIONS
Do not use branch names, but only tags to name the commits
--refs=<pattern>::
- Only use refs whose names match a given shell pattern.
+ Only use refs whose names match a given shell pattern. The pattern
+ can be one of branch name, tag name or fully qualified ref name.
--all::
List all commits reachable from all refs
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index c84854ae87..6b2e1c86ab 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -389,7 +389,9 @@ squash/fixup series.
the same ..., automatically modify the todo list of rebase -i
so that the commit marked for squashing comes right after the
commit to be modified, and change the action of the moved
- commit from `pick` to `squash` (or `fixup`).
+ commit from `pick` to `squash` (or `fixup`). Ignores subsequent
+ "fixup! " or "squash! " after the first, in case you referred to an
+ earlier fixup/squash with `git commit --fixup/--squash`.
+
This option is only valid when the '--interactive' option is used.
+
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 581bb4c413..9c3e3bf83a 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -187,18 +187,25 @@ Examples
$ git remote
origin
$ git branch -r
-origin/master
-$ git remote add linux-nfs git://linux-nfs.org/pub/linux/nfs-2.6.git
+ origin/HEAD -> origin/master
+ origin/master
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
$ git remote
-linux-nfs
origin
-$ git fetch
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
- commit: bf81b46
+staging
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch] master -> staging/master
+ * [new branch] staging-linus -> staging/staging-linus
+ * [new branch] staging-next -> staging/staging-next
$ git branch -r
-origin/master
-linux-nfs/master
-$ git checkout -b nfs linux-nfs/master
+ origin/HEAD -> origin/master
+ origin/master
+ staging/master
+ staging/staging-linus
+ staging/staging-next
+$ git checkout -b staging staging/master
...
------------
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 947d62fd25..993903c9f1 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -59,6 +59,22 @@ OPTIONS
If there is no parameter given by the user, use `<arg>`
instead.
+--prefix <arg>::
+ Behave as if 'git rev-parse' was invoked from the `<arg>`
+ subdirectory of the working tree. Any relative filenames are
+ resolved as if they are prefixed by `<arg>` and will be printed
+ in that form.
++
+This can be used to convert arguments to a command run in a subdirectory
+so that they can still be used after moving to the top-level of the
+repository. For example:
++
+----
+prefix=$(git rev-parse --show-prefix)
+cd "$(git rev-parse --show-toplevel)"
+eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
+----
+
--verify::
Verify that exactly one parameter is provided, and that it
can be turned into a raw 20-byte SHA-1 that can be used to
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 894454609f..b738a40e6b 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.8.3.1/git.html[documentation for release 1.8.3.1]
+* link:v1.8.3.2/git.html[documentation for release 1.8.3.2]
* release notes for
+ link:RelNotes/1.8.3.2.txt[1.8.3.2],
link:RelNotes/1.8.3.1.txt[1.8.3.1],
link:RelNotes/1.8.3.txt[1.8.3].
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index b462f17f62..e157ec3fe7 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -625,6 +625,10 @@ By default, the commits are shown in reverse chronological order.
Show no parents before all of its children are shown, but
otherwise show commits in the commit timestamp order.
+--author-date-order::
+ Show no parents before all of its children are shown, but
+ otherwise show commits in the author timestamp order.
+
--topo-order::
Show no parents before all of its children are shown, and
avoid showing commits on multiple lines of history
diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index 6dc82ca5a8..f716d6d97f 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -135,9 +135,9 @@ them, and give the same timestamp to the index file:
$ git ls-files | git update-index --stdin
$ touch -r .datestamp .git/index
-This will make all index entries racily clean. The linux-2.6
-project, for example, there are over 20,000 files in the working
-tree. On my Athlon 64 X2 3800+, after the above:
+This will make all index entries racily clean. The linux project, for
+example, there are over 20,000 files in the working tree. On my
+Athlon 64 X2 3800+, after the above:
$ /usr/bin/time git diff-files
1.68user 0.54system 0:02.22elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 5e185f87c2..9ccb24677e 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -11,6 +11,9 @@ and ftps can be used for fetching and rsync can be used for fetching
and pushing, but these are inefficient and deprecated; do not use
them).
+The native transport (i.e. git:// URL) does no authentication and
+should be used with caution on unsecured networks.
+
The following syntaxes may be used with them:
- ssh://{startsb}user@{endsb}host.xz{startsb}:port{endsb}/path/to/repo.git/
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 644acce079..e364007d7c 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -66,8 +66,8 @@ $ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
The initial clone may be time-consuming for a large project, but you
will only need to clone once.
-The clone command creates a new directory named after the project (`git`
-or `linux-2.6` in the examples above). After you cd into this
+The clone command creates a new directory named after the project
+(`git` or `linux` in the examples above). After you cd into this
directory, you will see that it contains a copy of the project files,
called the <<def_working_tree,working tree>>, together with a special
top-level directory named `.git`, which contains all the information
@@ -431,19 +431,25 @@ You can also track branches from repositories other than the one you
cloned from, using linkgit:git-remote[1]:
-------------------------------------------------
-$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
-$ git fetch linux-nfs
-* refs/remotes/linux-nfs/master: storing branch 'master' ...
- commit: bf81b46
+$ git remote add staging git://git.kernel.org/.../gregkh/staging.git
+$ git fetch staging
+...
+From git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
+ * [new branch] master -> staging/master
+ * [new branch] staging-linus -> staging/staging-linus
+ * [new branch] staging-next -> staging/staging-next
-------------------------------------------------
New remote-tracking branches will be stored under the shorthand name
-that you gave `git remote add`, in this case `linux-nfs`:
+that you gave `git remote add`, in this case `staging`:
-------------------------------------------------
$ git branch -r
-linux-nfs/master
-origin/master
+ origin/HEAD -> origin/master
+ origin/master
+ staging/master
+ staging/staging-linus
+ staging/staging-next
-------------------------------------------------
If you run `git fetch <remote>` later, the remote-tracking branches
@@ -455,9 +461,9 @@ a new stanza:
-------------------------------------------------
$ cat .git/config
...
-[remote "linux-nfs"]
- url = git://linux-nfs.org/pub/nfs-2.6.git
- fetch = +refs/heads/*:refs/remotes/linux-nfs/*
+[remote "staging"]
+ url = git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git
+ fetch = +refs/heads/*:refs/remotes/staging/*
...
-------------------------------------------------
@@ -1835,7 +1841,7 @@ Once the index is updated with the results of the conflict
resolution, instead of creating a new commit, just run
-------------------------------------------------
-$ git am --resolved
+$ git am --continue
-------------------------------------------------
and Git will create the commit for you and continue applying the
@@ -2156,7 +2162,7 @@ To set this up, first create your work tree by cloning Linus's public
tree:
-------------------------------------------------
-$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git work
+$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git work
$ cd work
-------------------------------------------------
@@ -2198,7 +2204,7 @@ make it easy to push both branches to your public tree. (See
-------------------------------------------------
$ cat >> .git/config <<EOF
[remote "mytree"]
- url = master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
+ url = master.kernel.org:/pub/scm/linux/kernel/git/aegl/linux.git
push = release
push = test
EOF
diff --git a/Makefile b/Makefile
index e1583761df..0600eb46db 100644
--- a/Makefile
+++ b/Makefile
@@ -569,6 +569,7 @@ TEST_PROGRAMS_NEED_X += test-mergesort
TEST_PROGRAMS_NEED_X += test-mktemp
TEST_PROGRAMS_NEED_X += test-parse-options
TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-prio-queue
TEST_PROGRAMS_NEED_X += test-read-cache
TEST_PROGRAMS_NEED_X += test-regex
TEST_PROGRAMS_NEED_X += test-revision-walking
@@ -705,6 +706,7 @@ LIB_H += parse-options.h
LIB_H += patch-ids.h
LIB_H += pathspec.h
LIB_H += pkt-line.h
+LIB_H += prio-queue.h
LIB_H += progress.h
LIB_H += prompt.h
LIB_H += quote.h
@@ -846,6 +848,7 @@ LIB_OBJS += pathspec.o
LIB_OBJS += pkt-line.o
LIB_OBJS += preload-index.o
LIB_OBJS += pretty.o
+LIB_OBJS += prio-queue.o
LIB_OBJS += progress.o
LIB_OBJS += prompt.o
LIB_OBJS += quote.o
@@ -2302,9 +2305,6 @@ check: common-cmds.h
exit 1; \
fi
-remove-dashes:
- ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
-
### Installation rules
ifneq ($(filter /%,$(firstword $(template_dir))),)
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3be0018821..7fe0bffaf8 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -587,7 +587,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
struct branch_info *new)
{
struct strbuf msg = STRBUF_INIT;
- const char *old_desc;
+ const char *old_desc, *reflog_msg;
if (opts->new_branch) {
if (opts->new_orphan_branch) {
if (opts->new_branch_log && !log_all_ref_updates) {
@@ -620,8 +620,13 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
old_desc = old->name;
if (!old_desc && old->commit)
old_desc = sha1_to_hex(old->commit->object.sha1);
- strbuf_addf(&msg, "checkout: moving from %s to %s",
- old_desc ? old_desc : "(invalid)", new->name);
+
+ reflog_msg = getenv("GIT_REFLOG_ACTION");
+ if (!reflog_msg)
+ strbuf_addf(&msg, "checkout: moving from %s to %s",
+ old_desc ? old_desc : "(invalid)", new->name);
+ else
+ strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
/* Nothing to do. */
diff --git a/builtin/clone.c b/builtin/clone.c
index 66bff5700f..14b1323568 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -493,13 +493,16 @@ static void write_remote_refs(const struct ref *local_refs)
{
const struct ref *r;
+ lock_packed_refs(LOCK_DIE_ON_ERROR);
+
for (r = local_refs; r; r = r->next) {
if (!r->peer_ref)
continue;
add_packed_ref(r->peer_ref->name, r->old_sha1);
}
- pack_refs(PACK_REFS_ALL);
+ if (commit_packed_refs())
+ die_errno("unable to overwrite old ref-pack file");
}
static void write_followtags(const struct ref *refs, const char *msg)
diff --git a/builtin/commit.c b/builtin/commit.c
index 6b693c16d8..790e5ab5cf 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -112,12 +112,14 @@ static int show_ignored_in_status, have_option_m;
static const char *only_include_assumed;
static struct strbuf message = STRBUF_INIT;
-static enum {
+static enum status_format {
STATUS_FORMAT_NONE = 0,
STATUS_FORMAT_LONG,
STATUS_FORMAT_SHORT,
- STATUS_FORMAT_PORCELAIN
-} status_format;
+ STATUS_FORMAT_PORCELAIN,
+
+ STATUS_FORMAT_UNSPECIFIED
+} status_format = STATUS_FORMAT_UNSPECIFIED;
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
{
@@ -460,6 +462,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(s);
break;
+ case STATUS_FORMAT_UNSPECIFIED:
+ die("BUG: finalize_deferred_config() should have been called");
+ break;
case STATUS_FORMAT_NONE:
case STATUS_FORMAT_LONG:
wt_status_print(s);
@@ -961,6 +966,42 @@ static const char *read_commit_message(const char *name)
return logmsg_reencode(commit, NULL, out_enc);
}
+/*
+ * Enumerate what needs to be propagated when --porcelain
+ * is not in effect here.
+ */
+static struct status_deferred_config {
+ enum status_format status_format;
+ int show_branch;
+} status_deferred_config = {
+ STATUS_FORMAT_UNSPECIFIED,
+ -1 /* unspecified */
+};
+
+static void finalize_deferred_config(struct wt_status *s)
+{
+ int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
+ !s->null_termination);
+
+ if (s->null_termination) {
+ if (status_format == STATUS_FORMAT_NONE ||
+ status_format == STATUS_FORMAT_UNSPECIFIED)
+ status_format = STATUS_FORMAT_PORCELAIN;
+ else if (status_format == STATUS_FORMAT_LONG)
+ die(_("--long and -z are incompatible"));
+ }
+
+ if (use_deferred_config && status_format == STATUS_FORMAT_UNSPECIFIED)
+ status_format = status_deferred_config.status_format;
+ if (status_format == STATUS_FORMAT_UNSPECIFIED)
+ status_format = STATUS_FORMAT_NONE;
+
+ if (use_deferred_config && s->show_branch < 0)
+ s->show_branch = status_deferred_config.show_branch;
+ if (s->show_branch < 0)
+ s->show_branch = 0;
+}
+
static int parse_and_validate_options(int argc, const char *argv[],
const struct option *options,
const char * const usage[],
@@ -971,6 +1012,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
int f = 0;
argc = parse_options(argc, argv, prefix, options, usage, 0);
+ finalize_deferred_config(s);
if (force_author && !strchr(force_author, '>'))
force_author = find_author_by_nickname(force_author);
@@ -1055,12 +1097,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
if (all && argc > 0)
die(_("Paths with -a does not make sense."));
- if (s->null_termination) {
- if (status_format == STATUS_FORMAT_NONE)
- status_format = STATUS_FORMAT_PORCELAIN;
- else if (status_format == STATUS_FORMAT_LONG)
- die(_("--long and -z are incompatible"));
- }
if (status_format != STATUS_FORMAT_NONE)
dry_run = 1;
@@ -1113,6 +1149,17 @@ static int git_status_config(const char *k, const char *v, void *cb)
s->submodule_summary = -1;
return 0;
}
+ if (!strcmp(k, "status.short")) {
+ if (git_config_bool(k, v))
+ status_deferred_config.status_format = STATUS_FORMAT_SHORT;
+ else
+ status_deferred_config.status_format = STATUS_FORMAT_NONE;
+ return 0;
+ }
+ if (!strcmp(k, "status.branch")) {
+ status_deferred_config.show_branch = git_config_bool(k, v);
+ return 0;
+ }
if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
s->use_color = git_config_colorbool(k, v);
return 0;
@@ -1155,8 +1202,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
OPT__VERBOSE(&verbose, N_("be verbose")),
OPT_SET_INT('s', "short", &status_format,
N_("show status concisely"), STATUS_FORMAT_SHORT),
- OPT_BOOLEAN('b', "branch", &s.show_branch,
- N_("show branch information")),
+ OPT_BOOL('b', "branch", &s.show_branch,
+ N_("show branch information")),
OPT_SET_INT(0, "porcelain", &status_format,
N_("machine-readable output"),
STATUS_FORMAT_PORCELAIN),
@@ -1189,13 +1236,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
builtin_status_options,
builtin_status_usage, 0);
finalize_colopts(&s.colopts, -1);
-
- if (s.null_termination) {
- if (status_format == STATUS_FORMAT_NONE)
- status_format = STATUS_FORMAT_PORCELAIN;
- else if (status_format == STATUS_FORMAT_LONG)
- die(_("--long and -z are incompatible"));
- }
+ finalize_deferred_config(&s);
handle_untracked_files_arg(&s);
if (show_ignored_in_status)
@@ -1224,6 +1265,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
case STATUS_FORMAT_PORCELAIN:
wt_porcelain_print(&s);
break;
+ case STATUS_FORMAT_UNSPECIFIED:
+ die("BUG: finalize_deferred_config() should have been called");
+ break;
case STATUS_FORMAT_NONE:
case STATUS_FORMAT_LONG:
s.verbose = verbose;
@@ -1392,7 +1436,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "dry-run", &dry_run, N_("show what would be committed")),
OPT_SET_INT(0, "short", &status_format, N_("show status concisely"),
STATUS_FORMAT_SHORT),
- OPT_BOOLEAN(0, "branch", &s.show_branch, N_("show branch information")),
+ OPT_BOOL(0, "branch", &s.show_branch, N_("show branch information")),
OPT_SET_INT(0, "porcelain", &status_format,
N_("machine-readable output"), STATUS_FORMAT_PORCELAIN),
OPT_SET_INT(0, "long", &status_format,
@@ -1433,6 +1477,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
wt_status_prepare(&s);
gitmodules_config();
git_config(git_commit_config, &s);
+ status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
determine_whence(&s);
s.colopts = 0;
diff --git a/builtin/log.c b/builtin/log.c
index 9e2123295f..e3222ed9f9 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -237,7 +237,7 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
int i = revs->early_output;
int show_header = 1;
- sort_in_topological_order(&list, revs->lifo);
+ sort_in_topological_order(&list, revs->sort_order);
while (list && i) {
struct commit *commit = list->item;
switch (simplify_commit(revs, commit)) {
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 87f3b331ca..3a410c35d9 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -165,11 +165,13 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
}
write_name(ce->name, ce_namelen(ce));
if (debug_mode) {
- printf(" ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
- printf(" mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
- printf(" dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
- printf(" uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
- printf(" size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
+ struct stat_data *sd = &ce->ce_stat_data;
+
+ printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
+ printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", sd->sd_dev, sd->sd_ino);
+ printf(" uid: %d\tgid: %d\n", sd->sd_uid, sd->sd_gid);
+ printf(" size: %d\tflags: %x\n", sd->sd_size, ce->ce_flags);
}
}
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 6238247974..87d485496f 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -82,6 +82,20 @@ copy_data:
}
}
+static int subpath_matches(const char *path, const char *filter)
+{
+ const char *subpath = path;
+
+ while (subpath) {
+ if (!fnmatch(filter, subpath, 0))
+ return subpath - path;
+ subpath = strchr(subpath, '/');
+ if (subpath)
+ subpath++;
+ }
+ return -1;
+}
+
struct name_ref_data {
int tags_only;
int name_only;
@@ -92,13 +106,23 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
{
struct object *o = parse_object(sha1);
struct name_ref_data *data = cb_data;
+ int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
if (data->tags_only && prefixcmp(path, "refs/tags/"))
return 0;
- if (data->ref_filter && fnmatch(data->ref_filter, path, 0))
- return 0;
+ if (data->ref_filter) {
+ switch (subpath_matches(path, data->ref_filter)) {
+ case -1: /* did not match */
+ return 0;
+ case 0: /* matched fully */
+ break;
+ default: /* matched subpath */
+ can_abbreviate_output = 1;
+ break;
+ }
+ }
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
@@ -110,12 +134,10 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
- if (!prefixcmp(path, "refs/heads/"))
+ if (can_abbreviate_output)
+ path = shorten_unambiguous_ref(path, 0);
+ else if (!prefixcmp(path, "refs/heads/"))
path = path + 11;
- else if (data->tags_only
- && data->name_only
- && !prefixcmp(path, "refs/tags/"))
- path = path + 10;
else if (!prefixcmp(path, "refs/"))
path = path + 5;
diff --git a/builtin/push.c b/builtin/push.c
index 2d84d10720..a2580a94f5 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -120,10 +120,11 @@ static const char message_detached_head_die[] =
"\n"
" git push %s HEAD:<name-of-remote-branch>\n");
-static void setup_push_upstream(struct remote *remote, int simple)
+static void setup_push_upstream(struct remote *remote, struct branch *branch,
+ int triangular)
{
struct strbuf refspec = STRBUF_INIT;
- struct branch *branch = branch_get(NULL);
+
if (!branch)
die(_(message_detached_head_die), remote->name);
if (!branch->merge_nr || !branch->merge || !branch->remote_name)
@@ -137,18 +138,29 @@ static void setup_push_upstream(struct remote *remote, int simple)
if (branch->merge_nr != 1)
die(_("The current branch %s has multiple upstream branches, "
"refusing to push."), branch->name);
- if (strcmp(branch->remote_name, remote->name))
+ if (triangular)
die(_("You are pushing to remote '%s', which is not the upstream of\n"
"your current branch '%s', without telling me what to push\n"
"to update which remote branch."),
remote->name, branch->name);
- if (simple && strcmp(branch->refname, branch->merge[0]->src))
- die_push_simple(branch, remote);
+
+ if (push_default == PUSH_DEFAULT_SIMPLE) {
+ /* Additional safety */
+ if (strcmp(branch->refname, branch->merge[0]->src))
+ die_push_simple(branch, remote);
+ }
strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
add_refspec(refspec.buf);
}
+static void setup_push_current(struct remote *remote, struct branch *branch)
+{
+ if (!branch)
+ die(_(message_detached_head_die), remote->name);
+ add_refspec(branch->name);
+}
+
static char warn_unspecified_push_default_msg[] =
N_("push.default is unset; its implicit value is changing in\n"
"Git 2.0 from 'matching' to 'simple'. To squelch this message\n"
@@ -173,9 +185,16 @@ static void warn_unspecified_push_default_configuration(void)
warning("%s\n", _(warn_unspecified_push_default_msg));
}
+static int is_workflow_triangular(struct remote *remote)
+{
+ struct remote *fetch_remote = remote_get(NULL);
+ return (fetch_remote && fetch_remote != remote);
+}
+
static void setup_default_push_refspecs(struct remote *remote)
{
- struct branch *branch;
+ struct branch *branch = branch_get(NULL);
+ int triangular = is_workflow_triangular(remote);
switch (push_default) {
default:
@@ -188,18 +207,18 @@ static void setup_default_push_refspecs(struct remote *remote)
break;
case PUSH_DEFAULT_SIMPLE:
- setup_push_upstream(remote, 1);
+ if (triangular)
+ setup_push_current(remote, branch);
+ else
+ setup_push_upstream(remote, branch, triangular);
break;
case PUSH_DEFAULT_UPSTREAM:
- setup_push_upstream(remote, 0);
+ setup_push_upstream(remote, branch, triangular);
break;
case PUSH_DEFAULT_CURRENT:
- branch = branch_get(NULL);
- if (!branch)
- die(_(message_detached_head_die), remote->name);
- add_refspec(branch->name);
+ setup_push_current(remote, branch);
break;
case PUSH_DEFAULT_NOTHING:
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index f267a1d3b5..de894c7577 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -212,11 +212,17 @@ static void show_datestring(const char *flag, const char *datestr)
show(buffer);
}
-static int show_file(const char *arg)
+static int show_file(const char *arg, int output_prefix)
{
show_default();
if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
- show(arg);
+ if (output_prefix) {
+ const char *prefix = startup_info->prefix;
+ show(prefix_filename(prefix,
+ prefix ? strlen(prefix) : 0,
+ arg));
+ } else
+ show(arg);
return 1;
}
return 0;
@@ -470,6 +476,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
{
int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
+ int output_prefix = 0;
unsigned char sha1[20];
const char *name = NULL;
@@ -503,7 +510,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
const char *arg = argv[i];
if (as_is) {
- if (show_file(arg) && as_is < 2)
+ if (show_file(arg, output_prefix) && as_is < 2)
verify_filename(prefix, arg, 0);
continue;
}
@@ -527,7 +534,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
as_is = 2;
/* Pass on the "--" if we show anything but files.. */
if (filter & (DO_FLAGS | DO_REVS))
- show_file(arg);
+ show_file(arg, 0);
continue;
}
if (!strcmp(arg, "--default")) {
@@ -535,6 +542,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
i++;
continue;
}
+ if (!strcmp(arg, "--prefix")) {
+ prefix = argv[i+1];
+ startup_info->prefix = prefix;
+ output_prefix = 1;
+ i++;
+ continue;
+ }
if (!strcmp(arg, "--revs-only")) {
filter &= ~DO_NOREV;
continue;
@@ -754,7 +768,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
if (verify)
die_no_single_rev(quiet);
as_is = 1;
- if (!show_file(arg))
+ if (!show_file(arg, output_prefix))
continue;
verify_filename(prefix, arg, 1);
}
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 90fc6b1b9d..99ec4af224 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
int num_rev, i, extra = 0;
int all_heads = 0, all_remotes = 0;
int all_mask, all_revs;
- int lifo = 1;
+ enum rev_sort_order sort_order = REV_SORT_IN_GRAPH_ORDER;
char head[128];
const char *head_p;
int head_len;
@@ -665,15 +665,17 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
N_("show possible merge bases")),
OPT_BOOLEAN(0, "independent", &independent,
N_("show refs unreachable from any other ref")),
- OPT_BOOLEAN(0, "topo-order", &lifo,
- N_("show commits in topological order")),
+ OPT_SET_INT(0, "topo-order", &sort_order,
+ N_("show commits in topological order"),
+ REV_SORT_IN_GRAPH_ORDER),
OPT_BOOLEAN(0, "topics", &topics,
N_("show only commits not on the first branch")),
OPT_SET_INT(0, "sparse", &dense,
N_("show merges reachable from only one tip"), 0),
- OPT_SET_INT(0, "date-order", &lifo,
+ OPT_SET_INT(0, "date-order", &sort_order,
N_("show commits where no parent comes before its "
- "children"), 0),
+ "children"),
+ REV_SORT_BY_COMMIT_DATE),
{ OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
N_("show <n> most recent ref-log entries starting at "
"base"),
@@ -900,7 +902,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
exit(0);
/* Sort topologically */
- sort_in_topological_order(&seen, lifo);
+ sort_in_topological_order(&seen, sort_order);
/* Give names to commits */
if (!sha1_name && !no_name)
diff --git a/cache.h b/cache.h
index ec8240f62a..dd0fb33a15 100644
--- a/cache.h
+++ b/cache.h
@@ -119,15 +119,19 @@ struct cache_time {
unsigned int nsec;
};
+struct stat_data {
+ struct cache_time sd_ctime;
+ struct cache_time sd_mtime;
+ unsigned int sd_dev;
+ unsigned int sd_ino;
+ unsigned int sd_uid;
+ unsigned int sd_gid;
+ unsigned int sd_size;
+};
+
struct cache_entry {
- struct cache_time ce_ctime;
- struct cache_time ce_mtime;
- unsigned int ce_dev;
- unsigned int ce_ino;
+ struct stat_data ce_stat_data;
unsigned int ce_mode;
- unsigned int ce_uid;
- unsigned int ce_gid;
- unsigned int ce_size;
unsigned int ce_flags;
unsigned int ce_namelen;
unsigned char sha1[20];
@@ -511,6 +515,21 @@ extern int limit_pathspec_to_literal(void);
#define HASH_FORMAT_CHECK 2
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
+
+/*
+ * Record to sd the data from st that we use to check whether a file
+ * might have changed.
+ */
+extern void fill_stat_data(struct stat_data *sd, struct stat *st);
+
+/*
+ * Return 0 if st is consistent with a file not having been changed
+ * since sd was filled. If there are differences, return a
+ * combination of MTIME_CHANGED, CTIME_CHANGED, OWNER_CHANGED,
+ * INODE_CHANGED, and DATA_CHANGED.
+ */
+extern int match_stat_data(const struct stat_data *sd, struct stat *st);
+
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_REALLY 0x0001 /* ignore_valid */
@@ -1338,4 +1357,31 @@ int checkout_fast_forward(const unsigned char *from,
int sane_execvp(const char *file, char *const argv[]);
+/*
+ * A struct to encapsulate the concept of whether a file has changed
+ * since we last checked it. This uses criteria similar to those used
+ * for the index.
+ */
+struct stat_validity {
+ struct stat_data *sd;
+};
+
+void stat_validity_clear(struct stat_validity *sv);
+
+/*
+ * Returns 1 if the path is a regular file (or a symlink to a regular
+ * file) and matches the saved stat_validity, 0 otherwise. A missing
+ * or inaccessible file is considered a match if the struct was just
+ * initialized, or if the previous update found an inaccessible file.
+ */
+int stat_validity_check(struct stat_validity *sv, const char *path);
+
+/*
+ * Update the stat_validity from a file opened at descriptor fd. If
+ * the file is missing, inaccessible, or not a regular file, then
+ * future calls to stat_validity_check will match iff one of those
+ * conditions continues to be true.
+ */
+void stat_validity_update(struct stat_validity *sv, int fd);
+
#endif /* CACHE_H */
diff --git a/commit-slab.h b/commit-slab.h
new file mode 100644
index 0000000000..7d481638af
--- /dev/null
+++ b/commit-slab.h
@@ -0,0 +1,98 @@
+#ifndef COMMIT_SLAB_H
+#define COMMIT_SLAB_H
+
+/*
+ * define_commit_slab(slabname, elemtype) creates boilerplate code to define
+ * a new struct (struct slabname) that is used to associate a piece of data
+ * of elemtype to commits, and a few functions to use that struct.
+ *
+ * After including this header file, using:
+ *
+ * define_commit_slab(indegee, int);
+ *
+ * will let you call the following functions:
+ *
+ * - int *indegree_at(struct indegree *, struct commit *);
+ *
+ * This function locates the data associated with the given commit in
+ * the indegree slab, and returns the pointer to it.
+ *
+ * - void init_indegree(struct indegree *);
+ * void init_indegree_with_stride(struct indegree *, int);
+ *
+ * Initializes the indegree slab that associates an array of integers
+ * to each commit. 'stride' specifies how big each array is. The slab
+ * that id initialied by the variant without "_with_stride" associates
+ * each commit with an array of one integer.
+ */
+
+/* allocate ~512kB at once, allowing for malloc overhead */
+#ifndef COMMIT_SLAB_SIZE
+#define COMMIT_SLAB_SIZE (512*1024-32)
+#endif
+
+#define define_commit_slab(slabname, elemtype) \
+ \
+struct slabname { \
+ unsigned slab_size; \
+ unsigned stride; \
+ unsigned slab_count; \
+ elemtype **slab; \
+}; \
+static int stat_ ##slabname## realloc; \
+ \
+static void init_ ##slabname## _with_stride(struct slabname *s, \
+ unsigned stride) \
+{ \
+ unsigned int elem_size; \
+ if (!stride) \
+ stride = 1; \
+ s->stride = stride; \
+ elem_size = sizeof(struct slabname) * stride; \
+ s->slab_size = COMMIT_SLAB_SIZE / elem_size; \
+ s->slab_count = 0; \
+ s->slab = NULL; \
+} \
+ \
+static void init_ ##slabname(struct slabname *s) \
+{ \
+ init_ ##slabname## _with_stride(s, 1); \
+} \
+ \
+static void clear_ ##slabname(struct slabname *s) \
+{ \
+ int i; \
+ for (i = 0; i < s->slab_count; i++) \
+ free(s->slab[i]); \
+ s->slab_count = 0; \
+ free(s->slab); \
+ s->slab = NULL; \
+} \
+ \
+static elemtype *slabname## _at(struct slabname *s, \
+ const struct commit *c) \
+{ \
+ int nth_slab, nth_slot, ix; \
+ \
+ ix = c->index * s->stride; \
+ nth_slab = ix / s->slab_size; \
+ nth_slot = ix % s->slab_size; \
+ \
+ if (s->slab_count <= nth_slab) { \
+ int i; \
+ s->slab = xrealloc(s->slab, \
+ (nth_slab + 1) * sizeof(s->slab)); \
+ stat_ ##slabname## realloc++; \
+ for (i = s->slab_count; i <= nth_slab; i++) \
+ s->slab[i] = NULL; \
+ s->slab_count = nth_slab + 1; \
+ } \
+ if (!s->slab[nth_slab]) \
+ s->slab[nth_slab] = xcalloc(s->slab_size, \
+ sizeof(**s->slab)); \
+ return &s->slab[nth_slab][nth_slot]; \
+} \
+ \
+static int stat_ ##slabname## realloc
+
+#endif /* COMMIT_SLAB_H */
diff --git a/commit.c b/commit.c
index 888e02ae2f..521e49c309 100644
--- a/commit.c
+++ b/commit.c
@@ -8,12 +8,15 @@
#include "notes.h"
#include "gpg-interface.h"
#include "mergesort.h"
+#include "commit-slab.h"
+#include "prio-queue.h"
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
int save_commit_buffer = 1;
const char *commit_type = "commit";
+static int commit_count;
static struct commit *check_commit(struct object *obj,
const unsigned char *sha1,
@@ -58,8 +61,11 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n
struct commit *lookup_commit(const unsigned char *sha1)
{
struct object *obj = lookup_object(sha1);
- if (!obj)
- return create_object(sha1, OBJ_COMMIT, alloc_commit_node());
+ if (!obj) {
+ struct commit *c = alloc_commit_node();
+ c->index = commit_count++;
+ return create_object(sha1, OBJ_COMMIT, c);
+ }
if (!obj->type)
obj->type = OBJ_COMMIT;
return check_commit(obj, sha1, 0);
@@ -507,32 +513,136 @@ struct commit *pop_commit(struct commit_list **stack)
}
/*
+ * Topological sort support
+ */
+
+/* count number of children that have not been emitted */
+define_commit_slab(indegree_slab, int);
+
+/* record author-date for each commit object */
+define_commit_slab(author_date_slab, unsigned long);
+
+static void record_author_date(struct author_date_slab *author_date,
+ struct commit *commit)
+{
+ const char *buf, *line_end;
+ char *buffer = NULL;
+ struct ident_split ident;
+ char *date_end;
+ unsigned long date;
+
+ if (!commit->buffer) {
+ unsigned long size;
+ enum object_type type;
+ buffer = read_sha1_file(commit->object.sha1, &type, &size);
+ if (!buffer)
+ return;
+ }
+
+ for (buf = commit->buffer ? commit->buffer : buffer;
+ buf;
+ buf = line_end + 1) {
+ line_end = strchrnul(buf, '\n');
+ if (prefixcmp(buf, "author ")) {
+ if (!line_end[0] || line_end[1] == '\n')
+ return; /* end of header */
+ continue;
+ }
+ if (split_ident_line(&ident,
+ buf + strlen("author "),
+ line_end - (buf + strlen("author "))) ||
+ !ident.date_begin || !ident.date_end)
+ goto fail_exit; /* malformed "author" line */
+ break;
+ }
+
+ date = strtoul(ident.date_begin, &date_end, 10);
+ if (date_end != ident.date_end)
+ goto fail_exit; /* malformed date */
+ *(author_date_slab_at(author_date, commit)) = date;
+
+fail_exit:
+ free(buffer);
+}
+
+static int compare_commits_by_author_date(const void *a_, const void *b_,
+ void *cb_data)
+{
+ const struct commit *a = a_, *b = b_;
+ struct author_date_slab *author_date = cb_data;
+ unsigned long a_date = *(author_date_slab_at(author_date, a));
+ unsigned long b_date = *(author_date_slab_at(author_date, b));
+
+ /* newer commits with larger date first */
+ if (a_date < b_date)
+ return 1;
+ else if (a_date > b_date)
+ return -1;
+ return 0;
+}
+
+static int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+{
+ const struct commit *a = a_, *b = b_;
+ /* newer commits with larger date first */
+ if (a->date < b->date)
+ return 1;
+ else if (a->date > b->date)
+ return -1;
+ return 0;
+}
+
+/*
* Performs an in-place topological sort on the list supplied.
*/
-void sort_in_topological_order(struct commit_list ** list, int lifo)
+void sort_in_topological_order(struct commit_list **list, enum rev_sort_order sort_order)
{
struct commit_list *next, *orig = *list;
- struct commit_list *work, **insert;
struct commit_list **pptr;
+ struct indegree_slab indegree;
+ struct prio_queue queue;
+ struct commit *commit;
+ struct author_date_slab author_date;
if (!orig)
return;
*list = NULL;
+ init_indegree_slab(&indegree);
+ memset(&queue, '\0', sizeof(queue));
+
+ switch (sort_order) {
+ default: /* REV_SORT_IN_GRAPH_ORDER */
+ queue.compare = NULL;
+ break;
+ case REV_SORT_BY_COMMIT_DATE:
+ queue.compare = compare_commits_by_commit_date;
+ break;
+ case REV_SORT_BY_AUTHOR_DATE:
+ init_author_date_slab(&author_date);
+ queue.compare = compare_commits_by_author_date;
+ queue.cb_data = &author_date;
+ break;
+ }
+
/* Mark them and clear the indegree */
for (next = orig; next; next = next->next) {
struct commit *commit = next->item;
- commit->indegree = 1;
+ *(indegree_slab_at(&indegree, commit)) = 1;
+ /* also record the author dates, if needed */
+ if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+ record_author_date(&author_date, commit);
}
/* update the indegree */
for (next = orig; next; next = next->next) {
- struct commit_list * parents = next->item->parents;
+ struct commit_list *parents = next->item->parents;
while (parents) {
struct commit *parent = parents->item;
+ int *pi = indegree_slab_at(&indegree, parent);
- if (parent->indegree)
- parent->indegree++;
+ if (*pi)
+ (*pi)++;
parents = parents->next;
}
}
@@ -544,34 +654,33 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
*
* the tips serve as a starting set for the work queue.
*/
- work = NULL;
- insert = &work;
for (next = orig; next; next = next->next) {
struct commit *commit = next->item;
- if (commit->indegree == 1)
- insert = &commit_list_insert(commit, insert)->next;
+ if (*(indegree_slab_at(&indegree, commit)) == 1)
+ prio_queue_put(&queue, commit);
}
- /* process the list in topological order */
- if (!lifo)
- commit_list_sort_by_date(&work);
+ /*
+ * This is unfortunate; the initial tips need to be shown
+ * in the order given from the revision traversal machinery.
+ */
+ if (sort_order == REV_SORT_IN_GRAPH_ORDER)
+ prio_queue_reverse(&queue);
+
+ /* We no longer need the commit list */
+ free_commit_list(orig);
pptr = list;
*list = NULL;
- while (work) {
- struct commit *commit;
- struct commit_list *parents, *work_item;
-
- work_item = work;
- work = work_item->next;
- work_item->next = NULL;
+ while ((commit = prio_queue_get(&queue)) != NULL) {
+ struct commit_list *parents;
- commit = work_item->item;
for (parents = commit->parents; parents ; parents = parents->next) {
struct commit *parent = parents->item;
+ int *pi = indegree_slab_at(&indegree, parent);
- if (!parent->indegree)
+ if (!*pi)
continue;
/*
@@ -579,21 +688,22 @@ void sort_in_topological_order(struct commit_list ** list, int lifo)
* when all their children have been emitted thereby
* guaranteeing topological order.
*/
- if (--parent->indegree == 1) {
- if (!lifo)
- commit_list_insert_by_date(parent, &work);
- else
- commit_list_insert(parent, &work);
- }
+ if (--(*pi) == 1)
+ prio_queue_put(&queue, parent);
}
/*
- * work_item is a commit all of whose children
- * have already been emitted. we can emit it now.
+ * all children of commit have already been
+ * emitted. we can emit it now.
*/
- commit->indegree = 0;
- *pptr = work_item;
- pptr = &work_item->next;
+ *(indegree_slab_at(&indegree, commit)) = 0;
+
+ pptr = &commit_list_insert(commit, pptr)->next;
}
+
+ clear_indegree_slab(&indegree);
+ clear_prio_queue(&queue);
+ if (sort_order == REV_SORT_BY_AUTHOR_DATE)
+ clear_author_date_slab(&author_date);
}
/* merge-base stuff */
diff --git a/commit.h b/commit.h
index 6e9c7cd9d5..4d452dc96d 100644
--- a/commit.h
+++ b/commit.h
@@ -15,7 +15,7 @@ struct commit_list {
struct commit {
struct object object;
void *util;
- unsigned int indegree;
+ unsigned int index;
unsigned long date;
struct commit_list *parents;
struct tree *tree;
@@ -142,15 +142,24 @@ void clear_commit_marks(struct commit *commit, unsigned int mark);
void clear_commit_marks_many(int nr, struct commit **commit, unsigned int mark);
void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark);
+
+enum rev_sort_order {
+ REV_SORT_IN_GRAPH_ORDER = 0,
+ REV_SORT_BY_COMMIT_DATE,
+ REV_SORT_BY_AUTHOR_DATE
+};
+
/*
* Performs an in-place topological sort of list supplied.
*
* invariant of resulting list is:
* a reachable from b => ord(b) < ord(a)
- * in addition, when lifo == 0, commits on parallel tracks are
- * sorted in the dates order.
+ * sort_order further specifies:
+ * REV_SORT_IN_GRAPH_ORDER: try to show a commit on a single-parent
+ * chain together.
+ * REV_SORT_BY_COMMIT_DATE: show eligible commits in committer-date order.
*/
-void sort_in_topological_order(struct commit_list ** list, int lifo);
+void sort_in_topological_order(struct commit_list **, enum rev_sort_order);
struct commit_graft {
unsigned char sha1[20];
diff --git a/configure.ac b/configure.ac
index f3462d9c81..2f433939dc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -193,7 +193,7 @@ AC_ARG_ENABLE([pthreads],
[FLAGS is the value to pass to the compiler to enable POSIX Threads.]
[The default if FLAGS is not specified is to try first -pthread]
[and then -lpthread.]
- [--without-pthreads will disable threading.])],
+ [--disable-pthreads will disable threading.])],
[
if test "x$enableval" = "xyes"; then
AC_MSG_NOTICE([Will try -pthread then -lpthread to enable POSIX Threads])
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 6c3bafeea5..fa3ae1e853 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -33,8 +33,6 @@ esac
# returns location of .git repo
__gitdir ()
{
- # Note: this function is duplicated in git-prompt.sh
- # When updating it, make sure you update the other one to match.
if [ -z "${1-}" ]; then
if [ -n "${__git_dir-}" ]; then
echo "$__git_dir"
@@ -2492,9 +2490,10 @@ __git_main ()
i="${words[c]}"
case "$i" in
--git-dir=*) __git_dir="${i#--git-dir=}" ;;
+ --git-dir) ((c++)) ; __git_dir="${words[c]}" ;;
--bare) __git_dir="." ;;
--help) command="help"; break ;;
- -c) c=$((++c)) ;;
+ -c|--work-tree|--namespace) ((c++)) ;;
-*) ;;
*) command="$i"; break ;;
esac
@@ -2512,6 +2511,7 @@ __git_main ()
--exec-path
--exec-path=
--html-path
+ --man-path
--info-path
--work-tree=
--namespace=
diff --git a/contrib/completion/git-prompt.sh b/contrib/completion/git-prompt.sh
index 07a6218d10..a81ef5a482 100644
--- a/contrib/completion/git-prompt.sh
+++ b/contrib/completion/git-prompt.sh
@@ -3,7 +3,7 @@
# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
# Distributed under the GNU General Public License, version 2.0.
#
-# This script allows you to see the current branch in your prompt.
+# This script allows you to see repository status in your prompt.
#
# To enable:
#
@@ -13,24 +13,27 @@
# 3a) Change your PS1 to call __git_ps1 as
# command-substitution:
# Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
-# ZSH: PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
+# ZSH: setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '
# the optional argument will be used as format string.
-# 3b) Alternatively, if you are using bash, __git_ps1 can be
-# used for PROMPT_COMMAND with two parameters, <pre> and
-# <post>, which are strings you would put in $PS1 before
-# and after the status string generated by the git-prompt
-# machinery. e.g.
+# 3b) Alternatively, for a slightly faster prompt, __git_ps1 can
+# be used for PROMPT_COMMAND in Bash or for precmd() in Zsh
+# with two parameters, <pre> and <post>, which are strings
+# you would put in $PS1 before and after the status string
+# generated by the git-prompt machinery. e.g.
# Bash: PROMPT_COMMAND='__git_ps1 "\u@\h:\w" "\\\$ "'
+# will show username, at-sign, host, colon, cwd, then
+# various status string, followed by dollar and SP, as
+# your prompt.
# ZSH: precmd () { __git_ps1 "%n" ":%~$ " "|%s" }
-# will show username, at-sign, host, colon, cwd, then
-# various status string, followed by dollar and SP, as
-# your prompt.
+# will show username, pipe, then various status string,
+# followed by colon, cwd, dollar and SP, as your prompt.
# Optionally, you can supply a third argument with a printf
# format string to finetune the output of the branch status
#
-# The argument to __git_ps1 will be displayed only if you are currently
-# in a git repository. The %s token will be the name of the current
-# branch.
+# The repository status will be displayed only if you are currently in a
+# git repository. The %s token is the placeholder for the shown status.
+#
+# The prompt status always includes the current branch name.
#
# In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty value,
# unstaged (*) and staged (+) changes will be shown next to the branch
@@ -78,31 +81,8 @@
#
# If you would like a colored hint about the current dirty state, set
# GIT_PS1_SHOWCOLORHINTS to a nonempty value. The colors are based on
-# the colored output of "git status -sb".
-
-# __gitdir accepts 0 or 1 arguments (i.e., location)
-# returns location of .git repo
-__gitdir ()
-{
- # Note: this function is duplicated in git-completion.bash
- # When updating it, make sure you update the other one to match.
- if [ -z "${1-}" ]; then
- if [ -n "${__git_dir-}" ]; then
- echo "$__git_dir"
- elif [ -n "${GIT_DIR-}" ]; then
- test -d "${GIT_DIR-}" || return 1
- echo "$GIT_DIR"
- elif [ -d .git ]; then
- echo .git
- else
- git rev-parse --git-dir 2>/dev/null
- fi
- elif [ -d "$1/.git" ]; then
- echo "$1/.git"
- else
- echo "$1"
- fi
-}
+# the colored output of "git status -sb" and are available only when
+# using __git_ps1 for PROMPT_COMMAND or precmd.
# stores the divergence from upstream in $p
# used by GIT_PS1_SHOWUPSTREAM
@@ -225,8 +205,8 @@ __git_ps1_show_upstream ()
}
# Helper function that is meant to be called from __git_ps1. It
-# builds up a gitstring injecting color codes into the appropriate
-# places.
+# injects color codes into the appropriate gitstring variables used
+# to build a gitstring.
__git_ps1_colorize_gitstring ()
{
if [[ -n ${ZSH_VERSION-} ]]; then
@@ -234,74 +214,40 @@ __git_ps1_colorize_gitstring ()
local c_green='%F{green}'
local c_lblue='%F{blue}'
local c_clear='%f'
- local bad_color=$c_red
- local ok_color=$c_green
- local branch_color="$c_clear"
- local flags_color="$c_lblue"
- local branchstring="$c${b##refs/heads/}"
-
- if [ $detached = no ]; then
- branch_color="$ok_color"
- else
- branch_color="$bad_color"
- fi
-
- gitstring="$branch_color$branchstring$c_clear"
-
- if [ -n "$w$i$s$u$r$p" ]; then
- gitstring="$gitstring$z"
- fi
- if [ "$w" = "*" ]; then
- gitstring="$gitstring$bad_color$w"
- fi
- if [ -n "$i" ]; then
- gitstring="$gitstring$ok_color$i"
- fi
- if [ -n "$s" ]; then
- gitstring="$gitstring$flags_color$s"
- fi
- if [ -n "$u" ]; then
- gitstring="$gitstring$bad_color$u"
- fi
- gitstring="$gitstring$c_clear$r$p"
- return
+ else
+ # Using \[ and \] around colors is necessary to prevent
+ # issues with command line editing/browsing/completion!
+ local c_red='\[\e[31m\]'
+ local c_green='\[\e[32m\]'
+ local c_lblue='\[\e[1;34m\]'
+ local c_clear='\[\e[0m\]'
fi
- local c_red='\e[31m'
- local c_green='\e[32m'
- local c_lblue='\e[1;34m'
- local c_clear='\e[0m'
local bad_color=$c_red
local ok_color=$c_green
- local branch_color="$c_clear"
local flags_color="$c_lblue"
- local branchstring="$c${b##refs/heads/}"
+ local branch_color=""
if [ $detached = no ]; then
branch_color="$ok_color"
else
branch_color="$bad_color"
fi
+ c="$branch_color$c"
- # Setting gitstring directly with \[ and \] around colors
- # is necessary to prevent wrapping issues!
- gitstring="\[$branch_color\]$branchstring\[$c_clear\]"
-
- if [ -n "$w$i$s$u$r$p" ]; then
- gitstring="$gitstring$z"
- fi
+ z="$c_clear$z"
if [ "$w" = "*" ]; then
- gitstring="$gitstring\[$bad_color\]$w"
+ w="$bad_color$w"
fi
if [ -n "$i" ]; then
- gitstring="$gitstring\[$ok_color\]$i"
+ i="$ok_color$i"
fi
if [ -n "$s" ]; then
- gitstring="$gitstring\[$flags_color\]$s"
+ s="$flags_color$s"
fi
if [ -n "$u" ]; then
- gitstring="$gitstring\[$bad_color\]$u"
+ u="$bad_color$u"
fi
- gitstring="$gitstring\[$c_clear\]$r$p"
+ r="$c_clear$r"
}
# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
@@ -335,50 +281,83 @@ __git_ps1 ()
;;
esac
- local g="$(__gitdir)"
- if [ -z "$g" ]; then
+ local repo_info rev_parse_exit_code
+ repo_info="$(git rev-parse --git-dir --is-inside-git-dir \
+ --is-bare-repository --is-inside-work-tree \
+ --short HEAD 2>/dev/null)"
+ rev_parse_exit_code="$?"
+
+ if [ -z "$repo_info" ]; then
if [ $pcmode = yes ]; then
#In PC mode PS1 always needs to be set
PS1="$ps1pc_start$ps1pc_end"
fi
+ return
+ fi
+
+ local short_sha
+ if [ "$rev_parse_exit_code" = "0" ]; then
+ short_sha="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
+ fi
+ local inside_worktree="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
+ local bare_repo="${repo_info##*$'\n'}"
+ repo_info="${repo_info%$'\n'*}"
+ local inside_gitdir="${repo_info##*$'\n'}"
+ local g="${repo_info%$'\n'*}"
+
+ local r=""
+ local b=""
+ local step=""
+ local total=""
+ if [ -d "$g/rebase-merge" ]; then
+ read b 2>/dev/null <"$g/rebase-merge/head-name"
+ read step 2>/dev/null <"$g/rebase-merge/msgnum"
+ read total 2>/dev/null <"$g/rebase-merge/end"
+ if [ -f "$g/rebase-merge/interactive" ]; then
+ r="|REBASE-i"
+ else
+ r="|REBASE-m"
+ fi
else
- local r=""
- local b=""
- local step=""
- local total=""
- if [ -d "$g/rebase-merge" ]; then
- b="$(cat "$g/rebase-merge/head-name" 2>/dev/null)"
- step=$(cat "$g/rebase-merge/msgnum" 2>/dev/null)
- total=$(cat "$g/rebase-merge/end" 2>/dev/null)
- if [ -f "$g/rebase-merge/interactive" ]; then
- r="|REBASE-i"
+ if [ -d "$g/rebase-apply" ]; then
+ read step 2>/dev/null <"$g/rebase-apply/next"
+ read total 2>/dev/null <"$g/rebase-apply/last"
+ if [ -f "$g/rebase-apply/rebasing" ]; then
+ read b 2>/dev/null <"$g/rebase-apply/head-name"
+ r="|REBASE"
+ elif [ -f "$g/rebase-apply/applying" ]; then
+ r="|AM"
else
- r="|REBASE-m"
+ r="|AM/REBASE"
fi
+ elif [ -f "$g/MERGE_HEAD" ]; then
+ r="|MERGING"
+ elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
+ r="|CHERRY-PICKING"
+ elif [ -f "$g/REVERT_HEAD" ]; then
+ r="|REVERTING"
+ elif [ -f "$g/BISECT_LOG" ]; then
+ r="|BISECTING"
+ fi
+
+ if [ -n "$b" ]; then
+ :
+ elif [ -h "$g/HEAD" ]; then
+ # symlink symbolic ref
+ b="$(git symbolic-ref HEAD 2>/dev/null)"
else
- if [ -d "$g/rebase-apply" ]; then
- step=$(cat "$g/rebase-apply/next" 2>/dev/null)
- total=$(cat "$g/rebase-apply/last" 2>/dev/null)
- if [ -f "$g/rebase-apply/rebasing" ]; then
- b="$(cat "$g/rebase-apply/head-name" 2>/dev/null)"
- r="|REBASE"
- elif [ -f "$g/rebase-apply/applying" ]; then
- r="|AM"
- else
- r="|AM/REBASE"
+ local head=""
+ if ! read head 2>/dev/null <"$g/HEAD"; then
+ if [ $pcmode = yes ]; then
+ PS1="$ps1pc_start$ps1pc_end"
fi
- elif [ -f "$g/MERGE_HEAD" ]; then
- r="|MERGING"
- elif [ -f "$g/CHERRY_PICK_HEAD" ]; then
- r="|CHERRY-PICKING"
- elif [ -f "$g/REVERT_HEAD" ]; then
- r="|REVERTING"
- elif [ -f "$g/BISECT_LOG" ]; then
- r="|BISECTING"
+ return
fi
-
- test -n "$b" ||
- b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+ # is it a symbolic ref?
+ b="${head#ref: }"
+ if [ "$head" = "$b" ]; then
detached=yes
b="$(
case "${GIT_PS1_DESCRIBE_STYLE-}" in
@@ -392,70 +371,75 @@ __git_ps1 ()
git describe --tags --exact-match HEAD ;;
esac 2>/dev/null)" ||
- b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
- b="unknown"
+ b="$short_sha..."
b="($b)"
- }
+ fi
fi
+ fi
- if [ -n "$step" ] && [ -n "$total" ]; then
- r="$r $step/$total"
- fi
+ if [ -n "$step" ] && [ -n "$total" ]; then
+ r="$r $step/$total"
+ fi
- local w=""
- local i=""
- local s=""
- local u=""
- local c=""
- local p=""
+ local w=""
+ local i=""
+ local s=""
+ local u=""
+ local c=""
+ local p=""
- if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
- if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
- c="BARE:"
+ if [ "true" = "$inside_gitdir" ]; then
+ if [ "true" = "$bare_repo" ]; then
+ c="BARE:"
+ else
+ b="GIT_DIR!"
+ fi
+ elif [ "true" = "$inside_worktree" ]; then
+ if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
+ [ "$(git config --bool bash.showDirtyState)" != "false" ]
+ then
+ git diff --no-ext-diff --quiet --exit-code || w="*"
+ if [ -n "$short_sha" ]; then
+ git diff-index --cached --quiet HEAD -- || i="+"
else
- b="GIT_DIR!"
- fi
- elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
- if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
- [ "$(git config --bool bash.showDirtyState)" != "false" ]
- then
- git diff --no-ext-diff --quiet --exit-code || w="*"
- if git rev-parse --quiet --verify HEAD >/dev/null; then
- git diff-index --cached --quiet HEAD -- || i="+"
- else
- i="#"
- fi
- fi
- if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
- git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+ i="#"
fi
+ fi
+ if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ] &&
+ [ -r "$g/refs/stash" ]; then
+ s="$"
+ fi
- if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
- [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
- [ -n "$(git ls-files --others --exclude-standard)" ]
- then
- u="%${ZSH_VERSION+%}"
- fi
+ if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ] &&
+ [ "$(git config --bool bash.showUntrackedFiles)" != "false" ] &&
+ git ls-files --others --exclude-standard --error-unmatch -- '*' >/dev/null 2>/dev/null
+ then
+ u="%${ZSH_VERSION+%}"
+ fi
- if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
- __git_ps1_show_upstream
- fi
+ if [ -n "${GIT_PS1_SHOWUPSTREAM-}" ]; then
+ __git_ps1_show_upstream
fi
+ fi
- local z="${GIT_PS1_STATESEPARATOR-" "}"
- local f="$w$i$s$u"
- if [ $pcmode = yes ]; then
- local gitstring=
- if [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
- __git_ps1_colorize_gitstring
- else
- gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
- fi
+ local z="${GIT_PS1_STATESEPARATOR-" "}"
+
+ # NO color option unless in PROMPT_COMMAND mode
+ if [ $pcmode = yes ] && [ -n "${GIT_PS1_SHOWCOLORHINTS-}" ]; then
+ __git_ps1_colorize_gitstring
+ fi
+
+ local f="$w$i$s$u"
+ local gitstring="$c${b##refs/heads/}${f:+$z$f}$r$p"
+
+ if [ $pcmode = yes ]; then
+ if [[ -n ${ZSH_VERSION-} ]]; then
gitstring=$(printf -- "$printf_format" "$gitstring")
- PS1="$ps1pc_start$gitstring$ps1pc_end"
else
- # NO color option unless in PROMPT_COMMAND mode
- printf -- "$printf_format" "$c${b##refs/heads/}${f:+$z$f}$r$p"
+ printf -v gitstring -- "$printf_format" "$gitstring"
fi
+ PS1="$ps1pc_start$gitstring$ps1pc_end"
+ else
+ printf -- "$printf_format" "$gitstring"
fi
}
diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl
index 71baf8ace8..d09f5da668 100755
--- a/contrib/mw-to-git/git-remote-mediawiki.perl
+++ b/contrib/mw-to-git/git-remote-mediawiki.perl
@@ -635,9 +635,9 @@ sub literal_data_raw {
my ($content) = @_;
# Avoid confusion between size in bytes and in characters
utf8::downgrade($content);
- binmode {*STDOUT}, ':raw';
+ binmode STDOUT, ':raw';
print {*STDOUT} 'data ', bytes::length($content), "\n", $content;
- binmode {*STDOUT}, ':encoding(UTF-8)';
+ binmode STDOUT, ':encoding(UTF-8)';
return;
}
diff --git a/diff.c b/diff.c
index f0b3e7cfe3..e53ddad7d4 100644
--- a/diff.c
+++ b/diff.c
@@ -2677,6 +2677,14 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
int diff_populate_filespec(struct diff_filespec *s, int size_only)
{
int err = 0;
+ /*
+ * demote FAIL to WARN to allow inspecting the situation
+ * instead of refusing.
+ */
+ enum safe_crlf crlf_warn = (safe_crlf == SAFE_CRLF_FAIL
+ ? SAFE_CRLF_WARN
+ : safe_crlf);
+
if (!DIFF_FILE_VALID(s))
die("internal error: asking to populate invalid file.");
if (S_ISDIR(s->mode))
@@ -2732,7 +2740,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
/*
* Convert from working tree format to canonical git format
*/
- if (convert_to_git(s->path, s->data, s->size, &buf, safe_crlf)) {
+ if (convert_to_git(s->path, s->data, s->size, &buf, crlf_warn)) {
size_t size = 0;
munmap(s->data, s->size);
s->should_munmap = 0;
@@ -3593,6 +3601,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
DIFF_XDL_SET(options, IGNORE_WHITESPACE_CHANGE);
else if (!strcmp(arg, "--ignore-space-at-eol"))
DIFF_XDL_SET(options, IGNORE_WHITESPACE_AT_EOL);
+ else if (!strcmp(arg, "--ignore-blank-lines"))
+ DIFF_XDL_SET(options, IGNORE_BLANK_LINES);
else if (!strcmp(arg, "--patience"))
options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
else if (!strcmp(arg, "--histogram"))
diff --git a/fixup-builtins b/fixup-builtins
deleted file mode 100755
index 63dfa4c475..0000000000
--- a/fixup-builtins
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/sh
-while [ "$1" ]
-do
- if [ "$1" != "git-sh-setup" -a "$1" != "git-parse-remote" -a "$1" != "git-svn" ]; then
- old="$1"
- new=$(echo "$1" | sed 's/git-/git /')
- echo "Converting '$old' to '$new'"
- sed -i "s/\\<$old\\>/$new/g" $(git ls-files '*.sh')
- fi
- shift
-done
-
-sed -i 's/git merge-one-file/git-merge-one-file/g
-s/git rebase-todo/git-rebase-todo/g' $(git ls-files '*.sh')
-git update-index --refresh >& /dev/null
-exit 0
diff --git a/git-am.sh b/git-am.sh
index 9f4450916c..7ea40fe644 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -6,7 +6,7 @@ SUBDIRECTORY_OK=Yes
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git am [options] [(<mbox>|<Maildir>)...]
-git am [options] (--resolved | --skip | --abort)
+git am [options] (--continue | --skip | --abort)
--
i,interactive run interactively
b,binary* (historical option -- no-op)
@@ -102,7 +102,7 @@ stop_here_user_resolve () {
printf '%s\n' "$resolvemsg"
stop_here $1
fi
- eval_gettextln "When you have resolved this problem, run \"\$cmdline --resolved\".
+ eval_gettextln "When you have resolved this problem, run \"\$cmdline --continue\".
If you prefer to skip this patch, run \"\$cmdline --skip\" instead.
To restore the original branch and stop patching, run \"\$cmdline --abort\"."
@@ -523,7 +523,7 @@ Use \"git am --abort\" to remove it.")"
esac
fi
- # Make sure we are not given --skip, --resolved, nor --abort
+ # Make sure we are not given --skip, --continue, nor --abort
test "$skip$resolved$abort" = "" ||
die "$(gettext "Resolve operation not in progress, we are not resuming.")"
@@ -670,7 +670,7 @@ do
# - patch is the patch body.
#
# When we are resuming, these files are either already prepared
- # by the user, or the user can tell us to do so by --resolved flag.
+ # by the user, or the user can tell us to do so by --continue flag.
case "$resume" in
'')
if test -f "$dotest/rebasing"
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index f953d8d224..169e876eed 100644
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -689,8 +689,22 @@ rearrange_squash () {
case "$message" in
"squash! "*|"fixup! "*)
action="${message%%!*}"
- rest="${message#*! }"
- echo "$sha1 $action $rest"
+ rest=$message
+ prefix=
+ # skip all squash! or fixup! (but save for later)
+ while :
+ do
+ case "$rest" in
+ "squash! "*|"fixup! "*)
+ prefix="$prefix${rest%%!*},"
+ rest="${rest#*! }"
+ ;;
+ *)
+ break
+ ;;
+ esac
+ done
+ echo "$sha1 $action $prefix $rest"
# if it's a single word, try to resolve to a full sha1 and
# emit a second copy. This allows us to match on both message
# and on sha1 prefix
@@ -699,7 +713,7 @@ rearrange_squash () {
if test -n "$fullsha"; then
# prefix the action to uniquely identify this line as
# intended for full sha1 match
- echo "$sha1 +$action $fullsha"
+ echo "$sha1 +$action $prefix $fullsha"
fi
fi
esac
@@ -714,7 +728,7 @@ rearrange_squash () {
esac
printf '%s\n' "$pick $sha1 $message"
used="$used$sha1 "
- while read -r squash action msg_content
+ while read -r squash action msg_prefix msg_content
do
case " $used" in
*" $squash "*) continue ;;
@@ -730,7 +744,8 @@ rearrange_squash () {
case "$message" in "$msg_content"*) emit=1;; esac ;;
esac
if test $emit = 1; then
- printf '%s\n' "$action $squash $action! $msg_content"
+ real_prefix=$(echo "$msg_prefix" | sed "s/,/! /g")
+ printf '%s\n' "$action $squash ${real_prefix}$msg_content"
used="$used$squash "
fi
done <"$1.sq"
diff --git a/git-submodule.sh b/git-submodule.sh
index eb58c8e89d..945e296d30 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -14,10 +14,13 @@ USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <re
or: $dashless [--quiet] foreach [--recursive] <command>
or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
OPTIONS_SPEC=
+SUBDIRECTORY_OK=Yes
. git-sh-setup
. git-sh-i18n
. git-parse-remote
require_work_tree
+wt_prefix=$(git rev-parse --show-prefix)
+cd_to_toplevel
command=
branch=
@@ -106,12 +109,48 @@ resolve_relative_url ()
echo "${is_relative:+${up_path}}${remoteurl#./}"
}
+# Resolve a path to be relative to another path. This is intended for
+# converting submodule paths when git-submodule is run in a subdirectory
+# and only handles paths where the directory separator is '/'.
+#
+# The output is the first argument as a path relative to the second argument,
+# which defaults to $wt_prefix if it is omitted.
+relative_path ()
+{
+ local target curdir result
+ target=$1
+ curdir=${2-$wt_prefix}
+ curdir=${curdir%/}
+ result=
+
+ while test -n "$curdir"
+ do
+ case "$target" in
+ "$curdir/"*)
+ target=${target#"$curdir"/}
+ break
+ ;;
+ esac
+
+ result="${result}../"
+ if test "$curdir" = "${curdir%/*}"
+ then
+ curdir=
+ else
+ curdir="${curdir%/*}"
+ fi
+ done
+
+ echo "$result$target"
+}
+
#
# Get submodule info for registered submodules
# $@ = path to limit submodule list
#
module_list()
{
+ eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
(
git ls-files -z --error-unmatch --stage -- "$@" ||
echo "unmatched pathspec exists"
@@ -283,6 +322,7 @@ isnumber()
cmd_add()
{
# parse $args after "submodule ... add".
+ reference_path=
while test $# -ne 0
do
case "$1" in
@@ -299,11 +339,11 @@ cmd_add()
;;
--reference)
case "$2" in '') usage ;; esac
- reference="--reference=$2"
+ reference_path=$2
shift
;;
--reference=*)
- reference="$1"
+ reference_path="${1#--reference=}"
;;
--name)
case "$2" in '') usage ;; esac
@@ -324,6 +364,14 @@ cmd_add()
shift
done
+ if test -n "$reference_path"
+ then
+ is_absolute_path "$reference_path" ||
+ reference_path="$wt_prefix$reference_path"
+
+ reference="--reference=$reference_path"
+ fi
+
repo=$1
sm_path=$2
@@ -336,9 +384,14 @@ cmd_add()
usage
fi
+ is_absolute_path "$sm_path" || sm_path="$wt_prefix$sm_path"
+
# assure repo is absolute or relative to parent
case "$repo" in
./*|../*)
+ test -z "$wt_prefix" ||
+ die "$(gettext "Relative path can only be used from the toplevel of the working tree")"
+
# dereference source url relative to parent's url
realrepo=$(resolve_relative_url "$repo") || exit
;;
@@ -472,21 +525,23 @@ cmd_foreach()
die_if_unmatched "$mode"
if test -e "$sm_path"/.git
then
- say "$(eval_gettext "Entering '\$prefix\$sm_path'")"
+ displaypath=$(relative_path "$sm_path")
+ say "$(eval_gettext "Entering '\$prefix\$displaypath'")"
name=$(module_name "$sm_path")
(
prefix="$prefix$sm_path/"
clear_local_git_env
- # we make $path available to scripts ...
- path=$sm_path
cd "$sm_path" &&
+ sm_path=$(relative_path "$sm_path") &&
+ # we make $path available to scripts ...
+ path=$sm_path &&
eval "$@" &&
if test -n "$recursive"
then
cmd_foreach "--recursive" "$@"
fi
) <&3 3<&- ||
- die "$(eval_gettext "Stopping at '\$sm_path'; script returned non-zero status.")"
+ die "$(eval_gettext "Stopping at '\$prefix\$displaypath'; script returned non-zero status.")"
fi
done
}
@@ -525,12 +580,14 @@ cmd_init()
die_if_unmatched "$mode"
name=$(module_name "$sm_path") || exit
+ displaypath=$(relative_path "$sm_path")
+
# Copy url setting when it is not set yet
if test -z "$(git config "submodule.$name.url")"
then
url=$(git config -f .gitmodules submodule."$name".url)
test -z "$url" &&
- die "$(eval_gettext "No url found for submodule path '\$sm_path' in .gitmodules")"
+ die "$(eval_gettext "No url found for submodule path '\$displaypath' in .gitmodules")"
# Possibly a url relative to parent
case "$url" in
@@ -539,9 +596,9 @@ cmd_init()
;;
esac
git config submodule."$name".url "$url" ||
- die "$(eval_gettext "Failed to register url for submodule path '\$sm_path'")"
+ die "$(eval_gettext "Failed to register url for submodule path '\$displaypath'")"
- say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$sm_path'")"
+ say "$(eval_gettext "Submodule '\$name' (\$url) registered for path '\$displaypath'")"
fi
# Copy "update" setting when it is not set yet
@@ -549,7 +606,7 @@ cmd_init()
test -z "$upd" ||
test -n "$(git config submodule."$name".update)" ||
git config submodule."$name".update "$upd" ||
- die "$(eval_gettext "Failed to register update mode for submodule path '\$sm_path'")"
+ die "$(eval_gettext "Failed to register update mode for submodule path '\$displaypath'")"
done
}
@@ -595,27 +652,29 @@ cmd_deinit()
die_if_unmatched "$mode"
name=$(module_name "$sm_path") || exit
+ displaypath=$(relative_path "$sm_path")
+
# Remove the submodule work tree (unless the user already did it)
if test -d "$sm_path"
then
# Protect submodules containing a .git directory
if test -d "$sm_path/.git"
then
- echo >&2 "$(eval_gettext "Submodule work tree '\$sm_path' contains a .git directory")"
+ echo >&2 "$(eval_gettext "Submodule work tree '\$displaypath' contains a .git directory")"
die "$(eval_gettext "(use 'rm -rf' if you really want to remove it including all of its history)")"
fi
if test -z "$force"
then
git rm -qn "$sm_path" ||
- die "$(eval_gettext "Submodule work tree '\$sm_path' contains local modifications; use '-f' to discard them")"
+ die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
fi
rm -rf "$sm_path" &&
- say "$(eval_gettext "Cleared directory '\$sm_path'")" ||
- say "$(eval_gettext "Could not remove submodule work tree '\$sm_path'")"
+ say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
+ say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
fi
- mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$sm_path'")"
+ mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
# Remove the .git/config entries (unless the user already did it)
if test -n "$(git config --get-regexp submodule."$name\.")"
@@ -624,7 +683,7 @@ cmd_deinit()
# the user later decides to init this submodule again
url=$(git config submodule."$name".url)
git config --remove-section submodule."$name" 2>/dev/null &&
- say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$sm_path'")"
+ say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
fi
done
}
@@ -718,9 +777,11 @@ cmd_update()
update_module=$(git config submodule."$name".update)
fi
+ displaypath=$(relative_path "$prefix$sm_path")
+
if test "$update_module" = "none"
then
- echo "Skipping submodule '$prefix$sm_path'"
+ echo "Skipping submodule '$displaypath'"
continue
fi
@@ -729,7 +790,7 @@ cmd_update()
# Only mention uninitialized submodules when its
# path have been specified
test "$#" != "0" &&
- say "$(eval_gettext "Submodule path '\$prefix\$sm_path' not initialized
+ say "$(eval_gettext "Submodule path '\$displaypath' not initialized
Maybe you want to use 'update --init'?")"
continue
fi
@@ -742,7 +803,7 @@ Maybe you want to use 'update --init'?")"
else
subsha1=$(clear_local_git_env; cd "$sm_path" &&
git rev-parse --verify HEAD) ||
- die "$(eval_gettext "Unable to find current revision in submodule path '\$prefix\$sm_path'")"
+ die "$(eval_gettext "Unable to find current revision in submodule path '\$displaypath'")"
fi
if test -n "$remote"
@@ -775,7 +836,7 @@ Maybe you want to use 'update --init'?")"
(clear_local_git_env; cd "$sm_path" &&
( (rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
test -z "$rev") || git-fetch)) ||
- die "$(eval_gettext "Unable to fetch in submodule path '\$prefix\$sm_path'")"
+ die "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
fi
# Is this something we just cloned?
@@ -789,20 +850,20 @@ Maybe you want to use 'update --init'?")"
case "$update_module" in
rebase)
command="git rebase"
- die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$prefix\$sm_path'")"
- say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': rebased into '\$sha1'")"
+ die_msg="$(eval_gettext "Unable to rebase '\$sha1' in submodule path '\$displaypath'")"
+ say_msg="$(eval_gettext "Submodule path '\$displaypath': rebased into '\$sha1'")"
must_die_on_failure=yes
;;
merge)
command="git merge"
- die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$prefix\$sm_path'")"
- say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': merged in '\$sha1'")"
+ die_msg="$(eval_gettext "Unable to merge '\$sha1' in submodule path '\$displaypath'")"
+ say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")"
must_die_on_failure=yes
;;
*)
command="git checkout $subforce -q"
- die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$prefix\$sm_path'")"
- say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': checked out '\$sha1'")"
+ die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
+ say_msg="$(eval_gettext "Submodule path '\$displaypath': checked out '\$sha1'")"
;;
esac
@@ -829,7 +890,7 @@ Maybe you want to use 'update --init'?")"
res=$?
if test $res -gt 0
then
- die_msg="$(eval_gettext "Failed to recurse into submodule path '\$prefix\$sm_path'")"
+ die_msg="$(eval_gettext "Failed to recurse into submodule path '\$displaypath'")"
if test $res -eq 1
then
err="${err};$die_msg"
@@ -943,6 +1004,7 @@ cmd_summary() {
fi
cd_to_toplevel
+ eval "set $(git rev-parse --sq --prefix "$wt_prefix" -- "$@")"
# Get modified modules cared by user
modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
sane_egrep '^:([0-7]* )?160000' |
@@ -992,16 +1054,18 @@ cmd_summary() {
! GIT_DIR="$name/.git" git-rev-parse -q --verify $sha1_dst^0 >/dev/null &&
missing_dst=t
+ display_name=$(relative_path "$name")
+
total_commits=
case "$missing_src,$missing_dst" in
t,)
- errmsg="$(eval_gettext " Warn: \$name doesn't contain commit \$sha1_src")"
+ errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_src")"
;;
,t)
- errmsg="$(eval_gettext " Warn: \$name doesn't contain commit \$sha1_dst")"
+ errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commit \$sha1_dst")"
;;
t,t)
- errmsg="$(eval_gettext " Warn: \$name doesn't contain commits \$sha1_src and \$sha1_dst")"
+ errmsg="$(eval_gettext " Warn: \$display_name doesn't contain commits \$sha1_src and \$sha1_dst")"
;;
*)
errmsg=
@@ -1030,12 +1094,12 @@ cmd_summary() {
submodule="$(gettext "submodule")"
if test $mod_dst = 160000
then
- echo "* $name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
+ echo "* $display_name $sha1_abbr_src($blob)->$sha1_abbr_dst($submodule)$total_commits:"
else
- echo "* $name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
+ echo "* $display_name $sha1_abbr_src($submodule)->$sha1_abbr_dst($blob)$total_commits:"
fi
else
- echo "* $name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
+ echo "* $display_name $sha1_abbr_src...$sha1_abbr_dst$total_commits:"
fi
if test -n "$errmsg"
then
@@ -1119,7 +1183,7 @@ cmd_status()
die_if_unmatched "$mode"
name=$(module_name "$sm_path") || exit
url=$(git config submodule."$name".url)
- displaypath="$prefix$sm_path"
+ displaypath=$(relative_path "$prefix$sm_path")
if test "$stage" = U
then
say "U$sha1 $displaypath"
@@ -1214,7 +1278,8 @@ cmd_sync()
if git config "submodule.$name.url" >/dev/null 2>/dev/null
then
- say "$(eval_gettext "Synchronizing submodule url for '\$prefix\$sm_path'")"
+ displaypath=$(relative_path "$prefix$sm_path")
+ say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
git config submodule."$name".url "$super_config_url"
if test -e "$sm_path"/.git
diff --git a/lockfile.c b/lockfile.c
index c6fb77b26f..8fbcb6a98a 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -124,15 +124,17 @@ static char *resolve_symlink(char *p, size_t s)
static int lock_file(struct lock_file *lk, const char *path, int flags)
{
- if (strlen(path) >= sizeof(lk->filename))
- return -1;
- strcpy(lk->filename, path);
/*
* subtract 5 from size to make sure there's room for adding
* ".lock" for the lock file name
*/
+ static const size_t max_path_len = sizeof(lk->filename) - 5;
+
+ if (strlen(path) >= max_path_len)
+ return -1;
+ strcpy(lk->filename, path);
if (!(flags & LOCK_NODEREF))
- resolve_symlink(lk->filename, sizeof(lk->filename)-5);
+ resolve_symlink(lk->filename, max_path_len);
strcat(lk->filename, ".lock");
lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
if (0 <= lk->fd) {
diff --git a/prio-queue.c b/prio-queue.c
new file mode 100644
index 0000000000..c9f8c6d253
--- /dev/null
+++ b/prio-queue.c
@@ -0,0 +1,84 @@
+#include "cache.h"
+#include "commit.h"
+#include "prio-queue.h"
+
+void prio_queue_reverse(struct prio_queue *queue)
+{
+ int i, j;
+
+ if (queue->compare != NULL)
+ die("BUG: prio_queue_reverse() on non-LIFO queue");
+ for (i = 0; i <= (j = (queue->nr - 1) - i); i++) {
+ struct commit *swap = queue->array[i];
+ queue->array[i] = queue->array[j];
+ queue->array[j] = swap;
+ }
+}
+
+void clear_prio_queue(struct prio_queue *queue)
+{
+ free(queue->array);
+ queue->nr = 0;
+ queue->alloc = 0;
+ queue->array = NULL;
+}
+
+void prio_queue_put(struct prio_queue *queue, void *thing)
+{
+ prio_queue_compare_fn compare = queue->compare;
+ int ix, parent;
+
+ /* Append at the end */
+ ALLOC_GROW(queue->array, queue->nr + 1, queue->alloc);
+ queue->array[queue->nr++] = thing;
+ if (!compare)
+ return; /* LIFO */
+
+ /* Bubble up the new one */
+ for (ix = queue->nr - 1; ix; ix = parent) {
+ parent = (ix - 1) / 2;
+ if (compare(queue->array[parent], queue->array[ix],
+ queue->cb_data) <= 0)
+ break;
+
+ thing = queue->array[parent];
+ queue->array[parent] = queue->array[ix];
+ queue->array[ix] = thing;
+ }
+}
+
+void *prio_queue_get(struct prio_queue *queue)
+{
+ void *result, *swap;
+ int ix, child;
+ prio_queue_compare_fn compare = queue->compare;
+
+ if (!queue->nr)
+ return NULL;
+ if (!compare)
+ return queue->array[--queue->nr]; /* LIFO */
+
+ result = queue->array[0];
+ if (!--queue->nr)
+ return result;
+
+ queue->array[0] = queue->array[queue->nr];
+
+ /* Push down the one at the root */
+ for (ix = 0; ix * 2 + 1 < queue->nr; ix = child) {
+ child = ix * 2 + 1; /* left */
+ if ((child + 1 < queue->nr) &&
+ (compare(queue->array[child], queue->array[child + 1],
+ queue->cb_data) >= 0))
+ child++; /* use right child */
+
+ if (compare(queue->array[ix], queue->array[child],
+ queue->cb_data) <= 0)
+ break;
+
+ swap = queue->array[child];
+ queue->array[child] = queue->array[ix];
+ queue->array[ix] = swap;
+ }
+ return result;
+}
diff --git a/prio-queue.h b/prio-queue.h
new file mode 100644
index 0000000000..9c3cd1f875
--- /dev/null
+++ b/prio-queue.h
@@ -0,0 +1,48 @@
+#ifndef PRIO_QUEUE_H
+#define PRIO_QUEUE_H
+
+/*
+ * A priority queue implementation, primarily for keeping track of
+ * commits in the 'date-order' so that we process them from new to old
+ * as they are discovered, but can be used to hold any pointer to
+ * struct. The caller is responsible for supplying a function to
+ * compare two "things".
+ *
+ * Alternatively, this data structure can also be used as a LIFO stack
+ * by specifying NULL as the comparison function.
+ */
+
+/*
+ * Compare two "things", one and two; the third parameter is cb_data
+ * in the prio_queue structure. The result is returned as a sign of
+ * the return value, being the same as the sign of the result of
+ * subtracting "two" from "one" (i.e. negative if "one" sorts earlier
+ * than "two").
+ */
+typedef int (*prio_queue_compare_fn)(const void *one, const void *two, void *cb_data);
+
+struct prio_queue {
+ prio_queue_compare_fn compare;
+ void *cb_data;
+ int alloc, nr;
+ void **array;
+};
+
+/*
+ * Add the "thing" to the queue.
+ */
+extern void prio_queue_put(struct prio_queue *, void *thing);
+
+/*
+ * Extract the "thing" that compares the smallest out of the queue,
+ * or NULL. If compare function is NULL, the queue acts as a LIFO
+ * stack.
+ */
+extern void *prio_queue_get(struct prio_queue *);
+
+extern void clear_prio_queue(struct prio_queue *);
+
+/* Reverse the LIFO elements */
+extern void prio_queue_reverse(struct prio_queue *);
+
+#endif /* PRIO_QUEUE_H */
diff --git a/read-cache.c b/read-cache.c
index b297addb57..d5201f9b06 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -67,6 +67,61 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
}
+void fill_stat_data(struct stat_data *sd, struct stat *st)
+{
+ sd->sd_ctime.sec = (unsigned int)st->st_ctime;
+ sd->sd_mtime.sec = (unsigned int)st->st_mtime;
+ sd->sd_ctime.nsec = ST_CTIME_NSEC(*st);
+ sd->sd_mtime.nsec = ST_MTIME_NSEC(*st);
+ sd->sd_dev = st->st_dev;
+ sd->sd_ino = st->st_ino;
+ sd->sd_uid = st->st_uid;
+ sd->sd_gid = st->st_gid;
+ sd->sd_size = st->st_size;
+}
+
+int match_stat_data(const struct stat_data *sd, struct stat *st)
+{
+ int changed = 0;
+
+ if (sd->sd_mtime.sec != (unsigned int)st->st_mtime)
+ changed |= MTIME_CHANGED;
+ if (trust_ctime && check_stat &&
+ sd->sd_ctime.sec != (unsigned int)st->st_ctime)
+ changed |= CTIME_CHANGED;
+
+#ifdef USE_NSEC
+ if (check_stat && sd->sd_mtime.nsec != ST_MTIME_NSEC(*st))
+ changed |= MTIME_CHANGED;
+ if (trust_ctime && check_stat &&
+ sd->sd_ctime.nsec != ST_CTIME_NSEC(*st))
+ changed |= CTIME_CHANGED;
+#endif
+
+ if (check_stat) {
+ if (sd->sd_uid != (unsigned int) st->st_uid ||
+ sd->sd_gid != (unsigned int) st->st_gid)
+ changed |= OWNER_CHANGED;
+ if (sd->sd_ino != (unsigned int) st->st_ino)
+ changed |= INODE_CHANGED;
+ }
+
+#ifdef USE_STDEV
+ /*
+ * st_dev breaks on network filesystems where different
+ * clients will have different views of what "device"
+ * the filesystem is on
+ */
+ if (check_stat && sd->sd_dev != (unsigned int) st->st_dev)
+ changed |= INODE_CHANGED;
+#endif
+
+ if (sd->sd_size != (unsigned int) st->st_size)
+ changed |= DATA_CHANGED;
+
+ return changed;
+}
+
/*
* This only updates the "non-critical" parts of the directory
* cache, ie the parts that aren't tracked by GIT, and only used
@@ -74,15 +129,7 @@ void rename_index_entry_at(struct index_state *istate, int nr, const char *new_n
*/
void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
{
- ce->ce_ctime.sec = (unsigned int)st->st_ctime;
- ce->ce_mtime.sec = (unsigned int)st->st_mtime;
- ce->ce_ctime.nsec = ST_CTIME_NSEC(*st);
- ce->ce_mtime.nsec = ST_MTIME_NSEC(*st);
- ce->ce_dev = st->st_dev;
- ce->ce_ino = st->st_ino;
- ce->ce_uid = st->st_uid;
- ce->ce_gid = st->st_gid;
- ce->ce_size = st->st_size;
+ fill_stat_data(&ce->ce_stat_data, st);
if (assume_unchanged)
ce->ce_flags |= CE_VALID;
@@ -195,43 +242,11 @@ static int ce_match_stat_basic(const struct cache_entry *ce, struct stat *st)
default:
die("internal error: ce_mode is %o", ce->ce_mode);
}
- if (ce->ce_mtime.sec != (unsigned int)st->st_mtime)
- changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
- ce->ce_ctime.sec != (unsigned int)st->st_ctime)
- changed |= CTIME_CHANGED;
-
-#ifdef USE_NSEC
- if (check_stat && ce->ce_mtime.nsec != ST_MTIME_NSEC(*st))
- changed |= MTIME_CHANGED;
- if (trust_ctime && check_stat &&
- ce->ce_ctime.nsec != ST_CTIME_NSEC(*st))
- changed |= CTIME_CHANGED;
-#endif
-
- if (check_stat) {
- if (ce->ce_uid != (unsigned int) st->st_uid ||
- ce->ce_gid != (unsigned int) st->st_gid)
- changed |= OWNER_CHANGED;
- if (ce->ce_ino != (unsigned int) st->st_ino)
- changed |= INODE_CHANGED;
- }
-#ifdef USE_STDEV
- /*
- * st_dev breaks on network filesystems where different
- * clients will have different views of what "device"
- * the filesystem is on
- */
- if (check_stat && ce->ce_dev != (unsigned int) st->st_dev)
- changed |= INODE_CHANGED;
-#endif
-
- if (ce->ce_size != (unsigned int) st->st_size)
- changed |= DATA_CHANGED;
+ changed |= match_stat_data(&ce->ce_stat_data, st);
/* Racily smudged entry? */
- if (!ce->ce_size) {
+ if (!ce->ce_stat_data.sd_size) {
if (!is_empty_blob_sha1(ce->sha1))
changed |= DATA_CHANGED;
}
@@ -246,11 +261,11 @@ static int is_racy_timestamp(const struct index_state *istate,
istate->timestamp.sec &&
#ifdef USE_NSEC
/* nanosecond timestamped files can also be racy! */
- (istate->timestamp.sec < ce->ce_mtime.sec ||
- (istate->timestamp.sec == ce->ce_mtime.sec &&
- istate->timestamp.nsec <= ce->ce_mtime.nsec))
+ (istate->timestamp.sec < ce->ce_stat_data.sd_mtime.sec ||
+ (istate->timestamp.sec == ce->ce_stat_data.sd_mtime.sec &&
+ istate->timestamp.nsec <= ce->ce_stat_data.sd_mtime.nsec))
#else
- istate->timestamp.sec <= ce->ce_mtime.sec
+ istate->timestamp.sec <= ce->ce_stat_data.sd_mtime.sec
#endif
);
}
@@ -342,7 +357,7 @@ int ie_modified(const struct index_state *istate,
* then we know it is.
*/
if ((changed & DATA_CHANGED) &&
- (S_ISGITLINK(ce->ce_mode) || ce->ce_size != 0))
+ (S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
return changed;
changed_fs = ce_modified_check_fs(ce, st);
@@ -1324,16 +1339,16 @@ static struct cache_entry *cache_entry_from_ondisk(struct ondisk_cache_entry *on
{
struct cache_entry *ce = xmalloc(cache_entry_size(len));
- ce->ce_ctime.sec = ntoh_l(ondisk->ctime.sec);
- ce->ce_mtime.sec = ntoh_l(ondisk->mtime.sec);
- ce->ce_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
- ce->ce_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
- ce->ce_dev = ntoh_l(ondisk->dev);
- ce->ce_ino = ntoh_l(ondisk->ino);
+ ce->ce_stat_data.sd_ctime.sec = ntoh_l(ondisk->ctime.sec);
+ ce->ce_stat_data.sd_mtime.sec = ntoh_l(ondisk->mtime.sec);
+ ce->ce_stat_data.sd_ctime.nsec = ntoh_l(ondisk->ctime.nsec);
+ ce->ce_stat_data.sd_mtime.nsec = ntoh_l(ondisk->mtime.nsec);
+ ce->ce_stat_data.sd_dev = ntoh_l(ondisk->dev);
+ ce->ce_stat_data.sd_ino = ntoh_l(ondisk->ino);
ce->ce_mode = ntoh_l(ondisk->mode);
- ce->ce_uid = ntoh_l(ondisk->uid);
- ce->ce_gid = ntoh_l(ondisk->gid);
- ce->ce_size = ntoh_l(ondisk->size);
+ ce->ce_stat_data.sd_uid = ntoh_l(ondisk->uid);
+ ce->ce_stat_data.sd_gid = ntoh_l(ondisk->gid);
+ ce->ce_stat_data.sd_size = ntoh_l(ondisk->size);
ce->ce_flags = flags & ~CE_NAMEMASK;
ce->ce_namelen = len;
hashcpy(ce->sha1, ondisk->sha1);
@@ -1611,7 +1626,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
* The only thing we care about in this function is to smudge the
* falsely clean entry due to touch-update-touch race, so we leave
* everything else as they are. We are called for entries whose
- * ce_mtime match the index file mtime.
+ * ce_stat_data.sd_mtime match the index file mtime.
*
* Note that this actually does not do much for gitlinks, for
* which ce_match_stat_basic() always goes to the actual
@@ -1650,7 +1665,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
* file, and never calls us, so the cached size information
* for "frotz" stays 6 which does not match the filesystem.
*/
- ce->ce_size = 0;
+ ce->ce_stat_data.sd_size = 0;
}
}
@@ -1660,16 +1675,16 @@ static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
{
short flags;
- ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
- ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
- ondisk->ctime.nsec = htonl(ce->ce_ctime.nsec);
- ondisk->mtime.nsec = htonl(ce->ce_mtime.nsec);
- ondisk->dev = htonl(ce->ce_dev);
- ondisk->ino = htonl(ce->ce_ino);
+ ondisk->ctime.sec = htonl(ce->ce_stat_data.sd_ctime.sec);
+ ondisk->mtime.sec = htonl(ce->ce_stat_data.sd_mtime.sec);
+ ondisk->ctime.nsec = htonl(ce->ce_stat_data.sd_ctime.nsec);
+ ondisk->mtime.nsec = htonl(ce->ce_stat_data.sd_mtime.nsec);
+ ondisk->dev = htonl(ce->ce_stat_data.sd_dev);
+ ondisk->ino = htonl(ce->ce_stat_data.sd_ino);
ondisk->mode = htonl(ce->ce_mode);
- ondisk->uid = htonl(ce->ce_uid);
- ondisk->gid = htonl(ce->ce_gid);
- ondisk->size = htonl(ce->ce_size);
+ ondisk->uid = htonl(ce->ce_stat_data.sd_uid);
+ ondisk->gid = htonl(ce->ce_stat_data.sd_gid);
+ ondisk->size = htonl(ce->ce_stat_data.sd_size);
hashcpy(ondisk->sha1, ce->sha1);
flags = ce->ce_flags;
@@ -1936,3 +1951,33 @@ void *read_blob_data_from_index(struct index_state *istate, const char *path, un
*size = sz;
return data;
}
+
+void stat_validity_clear(struct stat_validity *sv)
+{
+ free(sv->sd);
+ sv->sd = NULL;
+}
+
+int stat_validity_check(struct stat_validity *sv, const char *path)
+{
+ struct stat st;
+
+ if (stat(path, &st) < 0)
+ return sv->sd == NULL;
+ if (!sv->sd)
+ return 0;
+ return S_ISREG(st.st_mode) && !match_stat_data(sv->sd, &st);
+}
+
+void stat_validity_update(struct stat_validity *sv, int fd)
+{
+ struct stat st;
+
+ if (fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
+ stat_validity_clear(sv);
+ else {
+ if (!sv->sd)
+ sv->sd = xcalloc(1, sizeof(struct stat_data));
+ fill_stat_data(sv->sd, &st);
+ }
+}
diff --git a/refs.c b/refs.c
index 42a7e17f6b..4302206649 100644
--- a/refs.c
+++ b/refs.c
@@ -750,6 +750,21 @@ static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
}
/*
+ * Load all of the refs from the dir into our in-memory cache. The hard work
+ * of loading loose refs is done by get_ref_dir(), so we just need to recurse
+ * through all of the sub-directories. We do not even need to care about
+ * sorting, as traversal order does not matter to us.
+ */
+static void prime_ref_dir(struct ref_dir *dir)
+{
+ int i;
+ for (i = 0; i < dir->nr; i++) {
+ struct ref_entry *entry = dir->entries[i];
+ if (entry->flag & REF_DIR)
+ prime_ref_dir(get_ref_dir(entry));
+ }
+}
+/*
* Return true iff refname1 and refname2 conflict with each other.
* Two reference names conflict if one of them exactly matches the
* leading components of the other; e.g., "foo/bar" conflicts with
@@ -806,6 +821,30 @@ static int is_refname_available(const char *refname, const char *oldrefname,
return 1;
}
+struct packed_ref_cache {
+ struct ref_entry *root;
+
+ /*
+ * Count of references to the data structure in this instance,
+ * including the pointer from ref_cache::packed if any. The
+ * data will not be freed as long as the reference count is
+ * nonzero.
+ */
+ unsigned int referrers;
+
+ /*
+ * Iff the packed-refs file associated with this instance is
+ * currently locked for writing, this points at the associated
+ * lock (which is owned by somebody else). The referrer count
+ * is also incremented when the file is locked and decremented
+ * when it is unlocked.
+ */
+ struct lock_file *lock;
+
+ /* The metadata from when this packed-refs cache was read */
+ struct stat_validity validity;
+};
+
/*
* Future: need to be in "struct repository"
* when doing a full libification.
@@ -813,7 +852,7 @@ static int is_refname_available(const char *refname, const char *oldrefname,
static struct ref_cache {
struct ref_cache *next;
struct ref_entry *loose;
- struct ref_entry *packed;
+ struct packed_ref_cache *packed;
/*
* The submodule name, or "" for the main repo. We allocate
* length 1 rather than FLEX_ARRAY so that the main ref_cache
@@ -822,11 +861,42 @@ static struct ref_cache {
char name[1];
} ref_cache, *submodule_ref_caches;
+/* Lock used for the main packed-refs file: */
+static struct lock_file packlock;
+
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+ packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs. If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+ if (!--packed_refs->referrers) {
+ free_ref_entry(packed_refs->root);
+ stat_validity_clear(&packed_refs->validity);
+ free(packed_refs);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
static void clear_packed_ref_cache(struct ref_cache *refs)
{
if (refs->packed) {
- free_ref_entry(refs->packed);
+ struct packed_ref_cache *packed_refs = refs->packed;
+
+ if (packed_refs->lock)
+ die("internal error: packed-ref cache cleared while locked");
refs->packed = NULL;
+ release_packed_ref_cache(packed_refs);
}
}
@@ -996,29 +1066,57 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
}
}
-static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+/*
+ * Get the packed_ref_cache for the specified ref_cache, creating it
+ * if necessary.
+ */
+static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
{
+ const char *packed_refs_file;
+
+ if (*refs->name)
+ packed_refs_file = git_path_submodule(refs->name, "packed-refs");
+ else
+ packed_refs_file = git_path("packed-refs");
+
+ if (refs->packed &&
+ !stat_validity_check(&refs->packed->validity, packed_refs_file))
+ clear_packed_ref_cache(refs);
+
if (!refs->packed) {
- const char *packed_refs_file;
FILE *f;
- refs->packed = create_dir_entry(refs, "", 0, 0);
- if (*refs->name)
- packed_refs_file = git_path_submodule(refs->name, "packed-refs");
- else
- packed_refs_file = git_path("packed-refs");
+ refs->packed = xcalloc(1, sizeof(*refs->packed));
+ acquire_packed_ref_cache(refs->packed);
+ refs->packed->root = create_dir_entry(refs, "", 0, 0);
f = fopen(packed_refs_file, "r");
if (f) {
- read_packed_refs(f, get_ref_dir(refs->packed));
+ stat_validity_update(&refs->packed->validity, fileno(f));
+ read_packed_refs(f, get_ref_dir(refs->packed->root));
fclose(f);
}
}
- return get_ref_dir(refs->packed);
+ return refs->packed;
+}
+
+static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+{
+ return get_ref_dir(packed_ref_cache->root);
+}
+
+static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+{
+ return get_packed_ref_dir(get_packed_ref_cache(refs));
}
void add_packed_ref(const char *refname, const unsigned char *sha1)
{
- add_ref(get_packed_refs(&ref_cache),
+ struct packed_ref_cache *packed_ref_cache =
+ get_packed_ref_cache(&ref_cache);
+
+ if (!packed_ref_cache->lock)
+ die("internal error: packed refs not locked");
+ add_ref(get_packed_ref_dir(packed_ref_cache),
create_ref_entry(refname, sha1, REF_ISPACKED, 1));
}
@@ -1197,6 +1295,37 @@ static struct ref_entry *get_packed_ref(const char *refname)
return find_ref(get_packed_refs(&ref_cache), refname);
}
+/*
+ * A loose ref file doesn't exist; check for a packed ref. The
+ * options are forwarded from resolve_safe_unsafe().
+ */
+static const char *handle_missing_loose_ref(const char *refname,
+ unsigned char *sha1,
+ int reading,
+ int *flag)
+{
+ struct ref_entry *entry;
+
+ /*
+ * The loose reference file does not exist; check for a packed
+ * reference.
+ */
+ entry = get_packed_ref(refname);
+ if (entry) {
+ hashcpy(sha1, entry->u.value.sha1);
+ if (flag)
+ *flag |= REF_ISPACKED;
+ return refname;
+ }
+ /* The reference is not a packed reference, either. */
+ if (reading) {
+ return NULL;
+ } else {
+ hashclr(sha1);
+ return refname;
+ }
+}
+
const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH;
@@ -1221,36 +1350,34 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
git_snpath(path, sizeof(path), "%s", refname);
+ /*
+ * We might have to loop back here to avoid a race
+ * condition: first we lstat() the file, then we try
+ * to read it as a link or as a file. But if somebody
+ * changes the type of the file (file <-> directory
+ * <-> symlink) between the lstat() and reading, then
+ * we don't want to report that as an error but rather
+ * try again starting with the lstat().
+ */
+ stat_ref:
if (lstat(path, &st) < 0) {
- struct ref_entry *entry;
-
- if (errno != ENOENT)
+ if (errno == ENOENT)
+ return handle_missing_loose_ref(refname, sha1,
+ reading, flag);
+ else
return NULL;
- /*
- * The loose reference file does not exist;
- * check for a packed reference.
- */
- entry = get_packed_ref(refname);
- if (entry) {
- hashcpy(sha1, entry->u.value.sha1);
- if (flag)
- *flag |= REF_ISPACKED;
- return refname;
- }
- /* The reference is not a packed reference, either. */
- if (reading) {
- return NULL;
- } else {
- hashclr(sha1);
- return refname;
- }
}
/* Follow "normalized" - ie "refs/.." symlinks by hand */
if (S_ISLNK(st.st_mode)) {
len = readlink(path, buffer, sizeof(buffer)-1);
- if (len < 0)
- return NULL;
+ if (len < 0) {
+ if (errno == ENOENT || errno == EINVAL)
+ /* inconsistent with lstat; retry */
+ goto stat_ref;
+ else
+ return NULL;
+ }
buffer[len] = 0;
if (!prefixcmp(buffer, "refs/") &&
!check_refname_format(buffer, 0)) {
@@ -1273,8 +1400,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
* a ref
*/
fd = open(path, O_RDONLY);
- if (fd < 0)
- return NULL;
+ if (fd < 0) {
+ if (errno == ENOENT)
+ /* inconsistent with lstat; retry */
+ goto stat_ref;
+ else
+ return NULL;
+ }
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0)
@@ -1286,8 +1418,19 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
/*
* Is it a symbolic ref?
*/
- if (prefixcmp(buffer, "ref:"))
- break;
+ if (prefixcmp(buffer, "ref:")) {
+ /*
+ * Please note that FETCH_HEAD has a second
+ * line containing other data.
+ */
+ if (get_sha1_hex(buffer, sha1) ||
+ (buffer[40] != '\0' && !isspace(buffer[40]))) {
+ if (flag)
+ *flag |= REF_ISBROKEN;
+ return NULL;
+ }
+ return refname;
+ }
if (flag)
*flag |= REF_ISSYMREF;
buf = buffer + 4;
@@ -1300,13 +1443,6 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
}
refname = strcpy(refname_buffer, buf);
}
- /* Please note that FETCH_HEAD has a second line containing other data. */
- if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
- if (flag)
- *flag |= REF_ISBROKEN;
- return NULL;
- }
- return refname;
}
char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag)
@@ -1520,14 +1656,32 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
static int do_for_each_entry(struct ref_cache *refs, const char *base,
each_ref_entry_fn fn, void *cb_data)
{
- struct ref_dir *packed_dir = get_packed_refs(refs);
- struct ref_dir *loose_dir = get_loose_refs(refs);
+ struct packed_ref_cache *packed_ref_cache;
+ struct ref_dir *loose_dir;
+ struct ref_dir *packed_dir;
int retval = 0;
+ /*
+ * We must make sure that all loose refs are read before accessing the
+ * packed-refs file; this avoids a race condition in which loose refs
+ * are migrated to the packed-refs file by a simultaneous process, but
+ * our in-memory view is from before the migration. get_packed_ref_cache()
+ * takes care of making sure our view is up to date with what is on
+ * disk.
+ */
+ loose_dir = get_loose_refs(refs);
if (base && *base) {
- packed_dir = find_containing_dir(packed_dir, base, 0);
loose_dir = find_containing_dir(loose_dir, base, 0);
}
+ if (loose_dir)
+ prime_ref_dir(loose_dir);
+
+ packed_ref_cache = get_packed_ref_cache(refs);
+ acquire_packed_ref_cache(packed_ref_cache);
+ packed_dir = get_packed_ref_dir(packed_ref_cache);
+ if (base && *base) {
+ packed_dir = find_containing_dir(packed_dir, base, 0);
+ }
if (packed_dir && loose_dir) {
sort_ref_dir(packed_dir);
@@ -1544,6 +1698,7 @@ static int do_for_each_entry(struct ref_cache *refs, const char *base,
loose_dir, 0, fn, cb_data);
}
+ release_packed_ref_cache(packed_ref_cache);
return retval;
}
@@ -1998,6 +2153,73 @@ static void write_packed_entry(int fd, char *refname, unsigned char *sha1,
}
}
+/*
+ * An each_ref_entry_fn that writes the entry to a packed-refs file.
+ */
+static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
+{
+ int *fd = cb_data;
+ enum peel_status peel_status = peel_entry(entry, 0);
+
+ if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+ error("internal error: %s is not a valid packed reference!",
+ entry->name);
+ write_packed_entry(*fd, entry->name, entry->u.value.sha1,
+ peel_status == PEEL_PEELED ?
+ entry->u.value.peeled : NULL);
+ return 0;
+}
+
+int lock_packed_refs(int flags)
+{
+ struct packed_ref_cache *packed_ref_cache;
+
+ /* Discard the old cache because it might be invalid: */
+ clear_packed_ref_cache(&ref_cache);
+ if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
+ return -1;
+ /* Read the current packed-refs while holding the lock: */
+ packed_ref_cache = get_packed_ref_cache(&ref_cache);
+ packed_ref_cache->lock = &packlock;
+ /* Increment the reference count to prevent it from being freed: */
+ acquire_packed_ref_cache(packed_ref_cache);
+ return 0;
+}
+
+int commit_packed_refs(void)
+{
+ struct packed_ref_cache *packed_ref_cache =
+ get_packed_ref_cache(&ref_cache);
+ int error = 0;
+
+ if (!packed_ref_cache->lock)
+ die("internal error: packed-refs not locked");
+ write_or_die(packed_ref_cache->lock->fd,
+ PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+
+ do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
+ 0, write_packed_entry_fn,
+ &packed_ref_cache->lock->fd);
+ if (commit_lock_file(packed_ref_cache->lock))
+ error = -1;
+ packed_ref_cache->lock = NULL;
+ release_packed_ref_cache(packed_ref_cache);
+ return error;
+}
+
+void rollback_packed_refs(void)
+{
+ struct packed_ref_cache *packed_ref_cache =
+ get_packed_ref_cache(&ref_cache);
+
+ if (!packed_ref_cache->lock)
+ die("internal error: packed-refs not locked");
+ rollback_lock_file(packed_ref_cache->lock);
+ packed_ref_cache->lock = NULL;
+ release_packed_ref_cache(packed_ref_cache);
+ clear_packed_ref_cache(&ref_cache);
+}
+
struct ref_to_prune {
struct ref_to_prune *next;
unsigned char sha1[20];
@@ -2006,35 +2228,50 @@ struct ref_to_prune {
struct pack_refs_cb_data {
unsigned int flags;
+ struct ref_dir *packed_refs;
struct ref_to_prune *ref_to_prune;
- int fd;
};
-static int pack_one_ref(struct ref_entry *entry, void *cb_data)
+/*
+ * An each_ref_entry_fn that is run over loose references only. If
+ * the loose reference can be packed, add an entry in the packed ref
+ * cache. If the reference should be pruned, also add it to
+ * ref_to_prune in the pack_refs_cb_data.
+ */
+static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
{
struct pack_refs_cb_data *cb = cb_data;
enum peel_status peel_status;
+ struct ref_entry *packed_entry;
int is_tag_ref = !prefixcmp(entry->name, "refs/tags/");
- /* ALWAYS pack refs that were already packed or are tags */
- if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref &&
- !(entry->flag & REF_ISPACKED))
+ /* ALWAYS pack tags */
+ if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
return 0;
/* Do not pack symbolic or broken refs: */
if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry))
return 0;
+ /* Add a packed ref cache entry equivalent to the loose entry. */
peel_status = peel_entry(entry, 1);
if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
die("internal error peeling reference %s (%s)",
entry->name, sha1_to_hex(entry->u.value.sha1));
- write_packed_entry(cb->fd, entry->name, entry->u.value.sha1,
- peel_status == PEEL_PEELED ?
- entry->u.value.peeled : NULL);
+ packed_entry = find_ref(cb->packed_refs, entry->name);
+ if (packed_entry) {
+ /* Overwrite existing packed entry with info from loose entry */
+ packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
+ hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+ } else {
+ packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+ REF_ISPACKED | REF_KNOWS_PEELED, 0);
+ add_ref(cb->packed_refs, packed_entry);
+ }
+ hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
- /* If the ref was already packed, there is no need to prune it. */
- if ((cb->flags & PACK_REFS_PRUNE) && !(entry->flag & REF_ISPACKED)) {
+ /* Schedule the loose reference for pruning if requested. */
+ if ((cb->flags & PACK_REFS_PRUNE)) {
int namelen = strlen(entry->name) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
hashcpy(n->sha1, entry->u.value.sha1);
@@ -2096,8 +2333,6 @@ static void prune_refs(struct ref_to_prune *r)
}
}
-static struct lock_file packlock;
-
int pack_refs(unsigned int flags)
{
struct pack_refs_cb_data cbdata;
@@ -2105,26 +2340,38 @@ int pack_refs(unsigned int flags)
memset(&cbdata, 0, sizeof(cbdata));
cbdata.flags = flags;
- cbdata.fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"),
- LOCK_DIE_ON_ERROR);
+ lock_packed_refs(LOCK_DIE_ON_ERROR);
+ cbdata.packed_refs = get_packed_refs(&ref_cache);
- write_or_die(cbdata.fd, PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
+ do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
+ pack_if_possible_fn, &cbdata);
- do_for_each_entry(&ref_cache, "", pack_one_ref, &cbdata);
- if (commit_lock_file(&packlock) < 0)
+ if (commit_packed_refs())
die_errno("unable to overwrite old ref-pack file");
+
prune_refs(cbdata.ref_to_prune);
return 0;
}
-static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
+/*
+ * If entry is no longer needed in packed-refs, add it to the string
+ * list pointed to by cb_data. Reasons for deleting entries:
+ *
+ * - Entry is broken.
+ * - Entry is overridden by a loose ref.
+ * - Entry does not point at a valid object.
+ *
+ * In the first and third cases, also emit an error message because these
+ * are indications of repository corruption.
+ */
+static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
{
- int *fd = cb_data;
- enum peel_status peel_status;
+ struct string_list *refs_to_delete = cb_data;
if (entry->flag & REF_ISBROKEN) {
/* This shouldn't happen to packed refs. */
error("%s is broken!", entry->name);
+ string_list_append(refs_to_delete, entry->name);
return 0;
}
if (!has_sha1_file(entry->u.value.sha1)) {
@@ -2134,7 +2381,7 @@ static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
if (read_ref_full(entry->name, sha1, 0, &flags))
/* We should at least have found the packed ref. */
die("Internal error");
- if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED))
+ if ((flags & REF_ISSYMREF) || !(flags & REF_ISPACKED)) {
/*
* This packed reference is overridden by a
* loose reference, so it is OK that its value
@@ -2143,9 +2390,11 @@ static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
* collected. For this purpose we don't even
* care whether the loose reference itself is
* invalid, broken, symbolic, etc. Silently
- * omit the packed reference from the output.
+ * remove the packed reference.
*/
+ string_list_append(refs_to_delete, entry->name);
return 0;
+ }
/*
* There is no overriding loose reference, so the fact
* that this reference doesn't refer to a valid object
@@ -2154,44 +2403,47 @@ static int repack_ref_fn(struct ref_entry *entry, void *cb_data)
* the output.
*/
error("%s does not point to a valid object!", entry->name);
+ string_list_append(refs_to_delete, entry->name);
return 0;
}
- peel_status = peel_entry(entry, 0);
- write_packed_entry(*fd, entry->name, entry->u.value.sha1,
- peel_status == PEEL_PEELED ?
- entry->u.value.peeled : NULL);
-
return 0;
}
static int repack_without_ref(const char *refname)
{
- int fd;
struct ref_dir *packed;
+ struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
+ struct string_list_item *ref_to_delete;
if (!get_packed_ref(refname))
return 0; /* refname does not exist in packed refs */
- fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0);
- if (fd < 0) {
+ if (lock_packed_refs(0)) {
unable_to_lock_error(git_path("packed-refs"), errno);
return error("cannot delete '%s' from packed refs", refname);
}
- clear_packed_ref_cache(&ref_cache);
packed = get_packed_refs(&ref_cache);
- /* Remove refname from the cache. */
+
+ /* Remove refname from the cache: */
if (remove_entry(packed, refname) == -1) {
/*
* The packed entry disappeared while we were
* acquiring the lock.
*/
- rollback_lock_file(&packlock);
+ rollback_packed_refs();
return 0;
}
- write_or_die(fd, PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER));
- do_for_each_entry_in_dir(packed, 0, repack_ref_fn, &fd);
- return commit_lock_file(&packlock);
+
+ /* Remove any other accumulated cruft: */
+ do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete);
+ for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+ if (remove_entry(packed, ref_to_delete->string) == -1)
+ die("internal error");
+ }
+
+ /* Write what remains: */
+ return commit_packed_refs();
}
int delete_ref(const char *refname, const unsigned char *sha1, int delopt)
diff --git a/refs.h b/refs.h
index 246bf6096d..9e5db3ae26 100644
--- a/refs.h
+++ b/refs.h
@@ -77,12 +77,34 @@ extern int for_each_rawref(each_ref_fn, void *);
extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
/*
- * Add a reference to the in-memory packed reference cache. To actually
- * write the reference to the packed-refs file, call pack_refs().
+ * Lock the packed-refs file for writing. Flags is passed to
+ * hold_lock_file_for_update(). Return 0 on success.
+ */
+extern int lock_packed_refs(int flags);
+
+/*
+ * Add a reference to the in-memory packed reference cache. This may
+ * only be called while the packed-refs file is locked (see
+ * lock_packed_refs()). To actually write the packed-refs file, call
+ * commit_packed_refs().
*/
extern void add_packed_ref(const char *refname, const unsigned char *sha1);
/*
+ * Write the current version of the packed refs cache from memory to
+ * disk. The packed-refs file must already be locked for writing (see
+ * lock_packed_refs()). Return zero on success.
+ */
+extern int commit_packed_refs(void);
+
+/*
+ * Rollback the lockfile for the packed-refs file, and discard the
+ * in-memory packed reference cache. (The packed-refs file will be
+ * read anew if it is needed again after this function is called.)
+ */
+extern void rollback_packed_refs(void);
+
+/*
* Flags for controlling behaviour of pack_refs()
* PACK_REFS_PRUNE: Prune loose refs after packing
* PACK_REFS_ALL: Pack _all_ refs, not just tags and already packed refs
diff --git a/remote.c b/remote.c
index e71f66de25..6f57830b64 100644
--- a/remote.c
+++ b/remote.c
@@ -276,10 +276,9 @@ static void read_remotes_file(struct remote *remote)
static void read_branches_file(struct remote *remote)
{
- const char *slash = strchr(remote->name, '/');
char *frag;
struct strbuf branch = STRBUF_INIT;
- int n = slash ? slash - remote->name : 1000;
+ int n = 1000;
FILE *f = fopen(git_path("branches/%.*s", n, remote->name), "r");
char *s, *p;
int len;
@@ -299,21 +298,11 @@ static void read_branches_file(struct remote *remote)
while (isspace(p[-1]))
*--p = 0;
len = p - s;
- if (slash)
- len += strlen(slash);
p = xmalloc(len + 1);
strcpy(p, s);
- if (slash)
- strcat(p, slash);
/*
- * With "slash", e.g. "git fetch jgarzik/netdev-2.6" when
- * reading from $GIT_DIR/branches/jgarzik fetches "HEAD" from
- * the partial URL obtained from the branches file plus
- * "/netdev-2.6" and does not store it in any tracking ref.
- * #branch specifier in the file is ignored.
- *
- * Otherwise, the branches file would have URL and optionally
+ * The branches file would have URL and optionally
* #branch specified. The "master" (or specified) branch is
* fetched and stored in the local branch of the same name.
*/
@@ -323,12 +312,8 @@ static void read_branches_file(struct remote *remote)
strbuf_addf(&branch, "refs/heads/%s", frag);
} else
strbuf_addstr(&branch, "refs/heads/master");
- if (!slash) {
- strbuf_addf(&branch, ":refs/heads/%s", remote->name);
- } else {
- strbuf_reset(&branch);
- strbuf_addstr(&branch, "HEAD:");
- }
+
+ strbuf_addf(&branch, ":refs/heads/%s", remote->name);
add_url_alias(remote, p);
add_fetch_refspec(remote, strbuf_detach(&branch, NULL));
/*
diff --git a/revision.c b/revision.c
index f1bb731fd7..2f0142f22e 100644
--- a/revision.c
+++ b/revision.c
@@ -1296,7 +1296,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
DIFF_OPT_SET(&revs->pruning, QUICK);
revs->pruning.add_remove = file_add_remove;
revs->pruning.change = file_change;
- revs->lifo = 1;
+ revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
revs->dense = 1;
revs->prefix = prefix;
revs->max_age = -1;
@@ -1638,7 +1638,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--merge")) {
revs->show_merge = 1;
} else if (!strcmp(arg, "--topo-order")) {
- revs->lifo = 1;
+ revs->sort_order = REV_SORT_IN_GRAPH_ORDER;
revs->topo_order = 1;
} else if (!strcmp(arg, "--simplify-merges")) {
revs->simplify_merges = 1;
@@ -1656,7 +1656,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->prune = 1;
load_ref_decorations(DECORATE_SHORT_REFS);
} else if (!strcmp(arg, "--date-order")) {
- revs->lifo = 0;
+ revs->sort_order = REV_SORT_BY_COMMIT_DATE;
+ revs->topo_order = 1;
+ } else if (!strcmp(arg, "--author-date-order")) {
+ revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
revs->topo_order = 1;
} else if (!prefixcmp(arg, "--early-output")) {
int count = 100;
@@ -2606,7 +2609,7 @@ int prepare_revision_walk(struct rev_info *revs)
if (limit_list(revs) < 0)
return -1;
if (revs->topo_order)
- sort_in_topological_order(&revs->commits, revs->lifo);
+ sort_in_topological_order(&revs->commits, revs->sort_order);
if (revs->line_level_traverse)
line_log_filter(revs);
if (revs->simplify_merges)
@@ -2924,7 +2927,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
* If revs->topo_order is set, sort the boundary commits
* in topological order
*/
- sort_in_topological_order(&revs->commits, revs->lifo);
+ sort_in_topological_order(&revs->commits, revs->sort_order);
}
static struct commit *get_revision_internal(struct rev_info *revs)
diff --git a/revision.h b/revision.h
index eeea6fba3c..92d6614af6 100644
--- a/revision.h
+++ b/revision.h
@@ -4,6 +4,7 @@
#include "parse-options.h"
#include "grep.h"
#include "notes.h"
+#include "commit.h"
#define SEEN (1u<<0)
#define UNINTERESTING (1u<<1)
@@ -62,6 +63,10 @@ struct rev_info {
const char *prefix;
const char *def;
struct pathspec prune_data;
+
+ /* topo-sort */
+ enum rev_sort_order sort_order;
+
unsigned int early_output:1,
ignore_missing:1;
@@ -72,7 +77,6 @@ struct rev_info {
show_all:1,
remove_empty_trees:1,
simplify_history:1,
- lifo:1,
topo_order:1,
simplify_merges:1,
simplify_by_decoration:1,
diff --git a/t/README b/t/README
index ec52468861..2167125008 100644
--- a/t/README
+++ b/t/README
@@ -76,6 +76,11 @@ appropriately before running "make".
command being run and their output if any are also
output.
+--verbose-only=<pattern>::
+ Like --verbose, but the effect is limited to tests with
+ numbers matching <pattern>. The number matched against is
+ simply the running count of the test within the file.
+
--debug::
This may help the person who is developing a new test.
It causes the command defined with test_debug to run.
@@ -121,6 +126,11 @@ appropriately before running "make".
the 't/valgrind/' directory and use the commands under
't/valgrind/bin/'.
+--valgrind-only=<pattern>::
+ Like --valgrind, but the effect is limited to tests with
+ numbers matching <pattern>. The number matched against is
+ simply the running count of the test within the file.
+
--tee::
In addition to printing the test output to the terminal,
write it to files named 't/test-results/$TEST_NAME.out'.
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 4b74ae460b..8ff87fb3f9 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -17,53 +17,67 @@
# ("squash", "fixup", "edit", or "reword") and the SHA1 taken
# from the specified line.
#
+# "exec_cmd_with_args" -- add an "exec cmd with args" line.
+#
# "#" -- Add a comment line.
#
# ">" -- Add a blank line.
set_fake_editor () {
- echo "#!$SHELL_PATH" >fake-editor.sh
- cat >> fake-editor.sh <<\EOF
-case "$1" in
-*/COMMIT_EDITMSG)
- test -z "$EXPECT_HEADER_COUNT" ||
- test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+ write_script fake-editor.sh <<-\EOF
+ case "$1" in
+ */COMMIT_EDITMSG)
+ test -z "$EXPECT_HEADER_COUNT" ||
+ test "$EXPECT_HEADER_COUNT" = "$(sed -n '1s/^# This is a combination of \(.*\) commits\./\1/p' < "$1")" ||
+ exit
+ test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
+ test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
exit
- test -z "$FAKE_COMMIT_MESSAGE" || echo "$FAKE_COMMIT_MESSAGE" > "$1"
- test -z "$FAKE_COMMIT_AMEND" || echo "$FAKE_COMMIT_AMEND" >> "$1"
- exit
- ;;
-esac
-test -z "$EXPECT_COUNT" ||
- test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
- exit
-test -z "$FAKE_LINES" && exit
-grep -v '^#' < "$1" > "$1".tmp
-rm -f "$1"
-echo 'rebase -i script before editing:'
-cat "$1".tmp
-action=pick
-for line in $FAKE_LINES; do
- case $line in
- squash|fixup|edit|reword)
- action="$line";;
- exec*)
- echo "$line" | sed 's/_/ /g' >> "$1";;
- "#")
- echo '# comment' >> "$1";;
- ">")
- echo >> "$1";;
- *)
- sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
- action=pick;;
+ ;;
esac
-done
-echo 'rebase -i script after editing:'
-cat "$1"
-EOF
+ test -z "$EXPECT_COUNT" ||
+ test "$EXPECT_COUNT" = $(sed -e '/^#/d' -e '/^$/d' < "$1" | wc -l) ||
+ exit
+ test -z "$FAKE_LINES" && exit
+ grep -v '^#' < "$1" > "$1".tmp
+ rm -f "$1"
+ echo 'rebase -i script before editing:'
+ cat "$1".tmp
+ action=pick
+ for line in $FAKE_LINES; do
+ case $line in
+ squash|fixup|edit|reword)
+ action="$line";;
+ exec*)
+ echo "$line" | sed 's/_/ /g' >> "$1";;
+ "#")
+ echo '# comment' >> "$1";;
+ ">")
+ echo >> "$1";;
+ *)
+ sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
+ action=pick;;
+ esac
+ done
+ echo 'rebase -i script after editing:'
+ cat "$1"
+ EOF
+
+ test_set_editor "$(pwd)/fake-editor.sh"
+}
+
+# After set_cat_todo_editor, rebase -i will write the todo list (ignoring
+# blank lines and comments) to stdout, and exit failure (so you should run
+# it with test_must_fail). This can be used to verify the expected user
+# experience, for todo list changes that do not affect the outcome of
+# rebase; or as an extra check in addition to checking the outcome.
+set_cat_todo_editor () {
+ write_script fake-editor.sh <<-\EOF
+ grep "^[^#]" "$1"
+ exit 1
+ EOF
test_set_editor "$(pwd)/fake-editor.sh"
- chmod a+x fake-editor.sh
}
# checks that the revisions in "$2" represent a linear range with the
diff --git a/t/lib-t6000.sh b/t/lib-t6000.sh
index ea25dd89e5..4ffd90127e 100644
--- a/t/lib-t6000.sh
+++ b/t/lib-t6000.sh
@@ -1,55 +1,50 @@
: included from 6002 and others
-[ -d .git/refs/tags ] || mkdir -p .git/refs/tags
+mkdir -p .git/refs/tags
-:> sed.script
+>sed.script
-# Answer the sha1 has associated with the tag. The tag must exist in .git or .git/refs/tags
-tag()
-{
+# Answer the sha1 has associated with the tag. The tag must exist in .git/refs/tags
+tag () {
_tag=$1
- [ -f .git/refs/tags/$_tag ] || error "tag: \"$_tag\" does not exist"
- cat .git/refs/tags/$_tag
+ test -f ".git/refs/tags/$_tag" || error "tag: \"$_tag\" does not exist"
+ cat ".git/refs/tags/$_tag"
}
# Generate a commit using the text specified to make it unique and the tree
# named by the tag specified.
-unique_commit()
-{
+unique_commit () {
_text=$1
- _tree=$2
+ _tree=$2
shift 2
- echo $_text | git commit-tree $(tag $_tree) "$@"
+ echo "$_text" | git commit-tree $(tag "$_tree") "$@"
}
# Save the output of a command into the tag specified. Prepend
# a substitution script for the tag onto the front of sed.script
-save_tag()
-{
+save_tag () {
_tag=$1
- [ -n "$_tag" ] || error "usage: save_tag tag commit-args ..."
+ test -n "$_tag" || error "usage: save_tag tag commit-args ..."
shift 1
- "$@" >.git/refs/tags/$_tag
+ "$@" >".git/refs/tags/$_tag"
- echo "s/$(tag $_tag)/$_tag/g" > sed.script.tmp
- cat sed.script >> sed.script.tmp
+ echo "s/$(tag $_tag)/$_tag/g" >sed.script.tmp
+ cat sed.script >>sed.script.tmp
rm sed.script
mv sed.script.tmp sed.script
}
# Replace unhelpful sha1 hashses with their symbolic equivalents
-entag()
-{
+entag () {
sed -f sed.script
}
# Execute a command after first saving, then setting the GIT_AUTHOR_EMAIL
# tag to a specified value. Restore the original value on return.
-as_author()
-{
+as_author () {
_author=$1
shift 1
- _save=$GIT_AUTHOR_EMAIL
+ _save=$GIT_AUTHOR_EMAIL
GIT_AUTHOR_EMAIL="$_author"
export GIT_AUTHOR_EMAIL
@@ -63,45 +58,58 @@ as_author()
fi
}
-commit_date()
-{
- _commit=$1
- git cat-file commit $_commit | sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
+commit_date () {
+ _commit=$1
+ git cat-file commit $_commit |
+ sed -n "s/^committer .*> \([0-9]*\) .*/\1/p"
}
-on_committer_date()
-{
- _date=$1
- shift 1
- GIT_COMMITTER_DATE="$_date"
- export GIT_COMMITTER_DATE
- "$@"
- unset GIT_COMMITTER_DATE
+# Assign the value of fake date to a variable, but
+# allow fairly common "1971-08-16 00:00" to be omittd
+assign_fake_date () {
+ case "$2" in
+ ??:??:??) eval "$1='1971-08-16 $2'" ;;
+ ??:??) eval "$1='1971-08-16 00:$2'" ;;
+ ??) eval "$1='1971-08-16 00:00:$2'" ;;
+ *) eval "$1='$2'" ;;
+ esac
+}
+
+on_committer_date () {
+ assign_fake_date GIT_COMMITTER_DATE "$1"
+ export GIT_COMMITTER_DATE
+ shift 1
+ "$@"
+}
+
+on_dates () {
+ assign_fake_date GIT_COMMITTER_DATE "$1"
+ assign_fake_date GIT_AUTHOR_DATE "$2"
+ export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+ shift 2
+ "$@"
}
# Execute a command and suppress any error output.
-hide_error()
-{
+hide_error () {
"$@" 2>/dev/null
}
-check_output()
-{
+check_output () {
_name=$1
shift 1
- if eval "$*" | entag > $_name.actual
+ if eval "$*" | entag >"$_name.actual"
then
- test_cmp $_name.expected $_name.actual
+ test_cmp "$_name.expected" "$_name.actual"
else
- return 1;
+ return 1
fi
}
# Turn a reasonable test description into a reasonable test name.
# All alphanums translated into -'s which are then compressed and stripped
# from front and back.
-name_from_description()
-{
+name_from_description () {
perl -pe '
s/[^A-Za-z0-9.]/-/g;
s/-+/-/g;
@@ -119,9 +127,11 @@ name_from_description()
test_output_expect_success()
{
_description=$1
- _test=$2
- [ $# -eq 2 ] || error "usage: test_output_expect_success description test <<EOF ... EOF"
- _name=$(echo $_description | name_from_description)
- cat > $_name.expected
+ _test=$2
+ test $# -eq 2 ||
+ error "usage: test_output_expect_success description test <<EOF ... EOF"
+
+ _name=$(echo $_description | name_from_description)
+ cat >"$_name.expected"
test_expect_success "$_description" "check_output $_name \"$_test\""
}
diff --git a/t/perf/README b/t/perf/README
index c552f561bf..8848c14619 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -66,7 +66,7 @@ You can set the following variables (also in your config.mak):
GIT_PERF_LARGE_REPO
Repositories to copy for the performance tests. The normal
repo should be at least git.git size. The large repo should
- probably be about linux-2.6.git size for optimal results.
+ probably be about linux.git size for optimal results.
Both default to the git.git you are running from.
You can also pass the options taken by ordinary git tests; the most
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index a816fbcb76..c61d5351e1 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -150,6 +150,7 @@ exit $ret' >&3 2>&4
test_perf () {
+ test_start_
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -187,7 +188,7 @@ test_perf () {
base="$perf_results_dir"/"$perf_results_prefix$(basename "$0" .sh)"."$test_count"
"$TEST_DIRECTORY"/perf/min_time.perl test_time.* >"$base".times
fi
- echo >&3 ""
+ test_finish_
}
# We extend test_done to print timings at the end (./run disables this
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 0f1318056c..5c32288380 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -47,8 +47,13 @@ test_expect_failure 'pretend we have a known breakage' '
run_sub_test_lib_test () {
name="$1" descr="$2" # stdin is the body of the test code
+ shift 2
mkdir "$name" &&
(
+ # Pretend we're a test harness. This prevents
+ # test-lib from writing the counts to a file that will
+ # later be summarized, showing spurious "failed" tests
+ export HARNESS_ACTIVE=t &&
cd "$name" &&
cat >"$name.sh" <<-EOF &&
#!$SHELL_PATH
@@ -65,7 +70,7 @@ run_sub_test_lib_test () {
cat >>"$name.sh" &&
chmod +x "$name.sh" &&
export TEST_DIRECTORY &&
- ./"$name.sh" >out 2>err
+ ./"$name.sh" "$@" >out 2>err
)
}
@@ -215,6 +220,60 @@ test_expect_success 'pretend we have a mix of all possible results' "
EOF
"
+test_expect_success 'test --verbose' '
+ test_must_fail run_sub_test_lib_test \
+ test-verbose "test verbose" --verbose <<-\EOF &&
+ test_expect_success "passing test" true
+ test_expect_success "test with output" "echo foo"
+ test_expect_success "failing test" false
+ test_done
+ EOF
+ mv test-verbose/out test-verbose/out+
+ grep -v "^Initialized empty" test-verbose/out+ >test-verbose/out &&
+ check_sub_test_lib_test test-verbose <<-\EOF
+ > expecting success: true
+ > Z
+ > ok 1 - passing test
+ > Z
+ > expecting success: echo foo
+ > foo
+ > Z
+ > ok 2 - test with output
+ > Z
+ > expecting success: false
+ > Z
+ > not ok 3 - failing test
+ > # false
+ > Z
+ > # failed 1 among 3 test(s)
+ > 1..3
+ EOF
+'
+
+test_expect_success 'test --verbose-only' '
+ test_must_fail run_sub_test_lib_test \
+ test-verbose-only-2 "test verbose-only=2" \
+ --verbose-only=2 <<-\EOF &&
+ test_expect_success "passing test" true
+ test_expect_success "test with output" "echo foo"
+ test_expect_success "failing test" false
+ test_done
+ EOF
+ check_sub_test_lib_test test-verbose-only-2 <<-\EOF
+ > ok 1 - passing test
+ > Z
+ > expecting success: echo foo
+ > foo
+ > Z
+ > ok 2 - test with output
+ > Z
+ > not ok 3 - failing test
+ > # false
+ > # failed 1 among 3 test(s)
+ > 1..3
+ EOF
+'
+
test_set_prereq HAVEIT
haveit=no
test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
diff --git a/t/t0009-prio-queue.sh b/t/t0009-prio-queue.sh
new file mode 100755
index 0000000000..94045c3fad
--- /dev/null
+++ b/t/t0009-prio-queue.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+test_description='basic tests for priority queue implementation'
+. ./test-lib.sh
+
+cat >expect <<'EOF'
+1
+2
+3
+4
+5
+5
+6
+7
+8
+9
+10
+EOF
+test_expect_success 'basic ordering' '
+ test-prio-queue 2 6 3 10 9 5 7 4 5 8 1 dump >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+2
+3
+4
+1
+5
+6
+EOF
+test_expect_success 'mixed put and get' '
+ test-prio-queue 6 2 4 get 5 3 get get 1 dump >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+1
+2
+NULL
+1
+2
+NULL
+EOF
+test_expect_success 'notice empty queue' '
+ test-prio-queue 1 2 get get get 1 2 get get get >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh
index 1a8f44c44c..e526184a0b 100755
--- a/t/t0020-crlf.sh
+++ b/t/t0020-crlf.sh
@@ -81,6 +81,14 @@ test_expect_success 'safecrlf: print warning only once' '
test $(git add doublewarn 2>&1 | grep "CRLF will be replaced by LF" | wc -l) = 1
'
+
+test_expect_success 'safecrlf: git diff demotes safecrlf=true to warn' '
+ git config core.autocrlf input &&
+ git config core.safecrlf true &&
+ git diff HEAD
+'
+
+
test_expect_success 'switch off autocrlf, safecrlf, reset HEAD' '
git config core.autocrlf false &&
git config core.safecrlf false &&
diff --git a/t/t1513-rev-parse-prefix.sh b/t/t1513-rev-parse-prefix.sh
new file mode 100755
index 0000000000..87ec3ae714
--- /dev/null
+++ b/t/t1513-rev-parse-prefix.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='Tests for rev-parse --prefix'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ mkdir -p sub1/sub2 &&
+ echo top >top &&
+ echo file1 >sub1/file1 &&
+ echo file2 >sub1/sub2/file2 &&
+ git add top sub1/file1 sub1/sub2/file2 &&
+ git commit -m commit
+'
+
+test_expect_success 'empty prefix -- file' '
+ git rev-parse --prefix "" -- top sub1/file1 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ top
+ sub1/file1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- file' '
+ git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ sub1/file1
+ sub1/sub2/file2
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix -- ../file' '
+ git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ sub1/../top
+ sub1/sub2/file2
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'empty prefix HEAD:./path' '
+ git rev-parse --prefix "" HEAD:./top >actual &&
+ git rev-parse HEAD:top >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:./path' '
+ git rev-parse --prefix sub1/ HEAD:./file1 >actual &&
+ git rev-parse HEAD:sub1/file1 >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'valid prefix HEAD:../path' '
+ git rev-parse --prefix sub1/ HEAD:../top >actual &&
+ git rev-parse HEAD:top >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'prefix ignored with HEAD:top' '
+ git rev-parse --prefix sub1/ HEAD:top >actual &&
+ git rev-parse HEAD:top >expected &&
+ test_cmp expected actual
+'
+
+test_expect_success 'disambiguate path with valid prefix' '
+ git rev-parse --prefix sub1/ file1 >actual &&
+ cat <<-\EOF >expected &&
+ sub1/file1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'file and refs with prefix' '
+ git rev-parse --prefix sub1/ master file1 >actual &&
+ cat <<-EOF >expected &&
+ $(git rev-parse master)
+ sub1/file1
+ EOF
+ test_cmp expected actual
+'
+
+test_expect_success 'two-levels deep' '
+ git rev-parse --prefix sub1/sub2/ -- file2 >actual &&
+ cat <<-\EOF >expected &&
+ --
+ sub1/sub2/file2
+ EOF
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t2012-checkout-last.sh b/t/t2012-checkout-last.sh
index b44de9dc62..e7ba8c505f 100755
--- a/t/t2012-checkout-last.sh
+++ b/t/t2012-checkout-last.sh
@@ -116,4 +116,38 @@ test_expect_success 'master...' '
test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
'
+test_expect_success '"checkout -" works after a rebase A' '
+ git checkout master &&
+ git checkout other &&
+ git rebase master &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase A B' '
+ git branch moodle master~1 &&
+ git checkout master &&
+ git checkout other &&
+ git rebase master moodle &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A' '
+ git checkout master &&
+ git checkout other &&
+ git rebase -i master &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
+test_expect_success '"checkout -" works after a rebase -i A B' '
+ git branch foodle master~1 &&
+ git checkout master &&
+ git checkout other &&
+ git rebase master foodle &&
+ git checkout - &&
+ test "z$(git symbolic-ref HEAD)" = "zrefs/heads/master"
+'
+
test_done
diff --git a/t/t3415-rebase-autosquash.sh b/t/t3415-rebase-autosquash.sh
index a1e86c4097..41370ab998 100755
--- a/t/t3415-rebase-autosquash.sh
+++ b/t/t3415-rebase-autosquash.sh
@@ -4,6 +4,8 @@ test_description='auto squash'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
test_expect_success setup '
echo 0 >file0 &&
git add . &&
@@ -193,4 +195,59 @@ test_expect_success 'use commit --squash' '
test_auto_commit_flags squash 2
'
+test_auto_fixup_fixup () {
+ git reset --hard base &&
+ echo 1 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "$1! first" &&
+ echo 2 >file1 &&
+ git add -u &&
+ test_tick &&
+ git commit -m "$1! $2! first" &&
+ git tag "final-$1-$2" &&
+ test_tick &&
+ (
+ set_cat_todo_editor &&
+ test_must_fail git rebase --autosquash -i HEAD^^^^ >actual &&
+ cat >expected <<-EOF &&
+ pick $(git rev-parse --short HEAD^^^) first commit
+ $1 $(git rev-parse --short HEAD^) $1! first
+ $1 $(git rev-parse --short HEAD) $1! $2! first
+ pick $(git rev-parse --short HEAD^^) second commit
+ EOF
+ test_cmp expected actual
+ ) &&
+ git rebase --autosquash -i HEAD^^^^ &&
+ git log --oneline >actual &&
+ test_line_count = 3 actual
+ git diff --exit-code "final-$1-$2" &&
+ test 2 = "$(git cat-file blob HEAD^:file1)" &&
+ if test "$1" = "fixup"
+ then
+ test 1 = $(git cat-file commit HEAD^ | grep first | wc -l)
+ elif test "$1" = "squash"
+ then
+ test 3 = $(git cat-file commit HEAD^ | grep first | wc -l)
+ else
+ false
+ fi
+}
+
+test_expect_success 'fixup! fixup!' '
+ test_auto_fixup_fixup fixup fixup
+'
+
+test_expect_success 'fixup! squash!' '
+ test_auto_fixup_fixup fixup squash
+'
+
+test_expect_success 'squash! squash!' '
+ test_auto_fixup_fixup squash squash
+'
+
+test_expect_success 'squash! fixup!' '
+ test_auto_fixup_fixup squash fixup
+'
+
test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index cc3db1304e..3fb4b976a2 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -142,6 +142,351 @@ EOF
git diff --ignore-space-at-eol > out
test_expect_success 'another test, with --ignore-space-at-eol' 'test_cmp expect out'
+test_expect_success 'ignore-blank-lines: only new lines' '
+ test_seq 5 >x &&
+ git update-index x &&
+ test_seq 5 | sed "/3/i \\
+" >x &&
+ git diff --ignore-blank-lines >out &&
+ >expect &&
+ test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: only new lines with space' '
+ test_seq 5 >x &&
+ git update-index x &&
+ test_seq 5 | sed "/3/i \ " >x &&
+ git diff -w --ignore-blank-lines >out &&
+ >expect &&
+ test_cmp out expect
+'
+
+test_expect_success 'ignore-blank-lines: after change' '
+ cat <<-\EOF >x &&
+ 1
+ 2
+
+ 3
+ 4
+ 5
+
+ 6
+ 7
+ EOF
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+
+ 7
+ EOF
+ git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,7 @@
+ +change
+ +
+ 1
+ 2
+ -
+ 3
+ 4
+ 5
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: before change' '
+ cat <<-\EOF >x &&
+ 1
+ 2
+
+ 3
+ 4
+ 5
+ 6
+ 7
+ EOF
+ git update-index x &&
+ cat <<-\EOF >x &&
+
+ 1
+ 2
+ 3
+ 4
+ 5
+
+ 6
+ 7
+ change
+ EOF
+ git diff --inter-hunk-context=100 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -4,5 +4,7 @@
+ 3
+ 4
+ 5
+ +
+ 6
+ 7
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes' '
+ cat <<-\EOF >x &&
+ 1
+ 2
+ 3
+ 4
+ 5
+
+
+ 6
+ 7
+ 8
+ 9
+ 10
+ EOF
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+
+ 9
+ 10
+ change
+ EOF
+ git diff --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,5 +1,7 @@
+ +change
+ 1
+ 2
+ +
+ 3
+ 4
+ 5
+ @@ -8,5 +8,7 @@
+ 6
+ 7
+ 8
+ +
+ 9
+ 10
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: between changes (with interhunkctx)' '
+ test_seq 10 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+
+ 3
+ 4
+ 5
+
+ 6
+ 7
+ 8
+ 9
+
+ 10
+ change
+ EOF
+ git diff --inter-hunk-context=2 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,10 +1,15 @@
+ +change
+ 1
+ 2
+ +
+ 3
+ 4
+ 5
+ +
+ 6
+ 7
+ 8
+ 9
+ +
+ 10
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: scattered spaces' '
+ test_seq 10 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+ 3
+
+ 4
+
+ 5
+
+ 6
+
+
+ 7
+
+ 8
+ 9
+ 10
+ change
+ EOF
+ git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,3 +1,4 @@
+ +change
+ 1
+ 2
+ 3
+ @@ -8,3 +15,4 @@
+ 8
+ 9
+ 10
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: spaces coalesce' '
+ test_seq 6 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+ 3
+
+ 4
+
+ 5
+
+ 6
+ change
+ EOF
+ git diff --inter-hunk-context=4 --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,6 +1,11 @@
+ +change
+ 1
+ 2
+ 3
+ +
+ 4
+ +
+ 5
+ +
+ 6
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
+test_expect_success 'ignore-blank-lines: mix changes and blank lines' '
+ test_seq 16 >x &&
+ git update-index x &&
+ cat <<-\EOF >x &&
+ change
+ 1
+ 2
+
+ 3
+ 4
+ 5
+ change
+ 6
+ 7
+ 8
+
+ 9
+ 10
+ 11
+ change
+ 12
+ 13
+ 14
+
+ 15
+ 16
+ change
+ EOF
+ git diff --ignore-blank-lines >out.tmp &&
+ cat <<-\EOF >expected &&
+ diff --git a/x b/x
+ --- a/x
+ +++ b/x
+ @@ -1,8 +1,11 @@
+ +change
+ 1
+ 2
+ +
+ 3
+ 4
+ 5
+ +change
+ 6
+ 7
+ 8
+ @@ -9,8 +13,11 @@
+ 9
+ 10
+ 11
+ +change
+ 12
+ 13
+ 14
+ +
+ 15
+ 16
+ +change
+ EOF
+ compare_diff_patch expected out.tmp
+'
+
test_expect_success 'check mixed spaces and tabs in indent' '
# This is indented with SP HT SP.
diff --git a/t/t5505-remote.sh b/t/t5505-remote.sh
index dd10ff053c..ee5d65d52f 100755
--- a/t/t5505-remote.sh
+++ b/t/t5505-remote.sh
@@ -42,107 +42,104 @@ check_tracking_branch () {
}
test_expect_success setup '
-
setup_repository one &&
setup_repository two &&
(
- cd two && git branch another
+ cd two &&
+ git branch another
) &&
git clone one test
-
'
test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
-(
- cd test &&
- tokens_match origin "$(git remote)" &&
- check_remote_track origin master side &&
- check_tracking_branch origin HEAD master side
-)
+ (
+ cd test &&
+ tokens_match origin "$(git remote)" &&
+ check_remote_track origin master side &&
+ check_tracking_branch origin HEAD master side
+ )
'
test_expect_success 'add another remote' '
-(
- cd test &&
- git remote add -f second ../two &&
- tokens_match "origin second" "$(git remote)" &&
- check_tracking_branch second master side another &&
- git for-each-ref "--format=%(refname)" refs/remotes |
- sed -e "/^refs\/remotes\/origin\//d" \
- -e "/^refs\/remotes\/second\//d" >actual &&
- >expect &&
- test_cmp expect actual
-)
+ (
+ cd test &&
+ git remote add -f second ../two &&
+ tokens_match "origin second" "$(git remote)" &&
+ check_tracking_branch second master side another &&
+ git for-each-ref "--format=%(refname)" refs/remotes |
+ sed -e "/^refs\/remotes\/origin\//d" \
+ -e "/^refs\/remotes\/second\//d" >actual &&
+ >expect &&
+ test_cmp expect actual
+ )
'
test_expect_success C_LOCALE_OUTPUT 'check remote tracking' '
-(
- cd test &&
- check_remote_track origin master side &&
- check_remote_track second master side another
-)
+ (
+ cd test &&
+ check_remote_track origin master side &&
+ check_remote_track second master side another
+ )
'
test_expect_success 'remote forces tracking branches' '
-(
- cd test &&
- case `git config remote.second.fetch` in
- +*) true ;;
- *) false ;;
- esac
-)
+ (
+ cd test &&
+ case `git config remote.second.fetch` in
+ +*) true ;;
+ *) false ;;
+ esac
+ )
'
test_expect_success 'remove remote' '
-(
- cd test &&
- git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
- git remote rm second
-)
+ (
+ cd test &&
+ git symbolic-ref refs/remotes/second/HEAD refs/remotes/second/master &&
+ git remote rm second
+ )
'
test_expect_success C_LOCALE_OUTPUT 'remove remote' '
-(
- cd test &&
- tokens_match origin "$(git remote)" &&
- check_remote_track origin master side &&
- git for-each-ref "--format=%(refname)" refs/remotes |
- sed -e "/^refs\/remotes\/origin\//d" >actual &&
- >expect &&
- test_cmp expect actual
-)
+ (
+ cd test &&
+ tokens_match origin "$(git remote)" &&
+ check_remote_track origin master side &&
+ git for-each-ref "--format=%(refname)" refs/remotes |
+ sed -e "/^refs\/remotes\/origin\//d" >actual &&
+ >expect &&
+ test_cmp expect actual
+ )
'
test_expect_success 'remove remote protects local branches' '
-(
- cd test &&
- { cat >expect1 <<EOF
-Note: A branch outside the refs/remotes/ hierarchy was not removed;
-to delete it, use:
- git branch -d master
-EOF
- } &&
- { cat >expect2 <<EOF
-Note: Some branches outside the refs/remotes/ hierarchy were not removed;
-to delete them, use:
- git branch -d foobranch
- git branch -d master
-EOF
- } &&
- git tag footag &&
- git config --add remote.oops.fetch "+refs/*:refs/*" &&
- git remote remove oops 2>actual1 &&
- git branch foobranch &&
- git config --add remote.oops.fetch "+refs/*:refs/*" &&
- git remote rm oops 2>actual2 &&
- git branch -d foobranch &&
- git tag -d footag &&
- test_i18ncmp expect1 actual1 &&
- test_i18ncmp expect2 actual2
-)
-'
-
-cat > test/expect << EOF
+ (
+ cd test &&
+ cat >expect1 <<-\EOF &&
+ Note: A branch outside the refs/remotes/ hierarchy was not removed;
+ to delete it, use:
+ git branch -d master
+ EOF
+ cat >expect2 <<-\EOF &&
+ Note: Some branches outside the refs/remotes/ hierarchy were not removed;
+ to delete them, use:
+ git branch -d foobranch
+ git branch -d master
+ EOF
+ git tag footag &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote remove oops 2>actual1 &&
+ git branch foobranch &&
+ git config --add remote.oops.fetch "+refs/*:refs/*" &&
+ git remote rm oops 2>actual2 &&
+ git branch -d foobranch &&
+ git tag -d footag &&
+ test_i18ncmp expect1 actual1 &&
+ test_i18ncmp expect2 actual2
+ )
+'
+
+cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
Push URL: $(pwd)/one
@@ -172,36 +169,40 @@ cat > test/expect << EOF
EOF
test_expect_success 'show' '
- (cd test &&
- git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
- git fetch &&
- git checkout -b ahead origin/master &&
- echo 1 >> file &&
- test_tick &&
- git commit -m update file &&
- git checkout master &&
- git branch --track octopus origin/master &&
- git branch --track rebase origin/master &&
- git branch -d -r origin/master &&
- git config --add remote.two.url ../two &&
- git config --add remote.two.pushurl ../three &&
- git config branch.rebase.rebase true &&
- git config branch.octopus.merge "topic-a topic-b topic-c" &&
- (cd ../one &&
- echo 1 > file &&
- test_tick &&
- git commit -m update file) &&
- git config --add remote.origin.push : &&
- git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
- git config --add remote.origin.push +refs/tags/lastbackup &&
- git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
- git config --add remote.two.push refs/heads/master:refs/heads/another &&
- git remote show origin two > output &&
- git branch -d rebase octopus &&
- test_i18ncmp expect output)
-'
-
-cat > test/expect << EOF
+ (
+ cd test &&
+ git config --add remote.origin.fetch refs/heads/master:refs/heads/upstream &&
+ git fetch &&
+ git checkout -b ahead origin/master &&
+ echo 1 >>file &&
+ test_tick &&
+ git commit -m update file &&
+ git checkout master &&
+ git branch --track octopus origin/master &&
+ git branch --track rebase origin/master &&
+ git branch -d -r origin/master &&
+ git config --add remote.two.url ../two &&
+ git config --add remote.two.pushurl ../three &&
+ git config branch.rebase.rebase true &&
+ git config branch.octopus.merge "topic-a topic-b topic-c" &&
+ (
+ cd ../one &&
+ echo 1 >file &&
+ test_tick &&
+ git commit -m update file
+ ) &&
+ git config --add remote.origin.push : &&
+ git config --add remote.origin.push refs/heads/master:refs/heads/upstream &&
+ git config --add remote.origin.push +refs/tags/lastbackup &&
+ git config --add remote.two.push +refs/heads/ahead:refs/heads/master &&
+ git config --add remote.two.push refs/heads/master:refs/heads/another &&
+ git remote show origin two >output &&
+ git branch -d rebase octopus &&
+ test_i18ncmp expect output
+ )
+'
+
+cat >test/expect <<EOF
* remote origin
Fetch URL: $(pwd)/one
Push URL: $(pwd)/one
@@ -219,152 +220,187 @@ cat > test/expect << EOF
EOF
test_expect_success 'show -n' '
- (mv one one.unreachable &&
- cd test &&
- git remote show -n origin > output &&
- mv ../one.unreachable ../one &&
- test_i18ncmp expect output)
+ mv one one.unreachable &&
+ (
+ cd test &&
+ git remote show -n origin >output &&
+ mv ../one.unreachable ../one &&
+ test_i18ncmp expect output
+ )
'
test_expect_success 'prune' '
- (cd one &&
- git branch -m side side2) &&
- (cd test &&
- git fetch origin &&
- git remote prune origin &&
- git rev-parse refs/remotes/origin/side2 &&
- test_must_fail git rev-parse refs/remotes/origin/side)
+ (
+ cd one &&
+ git branch -m side side2
+ ) &&
+ (
+ cd test &&
+ git fetch origin &&
+ git remote prune origin &&
+ git rev-parse refs/remotes/origin/side2 &&
+ test_must_fail git rev-parse refs/remotes/origin/side
+ )
'
test_expect_success 'set-head --delete' '
- (cd test &&
- git symbolic-ref refs/remotes/origin/HEAD &&
- git remote set-head --delete origin &&
- test_must_fail git symbolic-ref refs/remotes/origin/HEAD)
+ (
+ cd test &&
+ git symbolic-ref refs/remotes/origin/HEAD &&
+ git remote set-head --delete origin &&
+ test_must_fail git symbolic-ref refs/remotes/origin/HEAD
+ )
'
test_expect_success 'set-head --auto' '
- (cd test &&
- git remote set-head --auto origin &&
- echo refs/remotes/origin/master >expect &&
- git symbolic-ref refs/remotes/origin/HEAD >output &&
- test_cmp expect output
+ (
+ cd test &&
+ git remote set-head --auto origin &&
+ echo refs/remotes/origin/master >expect &&
+ git symbolic-ref refs/remotes/origin/HEAD >output &&
+ test_cmp expect output
)
'
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
error: Multiple remote HEAD branches. Please choose one explicitly with:
git remote set-head two another
git remote set-head two master
EOF
test_expect_success 'set-head --auto fails w/multiple HEADs' '
- (cd test &&
- test_must_fail git remote set-head --auto two >output 2>&1 &&
- test_i18ncmp expect output)
+ (
+ cd test &&
+ test_must_fail git remote set-head --auto two >output 2>&1 &&
+ test_i18ncmp expect output
+ )
'
-cat >test/expect <<EOF
+cat >test/expect <<\EOF
refs/remotes/origin/side2
EOF
test_expect_success 'set-head explicit' '
- (cd test &&
- git remote set-head origin side2 &&
- git symbolic-ref refs/remotes/origin/HEAD >output &&
- git remote set-head origin master &&
- test_cmp expect output)
+ (
+ cd test &&
+ git remote set-head origin side2 &&
+ git symbolic-ref refs/remotes/origin/HEAD >output &&
+ git remote set-head origin master &&
+ test_cmp expect output
+ )
'
-cat > test/expect << EOF
+cat >test/expect <<EOF
Pruning origin
URL: $(pwd)/one
* [would prune] origin/side2
EOF
test_expect_success 'prune --dry-run' '
- (cd one &&
- git branch -m side2 side) &&
- (cd test &&
- git remote prune --dry-run origin > output &&
- git rev-parse refs/remotes/origin/side2 &&
- test_must_fail git rev-parse refs/remotes/origin/side &&
- (cd ../one &&
- git branch -m side side2) &&
- test_i18ncmp expect output)
+ (
+ cd one &&
+ git branch -m side2 side) &&
+ (
+ cd test &&
+ git remote prune --dry-run origin >output &&
+ git rev-parse refs/remotes/origin/side2 &&
+ test_must_fail git rev-parse refs/remotes/origin/side &&
+ (
+ cd ../one &&
+ git branch -m side side2) &&
+ test_i18ncmp expect output
+ )
'
test_expect_success 'add --mirror && prune' '
- (mkdir mirror &&
- cd mirror &&
- git init --bare &&
- git remote add --mirror -f origin ../one) &&
- (cd one &&
- git branch -m side2 side) &&
- (cd mirror &&
- git rev-parse --verify refs/heads/side2 &&
- test_must_fail git rev-parse --verify refs/heads/side &&
- git fetch origin &&
- git remote prune origin &&
- test_must_fail git rev-parse --verify refs/heads/side2 &&
- git rev-parse --verify refs/heads/side)
+ mkdir mirror &&
+ (
+ cd mirror &&
+ git init --bare &&
+ git remote add --mirror -f origin ../one
+ ) &&
+ (
+ cd one &&
+ git branch -m side2 side
+ ) &&
+ (
+ cd mirror &&
+ git rev-parse --verify refs/heads/side2 &&
+ test_must_fail git rev-parse --verify refs/heads/side &&
+ git fetch origin &&
+ git remote prune origin &&
+ test_must_fail git rev-parse --verify refs/heads/side2 &&
+ git rev-parse --verify refs/heads/side
+ )
'
test_expect_success 'add --mirror=fetch' '
mkdir mirror-fetch &&
git init mirror-fetch/parent &&
- (cd mirror-fetch/parent &&
- test_commit one) &&
+ (
+ cd mirror-fetch/parent &&
+ test_commit one
+ ) &&
git init --bare mirror-fetch/child &&
- (cd mirror-fetch/child &&
- git remote add --mirror=fetch -f parent ../parent)
+ (
+ cd mirror-fetch/child &&
+ git remote add --mirror=fetch -f parent ../parent
+ )
'
test_expect_success 'fetch mirrors act as mirrors during fetch' '
- (cd mirror-fetch/parent &&
- git branch new &&
- git branch -m master renamed
+ (
+ cd mirror-fetch/parent &&
+ git branch new &&
+ git branch -m master renamed
) &&
- (cd mirror-fetch/child &&
- git fetch parent &&
- git rev-parse --verify refs/heads/new &&
- git rev-parse --verify refs/heads/renamed
+ (
+ cd mirror-fetch/child &&
+ git fetch parent &&
+ git rev-parse --verify refs/heads/new &&
+ git rev-parse --verify refs/heads/renamed
)
'
test_expect_success 'fetch mirrors can prune' '
- (cd mirror-fetch/child &&
- git remote prune parent &&
- test_must_fail git rev-parse --verify refs/heads/master
+ (
+ cd mirror-fetch/child &&
+ git remote prune parent &&
+ test_must_fail git rev-parse --verify refs/heads/master
)
'
test_expect_success 'fetch mirrors do not act as mirrors during push' '
- (cd mirror-fetch/parent &&
- git checkout HEAD^0
+ (
+ cd mirror-fetch/parent &&
+ git checkout HEAD^0
) &&
- (cd mirror-fetch/child &&
- git branch -m renamed renamed2 &&
- git push parent :
+ (
+ cd mirror-fetch/child &&
+ git branch -m renamed renamed2 &&
+ git push parent :
) &&
- (cd mirror-fetch/parent &&
- git rev-parse --verify renamed &&
- test_must_fail git rev-parse --verify refs/heads/renamed2
+ (
+ cd mirror-fetch/parent &&
+ git rev-parse --verify renamed &&
+ test_must_fail git rev-parse --verify refs/heads/renamed2
)
'
test_expect_success 'add fetch mirror with specific branches' '
git init --bare mirror-fetch/track &&
- (cd mirror-fetch/track &&
- git remote add --mirror=fetch -t heads/new parent ../parent
+ (
+ cd mirror-fetch/track &&
+ git remote add --mirror=fetch -t heads/new parent ../parent
)
'
test_expect_success 'fetch mirror respects specific branches' '
- (cd mirror-fetch/track &&
- git fetch parent &&
- git rev-parse --verify refs/heads/new &&
- test_must_fail git rev-parse --verify refs/heads/renamed
+ (
+ cd mirror-fetch/track &&
+ git fetch parent &&
+ git rev-parse --verify refs/heads/new &&
+ test_must_fail git rev-parse --verify refs/heads/renamed
)
'
@@ -372,60 +408,72 @@ test_expect_success 'add --mirror=push' '
mkdir mirror-push &&
git init --bare mirror-push/public &&
git init mirror-push/private &&
- (cd mirror-push/private &&
- test_commit one &&
- git remote add --mirror=push public ../public
+ (
+ cd mirror-push/private &&
+ test_commit one &&
+ git remote add --mirror=push public ../public
)
'
test_expect_success 'push mirrors act as mirrors during push' '
- (cd mirror-push/private &&
- git branch new &&
- git branch -m master renamed &&
- git push public
+ (
+ cd mirror-push/private &&
+ git branch new &&
+ git branch -m master renamed &&
+ git push public
) &&
- (cd mirror-push/private &&
- git rev-parse --verify refs/heads/new &&
- git rev-parse --verify refs/heads/renamed &&
- test_must_fail git rev-parse --verify refs/heads/master
+ (
+ cd mirror-push/private &&
+ git rev-parse --verify refs/heads/new &&
+ git rev-parse --verify refs/heads/renamed &&
+ test_must_fail git rev-parse --verify refs/heads/master
)
'
test_expect_success 'push mirrors do not act as mirrors during fetch' '
- (cd mirror-push/public &&
- git branch -m renamed renamed2 &&
- git symbolic-ref HEAD refs/heads/renamed2
+ (
+ cd mirror-push/public &&
+ git branch -m renamed renamed2 &&
+ git symbolic-ref HEAD refs/heads/renamed2
) &&
- (cd mirror-push/private &&
- git fetch public &&
- git rev-parse --verify refs/heads/renamed &&
- test_must_fail git rev-parse --verify refs/heads/renamed2
+ (
+ cd mirror-push/private &&
+ git fetch public &&
+ git rev-parse --verify refs/heads/renamed &&
+ test_must_fail git rev-parse --verify refs/heads/renamed2
)
'
test_expect_success 'push mirrors do not allow you to specify refs' '
git init mirror-push/track &&
- (cd mirror-push/track &&
- test_must_fail git remote add --mirror=push -t new public ../public
+ (
+ cd mirror-push/track &&
+ test_must_fail git remote add --mirror=push -t new public ../public
)
'
test_expect_success 'add alt && prune' '
- (mkdir alttst &&
- cd alttst &&
- git init &&
- git remote add -f origin ../one &&
- git config remote.alt.url ../one &&
- git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*") &&
- (cd one &&
- git branch -m side side2) &&
- (cd alttst &&
- git rev-parse --verify refs/remotes/origin/side &&
- test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
- git fetch alt &&
- git remote prune alt &&
- test_must_fail git rev-parse --verify refs/remotes/origin/side &&
- git rev-parse --verify refs/remotes/origin/side2)
+ mkdir alttst &&
+ (
+ cd alttst &&
+ git init &&
+ git remote add -f origin ../one &&
+ git config remote.alt.url ../one &&
+ git config remote.alt.fetch "+refs/heads/*:refs/remotes/origin/*"
+ ) &&
+ (
+ cd one &&
+ git branch -m side side2
+ ) &&
+ (
+ cd alttst &&
+ git rev-parse --verify refs/remotes/origin/side &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side2 &&
+ git fetch alt &&
+ git remote prune alt &&
+ test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+ git rev-parse --verify refs/remotes/origin/side2
+ )
'
cat >test/expect <<\EOF
@@ -433,20 +481,24 @@ some-tag
EOF
test_expect_success 'add with reachable tags (default)' '
- (cd one &&
- >foobar &&
- git add foobar &&
- git commit -m "Foobar" &&
- git tag -a -m "Foobar tag" foobar-tag &&
- git reset --hard HEAD~1 &&
- git tag -a -m "Some tag" some-tag) &&
- (mkdir add-tags &&
- cd add-tags &&
- git init &&
- git remote add -f origin ../one &&
- git tag -l some-tag >../test/output &&
- git tag -l foobar-tag >>../test/output &&
- test_must_fail git config remote.origin.tagopt) &&
+ (
+ cd one &&
+ >foobar &&
+ git add foobar &&
+ git commit -m "Foobar" &&
+ git tag -a -m "Foobar tag" foobar-tag &&
+ git reset --hard HEAD~1 &&
+ git tag -a -m "Some tag" some-tag
+ ) &&
+ mkdir add-tags &&
+ (
+ cd add-tags &&
+ git init &&
+ git remote add -f origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >>../test/output &&
+ test_must_fail git config remote.origin.tagopt
+ ) &&
test_cmp test/expect test/output
'
@@ -457,14 +509,16 @@ foobar-tag
EOF
test_expect_success 'add --tags' '
- (rm -rf add-tags &&
- mkdir add-tags &&
- cd add-tags &&
- git init &&
- git remote add -f --tags origin ../one &&
- git tag -l some-tag >../test/output &&
- git tag -l foobar-tag >>../test/output &&
- git config remote.origin.tagopt >>../test/output) &&
+ rm -rf add-tags &&
+ (
+ mkdir add-tags &&
+ cd add-tags &&
+ git init &&
+ git remote add -f --tags origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >>../test/output &&
+ git config remote.origin.tagopt >>../test/output
+ ) &&
test_cmp test/expect test/output
'
@@ -473,25 +527,31 @@ cat >test/expect <<\EOF
EOF
test_expect_success 'add --no-tags' '
- (rm -rf add-tags &&
- mkdir add-no-tags &&
- cd add-no-tags &&
- git init &&
- git remote add -f --no-tags origin ../one &&
- git tag -l some-tag >../test/output &&
- git tag -l foobar-tag >../test/output &&
- git config remote.origin.tagopt >>../test/output) &&
- (cd one &&
- git tag -d some-tag foobar-tag) &&
+ rm -rf add-tags &&
+ (
+ mkdir add-no-tags &&
+ cd add-no-tags &&
+ git init &&
+ git remote add -f --no-tags origin ../one &&
+ git tag -l some-tag >../test/output &&
+ git tag -l foobar-tag >../test/output &&
+ git config remote.origin.tagopt >>../test/output
+ ) &&
+ (
+ cd one &&
+ git tag -d some-tag foobar-tag
+ ) &&
test_cmp test/expect test/output
'
test_expect_success 'reject --no-no-tags' '
- (cd add-no-tags &&
- test_must_fail git remote add -f --no-no-tags neworigin ../one)
+ (
+ cd add-no-tags &&
+ test_must_fail git remote add -f --no-no-tags neworigin ../one
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<\EOF
apis/master
apis/side
drosophila/another
@@ -500,17 +560,17 @@ cat > one/expect << EOF
EOF
test_expect_success 'update' '
-
- (cd one &&
- git remote add drosophila ../two &&
- git remote add apis ../mirror &&
- git remote update &&
- git branch -r > output &&
- test_cmp expect output)
-
+ (
+ cd one &&
+ git remote add drosophila ../two &&
+ git remote add apis ../mirror &&
+ git remote update &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<\EOF
drosophila/another
drosophila/master
drosophila/side
@@ -521,34 +581,40 @@ cat > one/expect << EOF
EOF
test_expect_success 'update with arguments' '
-
- (cd one &&
- for b in $(git branch -r)
- do
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
git branch -r -d $b || break
- done &&
- git remote add manduca ../mirror &&
- git remote add megaloprepus ../mirror &&
- git config remotes.phobaeticus "drosophila megaloprepus" &&
- git config remotes.titanus manduca &&
- git remote update phobaeticus titanus &&
- git branch -r > output &&
- test_cmp expect output)
-
+ done &&
+ git remote add manduca ../mirror &&
+ git remote add megaloprepus ../mirror &&
+ git config remotes.phobaeticus "drosophila megaloprepus" &&
+ git config remotes.titanus manduca &&
+ git remote update phobaeticus titanus &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
test_expect_success 'update --prune' '
-
- (cd one &&
- git branch -m side2 side3) &&
- (cd test &&
- git remote update --prune &&
- (cd ../one && git branch -m side3 side2) &&
- git rev-parse refs/remotes/origin/side3 &&
- test_must_fail git rev-parse refs/remotes/origin/side2)
+ (
+ cd one &&
+ git branch -m side2 side3
+ ) &&
+ (
+ cd test &&
+ git remote update --prune &&
+ (
+ cd ../one &&
+ git branch -m side3 side2
+ ) &&
+ git rev-parse refs/remotes/origin/side3 &&
+ test_must_fail git rev-parse refs/remotes/origin/side2
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<-\EOF
apis/master
apis/side
manduca/master
@@ -558,176 +624,204 @@ cat > one/expect << EOF
EOF
test_expect_success 'update default' '
-
- (cd one &&
- for b in $(git branch -r)
- do
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
git branch -r -d $b || break
- done &&
- git config remote.drosophila.skipDefaultUpdate true &&
- git remote update default &&
- git branch -r > output &&
- test_cmp expect output)
-
+ done &&
+ git config remote.drosophila.skipDefaultUpdate true &&
+ git remote update default &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
-cat > one/expect << EOF
+cat >one/expect <<\EOF
drosophila/another
drosophila/master
drosophila/side
EOF
test_expect_success 'update default (overridden, with funny whitespace)' '
-
- (cd one &&
- for b in $(git branch -r)
- do
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
git branch -r -d $b || break
- done &&
- git config remotes.default "$(printf "\t drosophila \n")" &&
- git remote update default &&
- git branch -r > output &&
- test_cmp expect output)
-
+ done &&
+ git config remotes.default "$(printf "\t drosophila \n")" &&
+ git remote update default &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
test_expect_success 'update (with remotes.default defined)' '
-
- (cd one &&
- for b in $(git branch -r)
- do
+ (
+ cd one &&
+ for b in $(git branch -r)
+ do
git branch -r -d $b || break
- done &&
- git config remotes.default "drosophila" &&
- git remote update &&
- git branch -r > output &&
- test_cmp expect output)
-
+ done &&
+ git config remotes.default "drosophila" &&
+ git remote update &&
+ git branch -r >output &&
+ test_cmp expect output
+ )
'
test_expect_success '"remote show" does not show symbolic refs' '
-
git clone one three &&
- (cd three &&
- git remote show origin > output &&
- ! grep "^ *HEAD$" < output &&
- ! grep -i stale < output)
-
+ (
+ cd three &&
+ git remote show origin >output &&
+ ! grep "^ *HEAD$" < output &&
+ ! grep -i stale < output
+ )
'
test_expect_success 'reject adding remote with an invalid name' '
-
test_must_fail git remote add some:url desired-name
-
'
# The first three test if the tracking branches are properly renamed,
# the last two ones check if the config is updated.
test_expect_success 'rename a remote' '
-
git clone one four &&
- (cd four &&
- git remote rename origin upstream &&
- rmdir .git/refs/remotes/origin &&
- test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
- test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
- test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
- test "$(git config branch.master.remote)" = "upstream")
-
+ (
+ cd four &&
+ git remote rename origin upstream &&
+ rmdir .git/refs/remotes/origin &&
+ test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
+ test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
+ test "$(git config branch.master.remote)" = "upstream"
+ )
'
test_expect_success 'rename does not update a non-default fetch refspec' '
-
git clone one four.one &&
- (cd four.one &&
- git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
- git remote rename origin upstream &&
- test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
- git rev-parse -q origin/master)
-
+ (
+ cd four.one &&
+ git config remote.origin.fetch +refs/heads/*:refs/heads/origin/* &&
+ git remote rename origin upstream &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/heads/origin/*" &&
+ git rev-parse -q origin/master
+ )
'
test_expect_success 'rename a remote with name part of fetch spec' '
-
git clone one four.two &&
- (cd four.two &&
- git remote rename origin remote &&
- git remote rename remote upstream &&
- test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*")
-
+ (
+ cd four.two &&
+ git remote rename origin remote &&
+ git remote rename remote upstream &&
+ test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*"
+ )
'
test_expect_success 'rename a remote with name prefix of other remote' '
-
git clone one four.three &&
- (cd four.three &&
- git remote add o git://example.com/repo.git &&
- git remote rename o upstream &&
- test "$(git rev-parse origin/master)" = "$(git rev-parse master)")
-
+ (
+ cd four.three &&
+ git remote add o git://example.com/repo.git &&
+ git remote rename o upstream &&
+ test "$(git rev-parse origin/master)" = "$(git rev-parse master)"
+ )
'
-cat > remotes_origin << EOF
+cat >remotes_origin <<EOF
URL: $(pwd)/one
Push: refs/heads/master:refs/heads/upstream
+Push: refs/heads/next:refs/heads/upstream2
Pull: refs/heads/master:refs/heads/origin
+Pull: refs/heads/next:refs/heads/origin2
EOF
test_expect_success 'migrate a remote from named file in $GIT_DIR/remotes' '
git clone one five &&
origin_url=$(pwd)/one &&
- (cd five &&
- git remote remove origin &&
- mkdir -p .git/remotes &&
- cat ../remotes_origin > .git/remotes/origin &&
- git remote rename origin origin &&
- ! test -f .git/remotes/origin &&
- test "$(git config remote.origin.url)" = "$origin_url" &&
- test "$(git config remote.origin.push)" = "refs/heads/master:refs/heads/upstream" &&
- test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+ (
+ cd five &&
+ git remote remove origin &&
+ mkdir -p .git/remotes &&
+ cat ../remotes_origin >.git/remotes/origin &&
+ git remote rename origin origin &&
+ test_path_is_missing .git/remotes/origin &&
+ test "$(git config remote.origin.url)" = "$origin_url" &&
+ cat >push_expected <<-\EOF &&
+ refs/heads/master:refs/heads/upstream
+ refs/heads/next:refs/heads/upstream2
+ EOF
+ cat >fetch_expected <<-\EOF &&
+ refs/heads/master:refs/heads/origin
+ refs/heads/next:refs/heads/origin2
+ EOF
+ git config --get-all remote.origin.push >push_actual &&
+ git config --get-all remote.origin.fetch >fetch_actual &&
+ test_cmp push_expected push_actual &&
+ test_cmp fetch_expected fetch_actual
+ )
'
test_expect_success 'migrate a remote from named file in $GIT_DIR/branches' '
git clone one six &&
origin_url=$(pwd)/one &&
- (cd six &&
- git remote rm origin &&
- echo "$origin_url" > .git/branches/origin &&
- git remote rename origin origin &&
- ! test -f .git/branches/origin &&
- test "$(git config remote.origin.url)" = "$origin_url" &&
- test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin")
+ (
+ cd six &&
+ git remote rm origin &&
+ echo "$origin_url" >.git/branches/origin &&
+ git remote rename origin origin &&
+ test_path_is_missing .git/branches/origin &&
+ test "$(git config remote.origin.url)" = "$origin_url" &&
+ test "$(git config remote.origin.fetch)" = "refs/heads/master:refs/heads/origin" &&
+ test "$(git config remote.origin.push)" = "HEAD:refs/heads/master"
+ )
'
-test_expect_success 'remote prune to cause a dangling symref' '
+test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)' '
git clone one seven &&
(
+ cd seven &&
+ git remote rm origin &&
+ echo "quux#foom" > .git/branches/origin &&
+ git remote rename origin origin &&
+ test_path_is_missing .git/branches/origin &&
+ test "$(git config remote.origin.url)" = "quux" &&
+ test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+ test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
+ )
+'
+
+test_expect_success 'remote prune to cause a dangling symref' '
+ git clone one eight &&
+ (
cd one &&
git checkout side2 &&
git branch -D master
) &&
(
- cd seven &&
+ cd eight &&
git remote prune origin
) >err 2>&1 &&
test_i18ngrep "has become dangling" err &&
: And the dangling symref will not cause other annoying errors &&
(
- cd seven &&
+ cd eight &&
git branch -a
) 2>err &&
! grep "points nowhere" err &&
(
- cd seven &&
+ cd eight &&
test_must_fail git branch nomore origin
) 2>err &&
grep "dangling symref" err
'
test_expect_success 'show empty remote' '
-
test_create_repo empty &&
git clone empty empty-clone &&
(
diff --git a/t/t5528-push-default.sh b/t/t5528-push-default.sh
index 4736da8f36..6a5ac3add4 100755
--- a/t/t5528-push-default.sh
+++ b/t/t5528-push-default.sh
@@ -15,17 +15,19 @@ test_expect_success 'setup bare remotes' '
# $1 = local revision
# $2 = remote revision (tested to be equal to the local one)
+# $3 = [optional] repo to check for actual output (repo1 by default)
check_pushed_commit () {
git log -1 --format='%h %s' "$1" >expect &&
- git --git-dir=repo1 log -1 --format='%h %s' "$2" >actual &&
+ git --git-dir="${3:-repo1}" log -1 --format='%h %s' "$2" >actual &&
test_cmp expect actual
}
# $1 = push.default value
# $2 = expected target branch for the push
+# $3 = [optional] repo to check for actual output (repo1 by default)
test_push_success () {
git -c push.default="$1" push &&
- check_pushed_commit HEAD "$2"
+ check_pushed_commit HEAD "$2" "$3"
}
# $1 = push.default value
@@ -37,6 +39,26 @@ test_push_failure () {
test_cmp expect actual
}
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+test_pushdefault_workflow () {
+ workflow=central
+ pushdefault=parent1
+ if test -n "${4-}"; then
+ workflow=triangular
+ pushdefault=parent2
+ fi
+ test_expect_success "push.default = $2 $1 in $workflow workflows" "
+ test_config branch.master.remote parent1 &&
+ test_config branch.master.merge refs/heads/foo &&
+ test_config remote.pushdefault $pushdefault &&
+ test_commit commit-for-$2${4+-triangular} &&
+ test_push_$1 $2 $3 ${4+repo2}
+ "
+}
+
test_expect_success '"upstream" pushes to configured upstream' '
git checkout master &&
test_config branch.master.remote parent1 &&
@@ -48,7 +70,6 @@ test_expect_success '"upstream" pushes to configured upstream' '
test_expect_success '"upstream" does not push on unconfigured remote' '
git checkout master &&
test_unconfig branch.master.remote &&
- test_config push.default upstream &&
test_commit three &&
test_push_failure upstream
'
@@ -57,7 +78,6 @@ test_expect_success '"upstream" does not push on unconfigured branch' '
git checkout master &&
test_config branch.master.remote parent1 &&
test_unconfig branch.master.merge &&
- test_config push.default upstream
test_commit four &&
test_push_failure upstream
'
@@ -115,4 +135,41 @@ test_expect_success 'push to existing branch, upstream configured with different
test_cmp expect-other-name actual-other-name
'
+# We are on 'master', which integrates with 'foo' from parent1
+# remote (set in test_pushdefault_workflow helper). Push to
+# parent1 in centralized, and push to parent2 in triangular workflow.
+# The parent1 repository has 'master' and 'foo' branches, while
+# the parent2 repository has only 'master' branch.
+#
+# test_pushdefault_workflow() arguments:
+# $1 = success or failure
+# $2 = push.default value
+# $3 = branch to check for actual output (master or foo)
+# $4 = [optional] switch to triangular workflow
+
+# update parent1's master (which is not our upstream)
+test_pushdefault_workflow success current master
+
+# update parent1's foo (which is our upstream)
+test_pushdefault_workflow success upstream foo
+
+# upsream is foo which is not the name of the current branch
+test_pushdefault_workflow failure simple master
+
+# master and foo are updated
+test_pushdefault_workflow success matching master
+
+# master is updated
+test_pushdefault_workflow success current master triangular
+
+# upstream mode cannot be used in triangular
+test_pushdefault_workflow failure upstream foo triangular
+
+# in triangular, 'simple' works as 'current' and update the branch
+# with the same name.
+test_pushdefault_workflow success simple master triangular
+
+# master is updated (parent2 does not have foo)
+test_pushdefault_workflow success matching master triangular
+
test_done
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index fb07536a0f..43ad772484 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -39,25 +39,25 @@ test_bisection_diff()
date >path0
git update-index --add path0
save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b2 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
+on_committer_date "00:00" hide_error save_tag root unique_commit root tree
+on_committer_date "00:01" save_tag l0 unique_commit l0 tree -p root
+on_committer_date "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_committer_date "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_committer_date "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_committer_date "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_committer_date "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_committer_date "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_committer_date "00:08" save_tag b2 unique_commit b2 tree -p b1
+on_committer_date "00:09" save_tag b3 unique_commit b2 tree -p b2
+on_committer_date "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_committer_date "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_committer_date "00:12" save_tag a2 unique_commit a2 tree -p a1
+on_committer_date "00:13" save_tag a3 unique_commit a3 tree -p a2
+on_committer_date "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_committer_date "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_committer_date "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_committer_date "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_committer_date "00:18" save_tag l5 unique_commit l5 tree -p l4
git update-ref HEAD $(tag l5)
@@ -90,29 +90,29 @@ git update-ref HEAD $(tag l5)
# F
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag F unique_commit F tree
-on_committer_date "1971-08-16 00:00:01" save_tag e8 unique_commit e8 tree -p F
-on_committer_date "1971-08-16 00:00:02" save_tag e7 unique_commit e7 tree -p e8
-on_committer_date "1971-08-16 00:00:03" save_tag e6 unique_commit e6 tree -p e7
-on_committer_date "1971-08-16 00:00:04" save_tag e5 unique_commit e5 tree -p e6
-on_committer_date "1971-08-16 00:00:05" save_tag f4 unique_commit f4 tree -p F
-on_committer_date "1971-08-16 00:00:06" save_tag f3 unique_commit f3 tree -p f4
-on_committer_date "1971-08-16 00:00:07" save_tag f2 unique_commit f2 tree -p f3
-on_committer_date "1971-08-16 00:00:08" save_tag f1 unique_commit f1 tree -p f2
-on_committer_date "1971-08-16 00:00:09" save_tag e4 unique_commit e4 tree -p e5
-on_committer_date "1971-08-16 00:00:10" save_tag e3 unique_commit e3 tree -p e4
-on_committer_date "1971-08-16 00:00:11" save_tag e2 unique_commit e2 tree -p e3
-on_committer_date "1971-08-16 00:00:12" save_tag e1 unique_commit e1 tree -p e2
-on_committer_date "1971-08-16 00:00:13" save_tag E unique_commit E tree -p e1 -p f1
-
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag U unique_commit U tree
-on_committer_date "1971-08-16 00:00:01" save_tag u0 unique_commit u0 tree -p U
-on_committer_date "1971-08-16 00:00:01" save_tag u1 unique_commit u1 tree -p u0
-on_committer_date "1971-08-16 00:00:02" save_tag u2 unique_commit u2 tree -p u0
-on_committer_date "1971-08-16 00:00:03" save_tag u3 unique_commit u3 tree -p u0
-on_committer_date "1971-08-16 00:00:04" save_tag u4 unique_commit u4 tree -p u0
-on_committer_date "1971-08-16 00:00:05" save_tag u5 unique_commit u5 tree -p u0
-on_committer_date "1971-08-16 00:00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
+on_committer_date "00:00" hide_error save_tag F unique_commit F tree
+on_committer_date "00:01" save_tag e8 unique_commit e8 tree -p F
+on_committer_date "00:02" save_tag e7 unique_commit e7 tree -p e8
+on_committer_date "00:03" save_tag e6 unique_commit e6 tree -p e7
+on_committer_date "00:04" save_tag e5 unique_commit e5 tree -p e6
+on_committer_date "00:05" save_tag f4 unique_commit f4 tree -p F
+on_committer_date "00:06" save_tag f3 unique_commit f3 tree -p f4
+on_committer_date "00:07" save_tag f2 unique_commit f2 tree -p f3
+on_committer_date "00:08" save_tag f1 unique_commit f1 tree -p f2
+on_committer_date "00:09" save_tag e4 unique_commit e4 tree -p e5
+on_committer_date "00:10" save_tag e3 unique_commit e3 tree -p e4
+on_committer_date "00:11" save_tag e2 unique_commit e2 tree -p e3
+on_committer_date "00:12" save_tag e1 unique_commit e1 tree -p e2
+on_committer_date "00:13" save_tag E unique_commit E tree -p e1 -p f1
+
+on_committer_date "00:00" hide_error save_tag U unique_commit U tree
+on_committer_date "00:01" save_tag u0 unique_commit u0 tree -p U
+on_committer_date "00:01" save_tag u1 unique_commit u1 tree -p u0
+on_committer_date "00:02" save_tag u2 unique_commit u2 tree -p u0
+on_committer_date "00:03" save_tag u3 unique_commit u3 tree -p u0
+on_committer_date "00:04" save_tag u4 unique_commit u4 tree -p u0
+on_committer_date "00:05" save_tag u5 unique_commit u5 tree -p u0
+on_committer_date "00:06" save_tag V unique_commit V tree -p u1 -p u2 -p u3 -p u4 -p u5
test_sequence()
{
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index e4c52b0214..24d1836f41 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -16,39 +16,34 @@ list_duplicates()
date >path0
git update-index --add path0
save_tag tree git write-tree
-on_committer_date "1971-08-16 00:00:00" hide_error save_tag root unique_commit root tree
-on_committer_date "1971-08-16 00:00:01" save_tag l0 unique_commit l0 tree -p root
-on_committer_date "1971-08-16 00:00:02" save_tag l1 unique_commit l1 tree -p l0
-on_committer_date "1971-08-16 00:00:03" save_tag l2 unique_commit l2 tree -p l1
-on_committer_date "1971-08-16 00:00:04" save_tag a0 unique_commit a0 tree -p l2
-on_committer_date "1971-08-16 00:00:05" save_tag a1 unique_commit a1 tree -p a0
-on_committer_date "1971-08-16 00:00:06" save_tag b1 unique_commit b1 tree -p a0
-on_committer_date "1971-08-16 00:00:07" save_tag c1 unique_commit c1 tree -p b1
-on_committer_date "1971-08-16 00:00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
-on_committer_date "1971-08-16 00:00:09" save_tag b3 unique_commit b3 tree -p b2
-on_committer_date "1971-08-16 00:00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
-on_committer_date "1971-08-16 00:00:11" save_tag c3 unique_commit c3 tree -p c2
-on_committer_date "1971-08-16 00:00:12" save_tag a2 unique_commit a2 tree -p a1
-on_committer_date "1971-08-16 00:00:13" save_tag a3 unique_commit a3 tree -p a2
-on_committer_date "1971-08-16 00:00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
-on_committer_date "1971-08-16 00:00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
-on_committer_date "1971-08-16 00:00:16" save_tag l3 unique_commit l3 tree -p a4
-on_committer_date "1971-08-16 00:00:17" save_tag l4 unique_commit l4 tree -p l3
-on_committer_date "1971-08-16 00:00:18" save_tag l5 unique_commit l5 tree -p l4
-on_committer_date "1971-08-16 00:00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
-on_committer_date "1971-08-16 00:00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
-on_committer_date "1971-08-16 00:00:21" hide_error save_tag alt_root unique_commit alt_root tree
-on_committer_date "1971-08-16 00:00:22" save_tag r0 unique_commit r0 tree -p alt_root
-on_committer_date "1971-08-16 00:00:23" save_tag r1 unique_commit r1 tree -p r0
-on_committer_date "1971-08-16 00:00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
-on_committer_date "1971-08-16 00:00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
+on_dates "00:00" "00:00" hide_error save_tag root unique_commit root tree
+on_dates "00:01" "00:01" save_tag l0 unique_commit l0 tree -p root
+on_dates "00:02" "00:02" save_tag l1 unique_commit l1 tree -p l0
+on_dates "00:03" "00:03" save_tag l2 unique_commit l2 tree -p l1
+on_dates "00:04" "00:04" save_tag a0 unique_commit a0 tree -p l2
+on_dates "00:05" "00:05" save_tag a1 unique_commit a1 tree -p a0
+on_dates "00:06" "00:06" save_tag b1 unique_commit b1 tree -p a0
+on_dates "00:07" "00:07" save_tag c1 unique_commit c1 tree -p b1
+on_dates "00:08" "00:08" as_author foobar@example.com save_tag b2 unique_commit b2 tree -p b1
+on_dates "00:09" "00:09" save_tag b3 unique_commit b3 tree -p b2
+on_dates "00:10" "00:10" save_tag c2 unique_commit c2 tree -p c1 -p b2
+on_dates "00:11" "00:11" save_tag c3 unique_commit c3 tree -p c2
+on_dates "00:12" "00:00" save_tag a2 unique_commit a2 tree -p a1
+on_dates "00:13" "00:01" save_tag a3 unique_commit a3 tree -p a2
+on_dates "00:14" "00:14" save_tag b4 unique_commit b4 tree -p b3 -p a3
+on_dates "00:15" "00:15" save_tag a4 unique_commit a4 tree -p a3 -p b4 -p c3
+on_dates "00:16" "00:16" save_tag l3 unique_commit l3 tree -p a4
+on_dates "00:17" "00:17" save_tag l4 unique_commit l4 tree -p l3
+on_dates "00:18" "00:18" save_tag l5 unique_commit l5 tree -p l4
+on_dates "00:19" "00:19" save_tag m1 unique_commit m1 tree -p a4 -p c3
+on_dates "00:20" "00:20" save_tag m2 unique_commit m2 tree -p c3 -p a4
+on_dates "00:21" "00:21" hide_error save_tag alt_root unique_commit alt_root tree
+on_dates "00:22" "00:22" save_tag r0 unique_commit r0 tree -p alt_root
+on_dates "00:23" "00:23" save_tag r1 unique_commit r1 tree -p r0
+on_dates "00:24" "00:24" save_tag l5r1 unique_commit l5r1 tree -p l5 -p r1
+on_dates "00:25" "00:25" save_tag r1l5 unique_commit r1l5 tree -p r1 -p l5
-#
-# note: as of 20/6, it isn't possible to create duplicate parents, so this
-# can't be tested.
-#
-#on_committer_date "1971-08-16 00:00:20" save_tag m3 unique_commit m3 tree -p c3 -p a4 -p c3
hide_error save_tag e1 as_author e@example.com unique_commit e1 tree
save_tag e2 as_author e@example.com unique_commit e2 tree -p e1
save_tag f1 as_author f@example.com unique_commit f1 tree -p e1
@@ -105,6 +100,50 @@ l0
root
EOF
+test_output_expect_success 'simple date order' 'git rev-list --date-order HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+a3
+a2
+c3
+c2
+b3
+b2
+c1
+b1
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
+test_output_expect_success 'simple author-date order' 'git rev-list --author-date-order HEAD' <<EOF
+l5
+l4
+l3
+a4
+b4
+c3
+c2
+b3
+b2
+c1
+b1
+a3
+a2
+a1
+a0
+l2
+l1
+l0
+root
+EOF
+
test_output_expect_success 'two diamonds topo order (g6)' 'git rev-list --topo-order g4' <<EOF
g4
h2
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
index 8f3b54d826..88d60c1ce2 100755
--- a/t/t7011-skip-worktree-reading.sh
+++ b/t/t7011-skip-worktree-reading.sh
@@ -91,12 +91,12 @@ test_expect_success 'update-index --remove' '
test_cmp expected 1
'
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
setup_absent &&
test -z "$(git ls-files -d)"
'
-test_expect_success 'ls-files --delete' '
+test_expect_success 'ls-files --deleted' '
setup_dirty &&
test -z "$(git ls-files -d)"
'
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 71a42f0086..50e6ad7458 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -212,6 +212,32 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
test_cmp empty untracked
'
+test_expect_success 'submodule add in subdirectory' '
+ echo "refs/heads/master" >expect &&
+ >empty &&
+
+ mkdir addtest/sub &&
+ (
+ cd addtest/sub &&
+ git submodule add "$submodurl" ../realsubmod3 &&
+ git submodule init
+ ) &&
+
+ rm -f heads head untracked &&
+ inspect addtest/realsubmod3 ../.. &&
+ test_cmp expect heads &&
+ test_cmp expect head &&
+ test_cmp empty untracked
+'
+
+test_expect_success 'submodule add in subdirectory with relative path should fail' '
+ (
+ cd addtest/sub &&
+ test_must_fail git submodule add ../../ submod3 2>../../output.err
+ ) &&
+ test_i18ngrep toplevel output.err
+'
+
test_expect_success 'setup - add an example entry to .gitmodules' '
GIT_CONFIG=.gitmodules \
git config submodule.example.url git://example.com/init.git
@@ -319,6 +345,26 @@ test_expect_success 'status should be "up-to-date" after update' '
grep "^ $rev1" list
'
+test_expect_success 'status "up-to-date" from subdirectory' '
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule status >../list
+ ) &&
+ grep "^ $rev1" list &&
+ grep "\\.\\./init" list
+'
+
+test_expect_success 'status "up-to-date" from subdirectory with path' '
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule status ../init >../list
+ ) &&
+ grep "^ $rev1" list &&
+ grep "\\.\\./init" list
+'
+
test_expect_success 'status should be "modified" after submodule commit' '
(
cd init &&
@@ -399,6 +445,25 @@ test_expect_success 'update --init' '
git rev-parse --resolve-git-dir init/.git
'
+test_expect_success 'update --init from subdirectory' '
+ mv init init2 &&
+ git config -f .gitmodules submodule.example.url "$(pwd)/init2" &&
+ git config --remove-section submodule.example &&
+ test_must_fail git config submodule.example.url &&
+
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule update ../init >update.out &&
+ cat update.out &&
+ test_i18ngrep "not initialized" update.out &&
+ test_must_fail git rev-parse --resolve-git-dir ../init/.git &&
+
+ git submodule update --init ../init
+ ) &&
+ git rev-parse --resolve-git-dir init/.git
+'
+
test_expect_success 'do not add files from a submodule' '
git reset --hard &&
@@ -772,6 +837,21 @@ test_expect_success 'submodule deinit should remove the whole submodule section
rmdir init
'
+test_expect_success 'submodule deinit from subdirectory' '
+ git submodule update --init &&
+ git config submodule.example.foo bar &&
+ mkdir -p sub &&
+ (
+ cd sub &&
+ git submodule deinit ../init >../output
+ ) &&
+ grep "\\.\\./init" output &&
+ test -z "$(git config --get-regexp "submodule\.example\.")" &&
+ test -n "$(git config --get-regexp "submodule\.example2\.")" &&
+ test -f example2/.git &&
+ rmdir init
+'
+
test_expect_success 'submodule deinit . deinits all initialized submodules' '
git submodule update --init &&
git config submodule.example.foo bar &&
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 30b429e7dc..ac2434c0db 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -45,6 +45,42 @@ EOF
test_cmp expected actual
"
+test_expect_success 'added submodule (subdirectory)' "
+ mkdir sub &&
+ (
+ cd sub &&
+ git submodule summary >../actual
+ ) &&
+ cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+ > Add foo2
+
+EOF
+ test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory only)' "
+ (
+ cd sub &&
+ git submodule summary . >../actual
+ ) &&
+ >expected &&
+ test_cmp expected actual
+"
+
+test_expect_success 'added submodule (subdirectory with explicit path)' "
+ (
+ cd sub &&
+ git submodule summary ../sm1 >../actual
+ ) &&
+ cat >expected <<-EOF &&
+* ../sm1 0000000...$head1 (2):
+ > Add foo2
+
+EOF
+ test_cmp expected actual
+"
+
commit_file sm1 &&
head2=$(add_file sm1 foo3)
@@ -76,8 +112,8 @@ head3=$(
)
test_expect_success 'modified submodule(backward)' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head2...$head3 (2):
< Add foo3
< Add foo2
@@ -89,8 +125,8 @@ EOF
head4=$(add_file sm1 foo4 foo5) &&
head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD)
test_expect_success 'modified submodule(backward and forward)' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head2...$head4 (4):
> Add foo5
> Add foo4
@@ -102,15 +138,15 @@ EOF
"
test_expect_success '--summary-limit' "
- git submodule summary -n 3 >actual &&
- cat >expected <<-EOF &&
+ git submodule summary -n 3 >actual &&
+ cat >expected <<-EOF &&
* sm1 $head2...$head4 (4):
> Add foo5
> Add foo4
< Add foo3
EOF
- test_cmp expected actual
+ test_cmp expected actual
"
commit_file sm1 &&
@@ -122,8 +158,8 @@ rm -f sm1 &&
mv sm1-bak sm1
test_expect_success 'typechanged submodule(submodule->blob), --cached' "
- git submodule summary --cached >actual &&
- cat >expected <<-EOF &&
+ git submodule summary --cached >actual &&
+ cat >expected <<-EOF &&
* sm1 $head4(submodule)->$head5(blob) (3):
< Add foo5
@@ -132,59 +168,59 @@ EOF
"
test_expect_success 'typechanged submodule(submodule->blob), --files' "
- git submodule summary --files >actual &&
- cat >expected <<-EOF &&
+ git submodule summary --files >actual &&
+ cat >expected <<-EOF &&
* sm1 $head5(blob)->$head4(submodule) (3):
> Add foo5
EOF
- test_i18ncmp actual expected
+ test_i18ncmp actual expected
"
rm -rf sm1 &&
git checkout-index sm1
test_expect_success 'typechanged submodule(submodule->blob)' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head4(submodule)->$head5(blob):
EOF
- test_i18ncmp actual expected
+ test_i18ncmp actual expected
"
rm -f sm1 &&
test_create_repo sm1 &&
head6=$(add_file sm1 foo6 foo7)
test_expect_success 'nonexistent commit' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head4...$head6:
Warn: sm1 doesn't contain commit $head4_full
EOF
- test_i18ncmp actual expected
+ test_i18ncmp actual expected
"
commit_file
test_expect_success 'typechanged submodule(blob->submodule)' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head5(blob)->$head6(submodule) (2):
> Add foo7
EOF
- test_i18ncmp expected actual
+ test_i18ncmp expected actual
"
commit_file sm1 &&
rm -rf sm1
test_expect_success 'deleted submodule' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head6...0000000:
EOF
- test_cmp expected actual
+ test_cmp expected actual
"
test_create_repo sm2 &&
@@ -192,43 +228,43 @@ head7=$(add_file sm2 foo8 foo9) &&
git add sm2
test_expect_success 'multiple submodules' "
- git submodule summary >actual &&
- cat >expected <<-EOF &&
+ git submodule summary >actual &&
+ cat >expected <<-EOF &&
* sm1 $head6...0000000:
* sm2 0000000...$head7 (2):
> Add foo9
EOF
- test_cmp expected actual
+ test_cmp expected actual
"
test_expect_success 'path filter' "
- git submodule summary sm2 >actual &&
- cat >expected <<-EOF &&
+ git submodule summary sm2 >actual &&
+ cat >expected <<-EOF &&
* sm2 0000000...$head7 (2):
> Add foo9
EOF
- test_cmp expected actual
+ test_cmp expected actual
"
commit_file sm2
test_expect_success 'given commit' "
- git submodule summary HEAD^ >actual &&
- cat >expected <<-EOF &&
+ git submodule summary HEAD^ >actual &&
+ cat >expected <<-EOF &&
* sm1 $head6...0000000:
* sm2 0000000...$head7 (2):
> Add foo9
EOF
- test_cmp expected actual
+ test_cmp expected actual
"
test_expect_success '--for-status' "
- git submodule summary --for-status HEAD^ >actual &&
- test_i18ncmp actual - <<EOF
+ git submodule summary --for-status HEAD^ >actual &&
+ test_i18ncmp actual - <<EOF
# Submodule changes to be committed:
#
# * sm1 $head6...0000000:
@@ -240,14 +276,14 @@ EOF
"
test_expect_success 'fail when using --files together with --cached' "
- test_must_fail git submodule summary --files --cached
+ test_must_fail git submodule summary --files --cached
"
test_expect_success 'should not fail in an empty repo' "
- git init xyzzy &&
- cd xyzzy &&
- git submodule summary >output 2>&1 &&
- test_cmp output /dev/null
+ git init xyzzy &&
+ cd xyzzy &&
+ git submodule summary >output 2>&1 &&
+ test_cmp output /dev/null
"
test_done
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 94e26c47ea..79bc135bf6 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -11,216 +11,338 @@ These tests exercise the "git submodule sync" subcommand.
. ./test-lib.sh
test_expect_success setup '
- echo file > file &&
+ echo file >file &&
git add file &&
test_tick &&
git commit -m upstream &&
git clone . super &&
git clone super submodule &&
- (cd submodule &&
- git submodule add ../submodule sub-submodule &&
- test_tick &&
- git commit -m "sub-submodule"
+ (
+ cd submodule &&
+ git submodule add ../submodule sub-submodule &&
+ test_tick &&
+ git commit -m "sub-submodule"
) &&
- (cd super &&
- git submodule add ../submodule submodule &&
- test_tick &&
- git commit -m "submodule"
+ (
+ cd super &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule"
) &&
git clone super super-clone &&
- (cd super-clone && git submodule update --init --recursive) &&
+ (
+ cd super-clone &&
+ git submodule update --init --recursive
+ ) &&
git clone super empty-clone &&
- (cd empty-clone && git submodule init) &&
+ (
+ cd empty-clone &&
+ git submodule init
+ ) &&
git clone super top-only-clone &&
git clone super relative-clone &&
- (cd relative-clone && git submodule update --init --recursive) &&
+ (
+ cd relative-clone &&
+ git submodule update --init --recursive
+ ) &&
git clone super recursive-clone &&
- (cd recursive-clone && git submodule update --init --recursive)
+ (
+ cd recursive-clone &&
+ git submodule update --init --recursive
+ )
'
test_expect_success 'change submodule' '
- (cd submodule &&
- echo second line >> file &&
- test_tick &&
- git commit -a -m "change submodule"
+ (
+ cd submodule &&
+ echo second line >>file &&
+ test_tick &&
+ git commit -a -m "change submodule"
)
'
+reset_submodule_urls () {
+ local root
+ root=$(pwd) &&
+ (
+ cd super-clone/submodule &&
+ git config remote.origin.url "$root/submodule"
+ ) &&
+ (
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url "$root/submodule"
+ )
+}
+
test_expect_success 'change submodule url' '
- (cd super &&
- cd submodule &&
- git checkout master &&
- git pull
+ (
+ cd super &&
+ cd submodule &&
+ git checkout master &&
+ git pull
) &&
mv submodule moved-submodule &&
- (cd moved-submodule &&
- git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
- test_tick &&
- git commit -a -m moved-sub-submodule
+ (
+ cd moved-submodule &&
+ git config -f .gitmodules submodule.sub-submodule.url ../moved-submodule &&
+ test_tick &&
+ git commit -a -m moved-sub-submodule
) &&
- (cd super &&
- git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
- test_tick &&
- git commit -a -m moved-submodule
+ (
+ cd super &&
+ git config -f .gitmodules submodule.submodule.url ../moved-submodule &&
+ test_tick &&
+ git commit -a -m moved-submodule
)
'
test_expect_success '"git submodule sync" should update submodule URLs' '
- (cd super-clone &&
- git pull --no-recurse-submodules &&
- git submodule sync
+ (
+ cd super-clone &&
+ git pull --no-recurse-submodules &&
+ git submodule sync
) &&
- test -d "$(cd super-clone/submodule &&
- git config remote.origin.url
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
)" &&
- test ! -d "$(cd super-clone/submodule/sub-submodule &&
- git config remote.origin.url
+ test ! -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
)" &&
- (cd super-clone/submodule &&
- git checkout master &&
- git pull
+ (
+ cd super-clone/submodule &&
+ git checkout master &&
+ git pull
) &&
- (cd super-clone &&
- test -d "$(git config submodule.submodule.url)"
+ (
+ cd super-clone &&
+ test -d "$(git config submodule.submodule.url)"
)
'
test_expect_success '"git submodule sync --recursive" should update all submodule URLs' '
- (cd super-clone &&
- (cd submodule &&
- git pull --no-recurse-submodules
- ) &&
- git submodule sync --recursive
+ (
+ cd super-clone &&
+ (
+ cd submodule &&
+ git pull --no-recurse-submodules
+ ) &&
+ git submodule sync --recursive
+ ) &&
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
+ )" &&
+ test -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
+ )" &&
+ (
+ cd super-clone/submodule/sub-submodule &&
+ git checkout master &&
+ git pull
+ )
+'
+
+test_expect_success 'reset submodule URLs' '
+ reset_submodule_urls super-clone
+'
+
+test_expect_success '"git submodule sync" should update submodule URLs - subdirectory' '
+ (
+ cd super-clone &&
+ git pull --no-recurse-submodules &&
+ mkdir -p sub &&
+ cd sub &&
+ git submodule sync >../../output
+ ) &&
+ grep "\\.\\./submodule" output &&
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
+ )" &&
+ test ! -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
+ )" &&
+ (
+ cd super-clone/submodule &&
+ git checkout master &&
+ git pull
+ ) &&
+ (
+ cd super-clone &&
+ test -d "$(git config submodule.submodule.url)"
+ )
+'
+
+test_expect_success '"git submodule sync --recursive" should update all submodule URLs - subdirectory' '
+ (
+ cd super-clone &&
+ (
+ cd submodule &&
+ git pull --no-recurse-submodules
+ ) &&
+ mkdir -p sub &&
+ cd sub &&
+ git submodule sync --recursive >../../output
) &&
- test -d "$(cd super-clone/submodule &&
- git config remote.origin.url
+ grep "\\.\\./submodule/sub-submodule" output &&
+ test -d "$(
+ cd super-clone/submodule &&
+ git config remote.origin.url
)" &&
- test -d "$(cd super-clone/submodule/sub-submodule &&
- git config remote.origin.url
+ test -d "$(
+ cd super-clone/submodule/sub-submodule &&
+ git config remote.origin.url
)" &&
- (cd super-clone/submodule/sub-submodule &&
- git checkout master &&
- git pull
+ (
+ cd super-clone/submodule/sub-submodule &&
+ git checkout master &&
+ git pull
)
'
test_expect_success '"git submodule sync" should update known submodule URLs' '
- (cd empty-clone &&
- git pull &&
- git submodule sync &&
- test -d "$(git config submodule.submodule.url)"
+ (
+ cd empty-clone &&
+ git pull &&
+ git submodule sync &&
+ test -d "$(git config submodule.submodule.url)"
)
'
test_expect_success '"git submodule sync" should not vivify uninteresting submodule' '
- (cd top-only-clone &&
- git pull &&
- git submodule sync &&
- test -z "$(git config submodule.submodule.url)" &&
- git submodule sync submodule &&
- test -z "$(git config submodule.submodule.url)"
+ (
+ cd top-only-clone &&
+ git pull &&
+ git submodule sync &&
+ test -z "$(git config submodule.submodule.url)" &&
+ git submodule sync submodule &&
+ test -z "$(git config submodule.submodule.url)"
)
'
test_expect_success '"git submodule sync" handles origin URL of the form foo' '
- (cd relative-clone &&
- git remote set-url origin foo &&
- git submodule sync &&
- (cd submodule &&
- #actual fails with: "cannot strip off url foo
- test "$(git config remote.origin.url)" = "../submodule"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin foo &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual fails with: "cannot strip off url foo
+ test "$(git config remote.origin.url)" = "../submodule"
+ )
)
'
test_expect_success '"git submodule sync" handles origin URL of the form foo/bar' '
- (cd relative-clone &&
- git remote set-url origin foo/bar &&
- git submodule sync &&
- (cd submodule &&
- #actual foo/submodule
- test "$(git config remote.origin.url)" = "../foo/submodule"
- )
- (cd submodule/sub-submodule &&
- test "$(git config remote.origin.url)" != "../../foo/submodule"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin foo/bar &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual foo/submodule
+ test "$(git config remote.origin.url)" = "../foo/submodule"
+ ) &&
+ (
+ cd submodule/sub-submodule &&
+ test "$(git config remote.origin.url)" != "../../foo/submodule"
+ )
)
'
test_expect_success '"git submodule sync --recursive" propagates changes in origin' '
- (cd recursive-clone &&
- git remote set-url origin foo/bar &&
- git submodule sync --recursive &&
- (cd submodule &&
- #actual foo/submodule
- test "$(git config remote.origin.url)" = "../foo/submodule"
- )
- (cd submodule/sub-submodule &&
- test "$(git config remote.origin.url)" = "../../foo/submodule"
- )
+ (
+ cd recursive-clone &&
+ git remote set-url origin foo/bar &&
+ git submodule sync --recursive &&
+ (
+ cd submodule &&
+ #actual foo/submodule
+ test "$(git config remote.origin.url)" = "../foo/submodule"
+ ) &&
+ (
+ cd submodule/sub-submodule &&
+ test "$(git config remote.origin.url)" = "../../foo/submodule"
+ )
)
'
test_expect_success '"git submodule sync" handles origin URL of the form ./foo' '
- (cd relative-clone &&
- git remote set-url origin ./foo &&
- git submodule sync &&
- (cd submodule &&
- #actual ./submodule
- test "$(git config remote.origin.url)" = "../submodule"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin ./foo &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ./submodule
+ test "$(git config remote.origin.url)" = "../submodule"
+ )
)
'
test_expect_success '"git submodule sync" handles origin URL of the form ./foo/bar' '
- (cd relative-clone &&
- git remote set-url origin ./foo/bar &&
- git submodule sync &&
- (cd submodule &&
- #actual ./foo/submodule
- test "$(git config remote.origin.url)" = "../foo/submodule"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin ./foo/bar &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ./foo/submodule
+ test "$(git config remote.origin.url)" = "../foo/submodule"
+ )
)
'
test_expect_success '"git submodule sync" handles origin URL of the form ../foo' '
- (cd relative-clone &&
- git remote set-url origin ../foo &&
- git submodule sync &&
- (cd submodule &&
- #actual ../submodule
- test "$(git config remote.origin.url)" = "../../submodule"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin ../foo &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ../submodule
+ test "$(git config remote.origin.url)" = "../../submodule"
+ )
)
'
test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar' '
- (cd relative-clone &&
- git remote set-url origin ../foo/bar &&
- git submodule sync &&
- (cd submodule &&
- #actual ../foo/submodule
- test "$(git config remote.origin.url)" = "../../foo/submodule"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin ../foo/bar &&
+ git submodule sync &&
+ (
+ cd submodule &&
+ #actual ../foo/submodule
+ test "$(git config remote.origin.url)" = "../../foo/submodule"
+ )
)
'
test_expect_success '"git submodule sync" handles origin URL of the form ../foo/bar with deeply nested submodule' '
- (cd relative-clone &&
- git remote set-url origin ../foo/bar &&
- mkdir -p a/b/c &&
- ( cd a/b/c &&
- git init &&
- :> .gitignore &&
- git add .gitignore &&
- test_tick &&
- git commit -m "initial commit" ) &&
- git submodule add ../bar/a/b/c ./a/b/c &&
- git submodule sync &&
- (cd a/b/c &&
- #actual ../foo/bar/a/b/c
- test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
- )
+ (
+ cd relative-clone &&
+ git remote set-url origin ../foo/bar &&
+ mkdir -p a/b/c &&
+ (
+ cd a/b/c &&
+ git init &&
+ >.gitignore &&
+ git add .gitignore &&
+ test_tick &&
+ git commit -m "initial commit"
+ ) &&
+ git submodule add ../bar/a/b/c ./a/b/c &&
+ git submodule sync &&
+ (
+ cd a/b/c &&
+ #actual ../foo/bar/a/b/c
+ test "$(git config remote.origin.url)" = "../../../../foo/bar/a/b/c"
+ )
)
'
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index a4ffea0dbe..cdb0538392 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -80,6 +80,21 @@ test_expect_success 'submodule update detaching the HEAD ' '
)
'
+test_expect_success 'submodule update from subdirectory' '
+ (cd super/submodule &&
+ git reset --hard HEAD~1
+ ) &&
+ mkdir super/sub &&
+ (cd super/sub &&
+ (cd ../submodule &&
+ compare_head
+ ) &&
+ git submodule update ../submodule &&
+ cd ../submodule &&
+ ! compare_head
+ )
+'
+
apos="'";
test_expect_success 'submodule update does not fetch already present commits' '
(cd submodule &&
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index 107b4b7c45..91d4fd1fac 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -80,6 +80,22 @@ test_expect_success 'test basic "submodule foreach" usage' '
test_i18ncmp expect actual
'
+cat >expect <<EOF
+Entering '../sub1'
+$pwd/clone-foo1-../sub1-$sub1sha1
+Entering '../sub3'
+$pwd/clone-foo3-../sub3-$sub3sha1
+EOF
+
+test_expect_success 'test "submodule foreach" from subdirectory' '
+ mkdir clone/sub &&
+ (
+ cd clone/sub &&
+ git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$sha1" >../../actual
+ ) &&
+ test_i18ncmp expect actual
+'
+
test_expect_success 'setup nested submodules' '
git clone submodule nested1 &&
git clone submodule nested2 &&
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 436b7b606e..bdc1f29503 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -13,9 +13,9 @@ commit_msg_is () {
expect=commit_msg_is.expect
actual=commit_msg_is.actual
- printf "%s" "$(git log --pretty=format:%s%b -1)" >$expect &&
- printf "%s" "$1" >$actual &&
- test_i18ncmp $expect $actual
+ printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
+ printf "%s" "$1" >"$expect" &&
+ test_i18ncmp "$expect" "$actual"
}
# A sanity check to see if commit is working at all.
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index e2ffdacc26..ac3d0fe445 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -1335,4 +1335,66 @@ test_expect_failure '.git/config ignore=all suppresses submodule summary' '
git config -f .gitmodules --remove-section submodule.subname
'
+test_expect_success 'setup of test environment' '
+ git config status.showUntrackedFiles no &&
+ git status -s >expected_short &&
+ git status --no-short >expected_noshort
+'
+
+test_expect_success '"status.short=true" same as "-s"' '
+ git -c status.short=true status >actual &&
+ test_cmp expected_short actual
+'
+
+test_expect_success '"status.short=true" weaker than "--no-short"' '
+ git -c status.short=true status --no-short >actual &&
+ test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" same as "--no-short"' '
+ git -c status.short=false status >actual &&
+ test_cmp expected_noshort actual
+'
+
+test_expect_success '"status.short=false" weaker than "-s"' '
+ git -c status.short=false status -s >actual &&
+ test_cmp expected_short actual
+'
+
+test_expect_success '"status.branch=true" same as "-b"' '
+ git status -sb >expected_branch &&
+ git -c status.branch=true status -s >actual &&
+ test_cmp expected_branch actual
+'
+
+test_expect_success '"status.branch=true" different from "--no-branch"' '
+ git status -s --no-branch >expected_nobranch &&
+ git -c status.branch=true status -s >actual &&
+ test_must_fail test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--no-branch"' '
+ git -c status.branch=true status -s --no-branch >actual &&
+ test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=true" weaker than "--porcelain"' '
+ git -c status.branch=true status --porcelain >actual &&
+ test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" same as "--no-branch"' '
+ git -c status.branch=false status -s >actual &&
+ test_cmp expected_nobranch actual
+'
+
+test_expect_success '"status.branch=false" weaker than "-b"' '
+ git -c status.branch=false status -sb >actual &&
+ test_cmp expected_branch actual
+'
+
+test_expect_success 'Restore default test environment' '
+ git config --unset status.showUntrackedFiles
+'
+
test_done
diff --git a/t/t7512-status-help.sh b/t/t7512-status-help.sh
index 4f09beca90..31a798fda2 100755
--- a/t/t7512-status-help.sh
+++ b/t/t7512-status-help.sh
@@ -77,7 +77,7 @@ test_expect_success 'status when rebase in progress before resolving conflicts'
ONTO=$(git rev-parse --short HEAD^^) &&
test_must_fail git rebase HEAD^ --onto HEAD^^ &&
cat >expected <<-EOF &&
- # HEAD detached at $ONTO
+ # rebase in progress; onto $ONTO
# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
# (fix conflicts and then run "git rebase --continue")
# (use "git rebase --skip" to skip this patch)
@@ -104,7 +104,7 @@ test_expect_success 'status when rebase in progress before rebase --continue' '
echo three >main.txt &&
git add main.txt &&
cat >expected <<-EOF &&
- # HEAD detached at $ONTO
+ # rebase in progress; onto $ONTO
# You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
# (all conflicts fixed: run "git rebase --continue")
#
@@ -136,7 +136,7 @@ test_expect_success 'status during rebase -i when conflicts unresolved' '
ONTO=$(git rev-parse --short rebase_i_conflicts) &&
test_must_fail git rebase -i rebase_i_conflicts &&
cat >expected <<-EOF &&
- # HEAD detached at $ONTO
+ # rebase in progress; onto $ONTO
# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
# (fix conflicts and then run "git rebase --continue")
# (use "git rebase --skip" to skip this patch)
@@ -162,7 +162,7 @@ test_expect_success 'status during rebase -i after resolving conflicts' '
test_must_fail git rebase -i rebase_i_conflicts &&
git add main.txt &&
cat >expected <<-EOF &&
- # HEAD detached at $ONTO
+ # rebase in progress; onto $ONTO
# You are currently rebasing branch '\''rebase_i_conflicts_second'\'' on '\''$ONTO'\''.
# (all conflicts fixed: run "git rebase --continue")
#
@@ -188,10 +188,9 @@ test_expect_success 'status when rebasing -i in edit mode' '
export FAKE_LINES &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD~2) &&
- TGT=$(git rev-parse --short two_rebase_i) &&
git rebase -i HEAD~2 &&
cat >expected <<-EOF &&
- # HEAD detached from $TGT
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -216,9 +215,8 @@ test_expect_success 'status when splitting a commit' '
ONTO=$(git rev-parse --short HEAD~3) &&
git rebase -i HEAD~3 &&
git reset HEAD^ &&
- TGT=$(git rev-parse --short HEAD) &&
cat >expected <<-EOF &&
- # HEAD detached at $TGT
+ # rebase in progress; onto $ONTO
# You are currently splitting a commit while rebasing branch '\''split_commit'\'' on '\''$ONTO'\''.
# (Once your working directory is clean, run "git rebase --continue")
#
@@ -246,11 +244,10 @@ test_expect_success 'status after editing the last commit with --amend during a
export FAKE_LINES &&
test_when_finished "git rebase --abort" &&
ONTO=$(git rev-parse --short HEAD~3) &&
- TGT=$(git rev-parse --short three_amend) &&
git rebase -i HEAD~3 &&
git commit --amend -m "foo" &&
cat >expected <<-EOF &&
- # HEAD detached from $TGT
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''amend_last'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -280,7 +277,7 @@ test_expect_success 'status: (continue first edit) second edit' '
git rebase -i HEAD~3 &&
git rebase --continue &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -302,7 +299,7 @@ test_expect_success 'status: (continue first edit) second edit and split' '
git rebase --continue &&
git reset HEAD^ &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (Once your working directory is clean, run "git rebase --continue")
#
@@ -329,7 +326,7 @@ test_expect_success 'status: (continue first edit) second edit and amend' '
git rebase --continue &&
git commit --amend -m "foo" &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -351,7 +348,7 @@ test_expect_success 'status: (amend first edit) second edit' '
git commit --amend -m "a" &&
git rebase --continue &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -374,7 +371,7 @@ test_expect_success 'status: (amend first edit) second edit and split' '
git rebase --continue &&
git reset HEAD^ &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (Once your working directory is clean, run "git rebase --continue")
#
@@ -402,7 +399,7 @@ test_expect_success 'status: (amend first edit) second edit and amend' '
git rebase --continue &&
git commit --amend -m "d" &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -426,7 +423,7 @@ test_expect_success 'status: (split first edit) second edit' '
git commit -m "e" &&
git rebase --continue &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -451,7 +448,7 @@ test_expect_success 'status: (split first edit) second edit and split' '
git rebase --continue &&
git reset HEAD^ &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently splitting a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (Once your working directory is clean, run "git rebase --continue")
#
@@ -481,7 +478,7 @@ test_expect_success 'status: (split first edit) second edit and amend' '
git rebase --continue &&
git commit --amend -m "h" &&
cat >expected <<-EOF &&
- # HEAD detached from $ONTO
+ # rebase in progress; onto $ONTO
# You are currently editing a commit while rebasing branch '\''several_edits'\'' on '\''$ONTO'\''.
# (use "git commit --amend" to amend the current commit)
# (use "git rebase --continue" once you are satisfied with your changes)
@@ -510,7 +507,7 @@ test_expect_success 'status in an am session: file already exists' '
cat >expected <<-\EOF &&
# On branch am_already_exists
# You are in the middle of an am session.
- # (fix conflicts and then run "git am --resolved")
+ # (fix conflicts and then run "git am --continue")
# (use "git am --skip" to skip this patch)
# (use "git am --abort" to restore the original branch)
#
@@ -532,7 +529,7 @@ test_expect_success 'status in an am session: file does not exist' '
cat >expected <<-\EOF &&
# On branch am_not_exists
# You are in the middle of an am session.
- # (fix conflicts and then run "git am --resolved")
+ # (fix conflicts and then run "git am --continue")
# (use "git am --skip" to skip this patch)
# (use "git am --abort" to restore the original branch)
#
@@ -601,7 +598,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
ONTO=$(git rev-parse --short HEAD^^) &&
test_must_fail git rebase HEAD^ --onto HEAD^^ &&
cat >expected <<-EOF &&
- # HEAD detached at $ONTO
+ # rebase in progress; onto $ONTO
# You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
#
# Unmerged paths:
@@ -669,7 +666,7 @@ test_expect_success 'status when cherry-picking after resolving conflicts' '
test_i18ncmp expected actual
'
-test_expect_success 'status showing detached from a tag' '
+test_expect_success 'status showing detached at and from a tag' '
test_commit atag tagging &&
git checkout atag &&
cat >expected <<-\EOF
@@ -677,6 +674,14 @@ test_expect_success 'status showing detached from a tag' '
nothing to commit (use -u to show untracked files)
EOF
git status --untracked-files=no >actual &&
+ test_i18ncmp expected actual &&
+
+ git reset --hard HEAD^ &&
+ cat >expected <<-\EOF
+ # HEAD detached from atag
+ nothing to commit (use -u to show untracked files)
+ EOF
+ git status --untracked-files=no >actual &&
test_i18ncmp expected actual
'
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index 81a1657efb..d9e3103af5 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -122,6 +122,140 @@ test_gitcomp_nl ()
invalid_variable_name='${foo.bar}'
+actual="$TRASH_DIRECTORY/actual"
+
+test_expect_success 'setup for __gitdir tests' '
+ mkdir -p subdir/subsubdir &&
+ git init otherrepo
+'
+
+test_expect_success '__gitdir - from command line (through $__git_dir)' '
+ echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+ (
+ __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - repo as argument' '
+ echo "otherrepo/.git" >expected &&
+ __gitdir "otherrepo" >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - remote as argument' '
+ echo "remote" >expected &&
+ __gitdir "remote" >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in cwd' '
+ echo ".git" >expected &&
+ __gitdir >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - .git directory in parent' '
+ echo "$(pwd -P)/.git" >expected &&
+ (
+ cd subdir/subsubdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - cwd is a .git directory' '
+ echo "." >expected &&
+ (
+ cd .git &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - parent is a .git directory' '
+ echo "$(pwd -P)/.git" >expected &&
+ (
+ cd .git/refs/heads &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
+ echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+ (
+ GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+ export GIT_DIR &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
+ echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+ (
+ GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+ export GIT_DIR &&
+ cd subdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - non-existing $GIT_DIR' '
+ (
+ GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+ export GIT_DIR &&
+ test_must_fail __gitdir
+ )
+'
+
+test_expect_success '__gitdir - gitfile in cwd' '
+ echo "$(pwd -P)/otherrepo/.git" >expected &&
+ echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+ test_when_finished "rm -f subdir/.git" &&
+ (
+ cd subdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - gitfile in parent' '
+ echo "$(pwd -P)/otherrepo/.git" >expected &&
+ echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" >subdir/.git &&
+ test_when_finished "rm -f subdir/.git" &&
+ (
+ cd subdir/subsubdir &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
+ echo "$(pwd -P)/otherrepo/.git" >expected &&
+ mkdir otherrepo/dir &&
+ test_when_finished "rm -rf otherrepo/dir" &&
+ ln -s otherrepo/dir link &&
+ test_when_finished "rm -f link" &&
+ (
+ cd link &&
+ __gitdir >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - not a git repository' '
+ (
+ cd subdir/subsubdir &&
+ GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
+ export GIT_CEILING_DIRECTORIES &&
+ test_must_fail __gitdir
+ )
+'
+
test_expect_success '__gitcomp - trailing space - options' '
test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
--reset-author" <<-EOF
@@ -231,6 +365,7 @@ test_expect_success 'double dash "git" itself' '
--exec-path Z
--exec-path=
--html-path Z
+ --man-path Z
--info-path Z
--work-tree=
--namespace=
diff --git a/t/t9903-bash-prompt.sh b/t/t9903-bash-prompt.sh
index 15521cc4f9..3c3e4e8c38 100755
--- a/t/t9903-bash-prompt.sh
+++ b/t/t9903-bash-prompt.sh
@@ -10,528 +10,558 @@ test_description='test git-specific bash prompt functions'
. "$GIT_BUILD_DIR/contrib/completion/git-prompt.sh"
actual="$TRASH_DIRECTORY/actual"
+c_red='\\[\\e[31m\\]'
+c_green='\\[\\e[32m\\]'
+c_lblue='\\[\\e[1;34m\\]'
+c_clear='\\[\\e[0m\\]'
test_expect_success 'setup for prompt tests' '
- mkdir -p subdir/subsubdir &&
git init otherrepo &&
- echo 1 > file &&
+ echo 1 >file &&
git add file &&
test_tick &&
git commit -m initial &&
git tag -a -m msg1 t1 &&
git checkout -b b1 &&
- echo 2 > file &&
+ echo 2 >file &&
git commit -m "second b1" file &&
- echo 3 > file &&
+ echo 3 >file &&
git commit -m "third b1" file &&
git tag -a -m msg2 t2 &&
git checkout -b b2 master &&
- echo 0 > file &&
+ echo 0 >file &&
git commit -m "second b2" file &&
- echo 00 > file &&
+ echo 00 >file &&
git commit -m "another b2" file &&
- echo 000 > file &&
+ echo 000 >file &&
git commit -m "yet another b2" file &&
git checkout master
'
-test_expect_success 'gitdir - from command line (through $__git_dir)' '
- echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
- (
- __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - repo as argument' '
- echo "otherrepo/.git" > expected &&
- __gitdir "otherrepo" > "$actual" &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - remote as argument' '
- echo "remote" > expected &&
- __gitdir "remote" > "$actual" &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in cwd' '
- echo ".git" > expected &&
- __gitdir > "$actual" &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - .git directory in parent' '
- echo "$(pwd -P)/.git" > expected &&
- (
- cd subdir/subsubdir &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - cwd is a .git directory' '
- echo "." > expected &&
- (
- cd .git &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - parent is a .git directory' '
- echo "$(pwd -P)/.git" > expected &&
- (
- cd .git/refs/heads &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in cwd' '
- echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
- (
- GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
- export GIT_DIR &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - $GIT_DIR set while .git directory in parent' '
- echo "$TRASH_DIRECTORY/otherrepo/.git" > expected &&
- (
- GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
- export GIT_DIR &&
- cd subdir &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - non-existing $GIT_DIR' '
- (
- GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
- export GIT_DIR &&
- test_must_fail __gitdir
- )
-'
-
-test_expect_success 'gitdir - gitfile in cwd' '
- echo "$(pwd -P)/otherrepo/.git" > expected &&
- echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
- test_when_finished "rm -f subdir/.git" &&
- (
- cd subdir &&
- __gitdir > "$actual"
- ) &&
- test_cmp expected "$actual"
-'
-
-test_expect_success 'gitdir - gitfile in parent' '
- echo "$(pwd -P)/otherrepo/.git" > expected &&
- echo "gitdir: $TRASH_DIRECTORY/otherrepo/.git" > subdir/.git &&
- test_when_finished "rm -f subdir/.git" &&
- (
- cd subdir/subsubdir &&
- __gitdir > "$actual"
- ) &&
+test_expect_success 'prompt - branch name' '
+ printf " (master)" >expected &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
-test_expect_success SYMLINKS 'gitdir - resulting path avoids symlinks' '
- echo "$(pwd -P)/otherrepo/.git" > expected &&
- mkdir otherrepo/dir &&
- test_when_finished "rm -rf otherrepo/dir" &&
- ln -s otherrepo/dir link &&
- test_when_finished "rm -f link" &&
- (
- cd link &&
- __gitdir > "$actual"
- ) &&
+test_expect_success SYMLINKS 'prompt - branch name - symlink symref' '
+ printf " (master)" >expected &&
+ test_when_finished "git checkout master" &&
+ test_config core.preferSymlinkRefs true &&
+ git checkout master &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
-test_expect_success 'gitdir - not a git repository' '
- (
- cd subdir/subsubdir &&
- GIT_CEILING_DIRECTORIES="$TRASH_DIRECTORY" &&
- export GIT_CEILING_DIRECTORIES &&
- test_must_fail __gitdir
- )
-'
-
-test_expect_success 'prompt - branch name' '
- printf " (master)" > expected &&
- __git_ps1 > "$actual" &&
+test_expect_success 'prompt - unborn branch' '
+ printf " (unborn)" >expected &&
+ git checkout --orphan unborn &&
+ test_when_finished "git checkout master" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - detached head' '
- printf " ((%s...))" $(git log -1 --format="%h" b1^) > expected &&
+ printf " ((%s...))" $(git log -1 --format="%h" --abbrev=13 b1^) >expected &&
+ test_config core.abbrev 13 &&
git checkout b1^ &&
test_when_finished "git checkout master" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - describe detached head - contains' '
- printf " ((t2~1))" > expected &&
+ printf " ((t2~1))" >expected &&
git checkout b1^ &&
test_when_finished "git checkout master" &&
(
GIT_PS1_DESCRIBE_STYLE=contains &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - describe detached head - branch' '
- printf " ((b1~1))" > expected &&
+ printf " ((b1~1))" >expected &&
git checkout b1^ &&
test_when_finished "git checkout master" &&
(
GIT_PS1_DESCRIBE_STYLE=branch &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - describe detached head - describe' '
- printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) > expected &&
+ printf " ((t1-1-g%s))" $(git log -1 --format="%h" b1^) >expected &&
git checkout b1^ &&
test_when_finished "git checkout master" &&
(
GIT_PS1_DESCRIBE_STYLE=describe &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - describe detached head - default' '
- printf " ((t2))" > expected &&
+ printf " ((t2))" >expected &&
git checkout --detach b1 &&
test_when_finished "git checkout master" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - inside .git directory' '
- printf " (GIT_DIR!)" > expected &&
+ printf " (GIT_DIR!)" >expected &&
(
cd .git &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - deep inside .git directory' '
- printf " (GIT_DIR!)" > expected &&
+ printf " (GIT_DIR!)" >expected &&
(
cd .git/refs/heads &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - inside bare repository' '
- printf " (BARE:master)" > expected &&
+ printf " (BARE:master)" >expected &&
git init --bare bare.git &&
test_when_finished "rm -rf bare.git" &&
(
cd bare.git &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - interactive rebase' '
- printf " (b1|REBASE-i 2/3)" > expected
- echo "#!$SHELL_PATH" >fake_editor.sh &&
- cat >>fake_editor.sh <<\EOF &&
-echo "exec echo" > "$1"
-echo "edit $(git log -1 --format="%h")" >> "$1"
-echo "exec echo" >> "$1"
-EOF
+ printf " (b1|REBASE-i 2/3)" >expected
+ write_script fake_editor.sh <<-\EOF &&
+ echo "exec echo" >"$1"
+ echo "edit $(git log -1 --format="%h")" >>"$1"
+ echo "exec echo" >>"$1"
+ EOF
test_when_finished "rm -f fake_editor.sh" &&
- chmod a+x fake_editor.sh &&
test_set_editor "$TRASH_DIRECTORY/fake_editor.sh" &&
git checkout b1 &&
test_when_finished "git checkout master" &&
git rebase -i HEAD^ &&
test_when_finished "git rebase --abort"
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - rebase merge' '
- printf " (b2|REBASE-m 1/3)" > expected &&
+ printf " (b2|REBASE-m 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
test_must_fail git rebase --merge b1 b2 &&
test_when_finished "git rebase --abort" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - rebase' '
- printf " (b2|REBASE 1/3)" > expected &&
+ printf " (b2|REBASE 1/3)" >expected &&
git checkout b2 &&
test_when_finished "git checkout master" &&
test_must_fail git rebase b1 b2 &&
test_when_finished "git rebase --abort" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - merge' '
- printf " (b1|MERGING)" > expected &&
+ printf " (b1|MERGING)" >expected &&
git checkout b1 &&
test_when_finished "git checkout master" &&
test_must_fail git merge b2 &&
test_when_finished "git reset --hard" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - cherry-pick' '
- printf " (master|CHERRY-PICKING)" > expected &&
+ printf " (master|CHERRY-PICKING)" >expected &&
test_must_fail git cherry-pick b1 &&
test_when_finished "git reset --hard" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - bisect' '
- printf " (master|BISECTING)" > expected &&
+ printf " (master|BISECTING)" >expected &&
git bisect start &&
test_when_finished "git bisect reset" &&
- __git_ps1 > "$actual" &&
+ __git_ps1 >"$actual" &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - clean' '
- printf " (master)" > expected &&
+ printf " (master)" >expected &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - dirty worktree' '
- printf " (master *)" > expected &&
- echo "dirty" > file &&
+ printf " (master *)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - dirty index' '
- printf " (master +)" > expected &&
- echo "dirty" > file &&
+ printf " (master +)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
git add -u &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - dirty index and worktree' '
- printf " (master *+)" > expected &&
- echo "dirty index" > file &&
+ printf " (master *+)" >expected &&
+ echo "dirty index" >file &&
test_when_finished "git reset --hard" &&
git add -u &&
- echo "dirty worktree" > file &&
+ echo "dirty worktree" >file &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - before root commit' '
- printf " (master #)" > expected &&
+ printf " (master #)" >expected &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
cd otherrepo &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - shell variable unset with config disabled' '
- printf " (master)" > expected &&
- echo "dirty" > file &&
+ printf " (master)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
test_config bash.showDirtyState false &&
(
sane_unset GIT_PS1_SHOWDIRTYSTATE &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - shell variable unset with config enabled' '
- printf " (master)" > expected &&
- echo "dirty" > file &&
+ printf " (master)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
test_config bash.showDirtyState true &&
(
sane_unset GIT_PS1_SHOWDIRTYSTATE &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - shell variable set with config disabled' '
- printf " (master)" > expected &&
- echo "dirty" > file &&
+ printf " (master)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
test_config bash.showDirtyState false &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - shell variable set with config enabled' '
- printf " (master *)" > expected &&
- echo "dirty" > file &&
+ printf " (master *)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
test_config bash.showDirtyState true &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - dirty status indicator - not shown inside .git directory' '
- printf " (GIT_DIR!)" > expected &&
- echo "dirty" > file &&
+ printf " (GIT_DIR!)" >expected &&
+ echo "dirty" >file &&
test_when_finished "git reset --hard" &&
(
GIT_PS1_SHOWDIRTYSTATE=y &&
cd .git &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - stash status indicator - no stash' '
- printf " (master)" > expected &&
+ printf " (master)" >expected &&
(
GIT_PS1_SHOWSTASHSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - stash status indicator - stash' '
- printf " (master $)" > expected &&
+ printf " (master $)" >expected &&
echo 2 >file &&
git stash &&
test_when_finished "git stash drop" &&
+ git pack-refs --all &&
(
GIT_PS1_SHOWSTASHSTATE=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - stash status indicator - not shown inside .git directory' '
- printf " (GIT_DIR!)" > expected &&
+ printf " (GIT_DIR!)" >expected &&
echo 2 >file &&
git stash &&
test_when_finished "git stash drop" &&
(
GIT_PS1_SHOWSTASHSTATE=y &&
cd .git &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - no untracked files' '
- printf " (master)" > expected &&
+ printf " (master)" >expected &&
(
GIT_PS1_SHOWUNTRACKEDFILES=y &&
cd otherrepo &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - untracked files' '
- printf " (master %%)" > expected &&
+ printf " (master %%)" >expected &&
(
GIT_PS1_SHOWUNTRACKEDFILES=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - shell variable unset with config disabled' '
- printf " (master)" > expected &&
+ printf " (master)" >expected &&
test_config bash.showUntrackedFiles false &&
(
sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - shell variable unset with config enabled' '
- printf " (master)" > expected &&
+ printf " (master)" >expected &&
test_config bash.showUntrackedFiles true &&
(
sane_unset GIT_PS1_SHOWUNTRACKEDFILES &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - shell variable set with config disabled' '
- printf " (master)" > expected &&
+ printf " (master)" >expected &&
test_config bash.showUntrackedFiles false &&
(
GIT_PS1_SHOWUNTRACKEDFILES=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - shell variable set with config enabled' '
- printf " (master %%)" > expected &&
+ printf " (master %%)" >expected &&
test_config bash.showUntrackedFiles true &&
(
GIT_PS1_SHOWUNTRACKEDFILES=y &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - untracked files status indicator - not shown inside .git directory' '
- printf " (GIT_DIR!)" > expected &&
+ printf " (GIT_DIR!)" >expected &&
(
GIT_PS1_SHOWUNTRACKEDFILES=y &&
cd .git &&
- __git_ps1 > "$actual"
+ __git_ps1 >"$actual"
) &&
test_cmp expected "$actual"
'
test_expect_success 'prompt - format string starting with dash' '
- printf -- "-master" > expected &&
- __git_ps1 "-%s" > "$actual" &&
+ printf -- "-master" >expected &&
+ __git_ps1 "-%s" >"$actual" &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - pc mode' '
+ printf "BEFORE: (master):AFTER" >expected &&
+ printf "" >expected_output &&
+ (
+ __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
+ test_cmp expected_output "$actual" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - branch name' '
+ printf "BEFORE: (${c_green}master${c_clear}):AFTER" >expected &&
+ (
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - detached head' '
+ printf "BEFORE: (${c_red}(%s...)${c_clear}):AFTER" $(git log -1 --format="%h" b1^) >expected &&
+ git checkout b1^ &&
+ test_when_finished "git checkout master" &&
+ (
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty worktree' '
+ printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_clear}):AFTER" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index' '
+ printf "BEFORE: (${c_green}master${c_clear} ${c_green}+${c_clear}):AFTER" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ git add -u &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - dirty index and worktree' '
+ printf "BEFORE: (${c_green}master${c_clear} ${c_red}*${c_green}+${c_clear}):AFTER" >expected &&
+ echo "dirty index" >file &&
+ test_when_finished "git reset --hard" &&
+ git add -u &&
+ echo "dirty worktree" >file &&
+ (
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - dirty status indicator - before root commit' '
+ printf "BEFORE: (${c_green}master${c_clear} ${c_green}#${c_clear}):AFTER" >expected &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ cd otherrepo &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - inside .git directory' '
+ printf "BEFORE: (${c_green}GIT_DIR!${c_clear}):AFTER" >expected &&
+ echo "dirty" >file &&
+ test_when_finished "git reset --hard" &&
+ (
+ GIT_PS1_SHOWDIRTYSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ cd .git &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - stash status indicator' '
+ printf "BEFORE: (${c_green}master${c_clear} ${c_lblue}\$${c_clear}):AFTER" >expected &&
+ echo 2 >file &&
+ git stash &&
+ test_when_finished "git stash drop" &&
+ (
+ GIT_PS1_SHOWSTASHSTATE=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - bash color pc mode - untracked files status indicator' '
+ printf "BEFORE: (${c_green}master${c_clear} ${c_red}%%${c_clear}):AFTER" >expected &&
+ (
+ GIT_PS1_SHOWUNTRACKEDFILES=y &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" &&
+ printf "%s" "$PS1" >"$actual"
+ ) &&
+ test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - zsh color pc mode' '
+ printf "BEFORE: (%%F{green}master%%f):AFTER" >expected &&
+ (
+ ZSH_VERSION=5.0.0 &&
+ GIT_PS1_SHOWCOLORHINTS=y &&
+ __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+ printf "%s" "$PS1" >"$actual"
+ ) &&
test_cmp expected "$actual"
'
diff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh
index 8828ff78f1..a7e9aacbb2 100644
--- a/t/test-lib-functions.sh
+++ b/t/test-lib-functions.sh
@@ -343,6 +343,7 @@ test_declared_prereq () {
}
test_expect_failure () {
+ test_start_
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
@@ -357,10 +358,11 @@ test_expect_failure () {
test_known_broken_failure_ "$1"
fi
fi
- echo >&3 ""
+ test_finish_
}
test_expect_success () {
+ test_start_
test "$#" = 3 && { test_prereq=$1; shift; } || test_prereq=
test "$#" = 2 ||
error "bug in the test script: not 2 or 3 parameters to test-expect-success"
@@ -375,7 +377,7 @@ test_expect_success () {
test_failure_ "$@"
fi
fi
- echo >&3 ""
+ test_finish_
}
# test_external runs external test scripts that provide continuous
diff --git a/t/test-lib.sh b/t/test-lib.sh
index eff3a653d1..97536419e6 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -184,6 +184,9 @@ do
help=t; shift ;;
-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
verbose=t; shift ;;
+ --verbose-only=*)
+ verbose_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
-q|--q|--qu|--qui|--quie|--quiet)
# Ignore --quiet under a TAP::Harness. Saying how many tests
# passed without the ok/not ok details is always an error.
@@ -198,17 +201,39 @@ do
--valgrind=*)
valgrind=$(expr "z$1" : 'z[^=]*=\(.*\)')
shift ;;
+ --valgrind-only=*)
+ valgrind_only=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
+ --valgrind-parallel=*)
+ valgrind_parallel=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
+ --valgrind-only-stride=*)
+ valgrind_only_stride=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
+ --valgrind-only-offset=*)
+ valgrind_only_offset=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
--tee)
shift ;; # was handled already
--root=*)
root=$(expr "z$1" : 'z[^=]*=\(.*\)')
shift ;;
+ --statusprefix=*)
+ statusprefix=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
done
-test -n "$valgrind" && verbose=t
+if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+then
+ test -z "$valgrind" && valgrind=memcheck
+ test -z "$verbose" && verbose_only="$valgrind_only"
+elif test -n "$valgrind"
+then
+ verbose=t
+fi
if test -n "$color"
then
@@ -303,12 +328,12 @@ trap 'die' EXIT
test_ok_ () {
test_success=$(($test_success + 1))
- say_color "" "ok $test_count - $@"
+ say_color "" "${statusprefix}ok $test_count - $@"
}
test_failure_ () {
test_failure=$(($test_failure + 1))
- say_color error "not ok $test_count - $1"
+ say_color error "${statusprefix}not ok $test_count - $1"
shift
echo "$@" | sed -e 's/^/# /'
test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
@@ -316,18 +341,83 @@ test_failure_ () {
test_known_broken_ok_ () {
test_fixed=$(($test_fixed+1))
- say_color error "ok $test_count - $@ # TODO known breakage vanished"
+ say_color error "${statusprefix}ok $test_count - $@ # TODO known breakage vanished"
}
test_known_broken_failure_ () {
test_broken=$(($test_broken+1))
- say_color warn "not ok $test_count - $@ # TODO known breakage"
+ say_color warn "${statusprefix}not ok $test_count - $@ # TODO known breakage"
}
test_debug () {
test "$debug" = "" || eval "$1"
}
+match_pattern_list () {
+ arg="$1"
+ shift
+ test -z "$*" && return 1
+ for pattern_
+ do
+ case "$arg" in
+ $pattern_)
+ return 0
+ esac
+ done
+ return 1
+}
+
+maybe_teardown_verbose () {
+ test -z "$verbose_only" && return
+ exec 4>/dev/null 3>/dev/null
+ verbose=
+}
+
+last_verbose=t
+maybe_setup_verbose () {
+ test -z "$verbose_only" && return
+ if match_pattern_list $test_count $verbose_only ||
+ { test -n "$valgrind_only_stride" &&
+ expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null; }
+ then
+ exec 4>&2 3>&1
+ # Emit a delimiting blank line when going from
+ # non-verbose to verbose. Within verbose mode the
+ # delimiter is printed by test_expect_*. The choice
+ # of the initial $last_verbose is such that before
+ # test 1, we do not print it.
+ test -z "$last_verbose" && echo >&3 ""
+ verbose=t
+ else
+ exec 4>/dev/null 3>/dev/null
+ verbose=
+ fi
+ last_verbose=$verbose
+}
+
+maybe_teardown_valgrind () {
+ test -z "$GIT_VALGRIND" && return
+ GIT_VALGRIND_ENABLED=
+}
+
+maybe_setup_valgrind () {
+ test -z "$GIT_VALGRIND" && return
+ if test -z "$valgrind_only" && test -z "$valgrind_only_stride"
+ then
+ GIT_VALGRIND_ENABLED=t
+ return
+ fi
+ GIT_VALGRIND_ENABLED=
+ if match_pattern_list $test_count $valgrind_only
+ then
+ GIT_VALGRIND_ENABLED=t
+ elif test -n "$valgrind_only_stride" &&
+ expr $test_count "%" $valgrind_only_stride - $valgrind_only_offset = 0 >/dev/null
+ then
+ GIT_VALGRIND_ENABLED=t
+ fi
+}
+
test_eval_ () {
# This is a separate function because some tests use
# "return" to end a test_expect_success block early.
@@ -337,8 +427,10 @@ test_eval_ () {
test_run_ () {
test_cleanup=:
expecting_failure=$2
+ setup_malloc_check
test_eval_ "$1"
eval_ret=$?
+ teardown_malloc_check
if test -z "$immediate" || test $eval_ret = 0 || test -n "$expecting_failure"
then
@@ -353,17 +445,24 @@ test_run_ () {
return "$eval_ret"
}
-test_skip () {
+test_start_ () {
test_count=$(($test_count+1))
+ maybe_setup_verbose
+ maybe_setup_valgrind
+}
+
+test_finish_ () {
+ echo >&3 ""
+ maybe_teardown_valgrind
+ maybe_teardown_verbose
+}
+
+test_skip () {
to_skip=
- for skp in $GIT_SKIP_TESTS
- do
- case $this_test.$test_count in
- $skp)
- to_skip=t
- break
- esac
- done
+ if match_pattern_list $this_test.$test_count $GIT_SKIP_TESTS
+ then
+ to_skip=t
+ fi
if test -z "$to_skip" && test -n "$test_prereq" &&
! test_have_prereq "$test_prereq"
then
@@ -377,8 +476,8 @@ test_skip () {
of_prereq=" of $test_prereq"
fi
- say_color skip >&3 "skipping test: $@"
- say_color skip "ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
+ say_color skip >&3 "${statusprefix}skipping test: $@"
+ say_color skip "${statusprefix}ok $test_count # skip $1 (missing $missing_prereq${of_prereq})"
: true
;;
*)
@@ -395,6 +494,8 @@ test_at_end_hook_ () {
test_done () {
GIT_EXIT_OK=t
+ # Note: t0000 relies on $HARNESS_ACTIVE disabling the .counts
+ # output file
if test -z "$HARNESS_ACTIVE"
then
test_results_dir="$TEST_OUTPUT_DIRECTORY/test-results"
@@ -414,11 +515,11 @@ test_done () {
if test "$test_fixed" != 0
then
- say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
+ say_color error "${statusprefix}# $test_fixed known breakage(s) vanished; please update test(s)"
fi
if test "$test_broken" != 0
then
- say_color warn "# still have $test_broken known breakage(s)"
+ say_color warn "${statusprefix}# still have $test_broken known breakage(s)"
fi
if test "$test_broken" != 0 || test "$test_fixed" != 0
then
@@ -441,9 +542,9 @@ test_done () {
then
if test $test_remaining -gt 0
then
- say_color pass "# passed all $msg"
+ say_color pass "${statusprefix}# passed all $msg"
fi
- say "1..$test_count$skip_all"
+ say "${statusprefix}1..$test_count$skip_all"
fi
test -d "$remove_trash" &&
@@ -457,8 +558,8 @@ test_done () {
*)
if test $test_external_has_tap -eq 0
then
- say_color error "# failed $test_failure among $msg"
- say "1..$test_count"
+ say_color error "${statusprefix}# failed $test_failure among $msg"
+ say "${statusprefix}1..$test_count"
fi
exit 1 ;;
@@ -466,6 +567,9 @@ test_done () {
esac
}
+
+# Set up a directory that we can put in PATH which redirects all git
+# calls to 'valgrind git ...'.
if test -n "$valgrind"
then
make_symlink () {
@@ -513,31 +617,43 @@ then
make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
}
- # override all git executables in TEST_DIRECTORY/..
- GIT_VALGRIND=$TEST_DIRECTORY/valgrind
- mkdir -p "$GIT_VALGRIND"/bin
- for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
- do
- make_valgrind_symlink $file
- done
- # special-case the mergetools loadables
- make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
- OLDIFS=$IFS
- IFS=:
- for path in $PATH
- do
- ls "$path"/git-* 2> /dev/null |
- while read file
+ # In the case of --valgrind-parallel, we only need to do the
+ # wrapping once, in the main script. The worker children all
+ # have $valgrind_only_stride set, so we can skip based on that.
+ if test -z "$valgrind_only_stride"
+ then
+ # override all git executables in TEST_DIRECTORY/..
+ GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+ mkdir -p "$GIT_VALGRIND"/bin
+ for file in $GIT_BUILD_DIR/git* $GIT_BUILD_DIR/test-*
do
- make_valgrind_symlink "$file"
+ make_valgrind_symlink $file
done
- done
- IFS=$OLDIFS
+ # special-case the mergetools loadables
+ make_symlink "$GIT_BUILD_DIR"/mergetools "$GIT_VALGRIND/bin/mergetools"
+ OLDIFS=$IFS
+ IFS=:
+ for path in $PATH
+ do
+ ls "$path"/git-* 2> /dev/null |
+ while read file
+ do
+ make_valgrind_symlink "$file"
+ done
+ done
+ IFS=$OLDIFS
+ fi
PATH=$GIT_VALGRIND/bin:$PATH
GIT_EXEC_PATH=$GIT_VALGRIND/bin
export GIT_VALGRIND
GIT_VALGRIND_MODE="$valgrind"
export GIT_VALGRIND_MODE
+ GIT_VALGRIND_ENABLED=t
+ if test -n "$valgrind_only" || test -n "$valgrind_only_stride"
+ then
+ GIT_VALGRIND_ENABLED=
+ fi
+ export GIT_VALGRIND_ENABLED
elif test -n "$GIT_TEST_INSTALLED"
then
GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path) ||
@@ -622,21 +738,53 @@ then
else
mkdir -p "$TRASH_DIRECTORY"
fi
+
+# Gross hack to spawn N sub-instances of the tests in parallel, and
+# summarize the results. Note that if this is enabled, the script
+# terminates at the end of this 'if' block.
+if test -n "$valgrind_parallel"
+then
+ for i in $(test_seq 1 $valgrind_parallel)
+ do
+ root="$TRASH_DIRECTORY/vgparallel-$i"
+ mkdir "$root"
+ TEST_OUTPUT_DIRECTORY="$root" \
+ ${SHELL_PATH} "$0" \
+ --root="$root" --statusprefix="[$i] " \
+ --valgrind="$valgrind" \
+ --valgrind-only-stride="$valgrind_parallel" \
+ --valgrind-only-offset="$i" &
+ pids="$pids $!"
+ done
+ trap "kill $pids" INT TERM HUP
+ wait $pids
+ trap - INT TERM HUP
+ for i in $(test_seq 1 $valgrind_parallel)
+ do
+ root="$TRASH_DIRECTORY/vgparallel-$i"
+ eval "$(cat "$root/test-results/$(basename "$0" .sh)"-*.counts |
+ sed 's/^\([a-z][a-z]*\) \([0-9][0-9]*\)/inner_\1=\2/')"
+ test_count=$(expr $test_count + $inner_total)
+ test_success=$(expr $test_success + $inner_success)
+ test_fixed=$(expr $test_fixed + $inner_fixed)
+ test_broken=$(expr $test_broken + $inner_broken)
+ test_failure=$(expr $test_failure + $inner_failed)
+ done
+ test_done
+fi
+
# Use -P to resolve symlinks in our working directory so that the cwd
# in subprocesses like git equals our $PWD (for pathname comparisons).
cd -P "$TRASH_DIRECTORY" || exit 1
this_test=${0##*/}
this_test=${this_test%%-*}
-for skp in $GIT_SKIP_TESTS
-do
- case "$this_test" in
- $skp)
- say_color info >&3 "skipping test $this_test altogether"
- skip_all="skip all tests in $this_test"
- test_done
- esac
-done
+if match_pattern_list "$this_test" $GIT_SKIP_TESTS
+then
+ say_color info >&3 "skipping test $this_test altogether"
+ skip_all="skip all tests in $this_test"
+ test_done
+fi
# Provide an implementation of the 'yes' utility
yes () {
diff --git a/t/valgrind/valgrind.sh b/t/valgrind/valgrind.sh
index 6b87c91b60..42153036dc 100755
--- a/t/valgrind/valgrind.sh
+++ b/t/valgrind/valgrind.sh
@@ -4,6 +4,9 @@ base=$(basename "$0")
TOOL_OPTIONS='--leak-check=no'
+test -z "$GIT_VALGRIND_ENABLED" &&
+exec "$GIT_VALGRIND"/../../"$base" "$@"
+
case "$GIT_VALGRIND_MODE" in
memcheck-fast)
;;
diff --git a/test-prio-queue.c b/test-prio-queue.c
new file mode 100644
index 0000000000..7be72f0086
--- /dev/null
+++ b/test-prio-queue.c
@@ -0,0 +1,39 @@
+#include "cache.h"
+#include "prio-queue.h"
+
+static int intcmp(const void *va, const void *vb, void *data)
+{
+ const int *a = va, *b = vb;
+ return *a - *b;
+}
+
+static void show(int *v)
+{
+ if (!v)
+ printf("NULL\n");
+ else
+ printf("%d\n", *v);
+ free(v);
+}
+
+int main(int argc, char **argv)
+{
+ struct prio_queue pq = { intcmp };
+
+ while (*++argv) {
+ if (!strcmp(*argv, "get"))
+ show(prio_queue_get(&pq));
+ else if (!strcmp(*argv, "dump")) {
+ int *v;
+ while ((v = prio_queue_get(&pq)))
+ show(v);
+ }
+ else {
+ int *v = malloc(sizeof(*v));
+ *v = atoi(*argv);
+ prio_queue_put(&pq, v);
+ }
+ }
+
+ return 0;
+}
diff --git a/wt-status.c b/wt-status.c
index 438a40d637..9c4d086b51 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -127,6 +127,7 @@ void wt_status_prepare(struct wt_status *s)
s->change.strdup_strings = 1;
s->untracked.strdup_strings = 1;
s->ignored.strdup_strings = 1;
+ s->show_branch = -1; /* unspecified */
}
static void wt_status_print_unmerged_header(struct wt_status *s)
@@ -826,7 +827,7 @@ static void show_am_in_progress(struct wt_status *s,
if (advice_status_hints) {
if (!state->am_empty_patch)
status_printf_ln(s, color,
- _(" (fix conflicts and then run \"git am --resolved\")"));
+ _(" (fix conflicts and then run \"git am --continue\")"));
status_printf_ln(s, color,
_(" (use \"git am --skip\" to skip this patch)"));
status_printf_ln(s, color,
@@ -1037,7 +1038,6 @@ got_nothing:
}
struct grab_1st_switch_cbdata {
- int found;
struct strbuf buf;
unsigned char nsha1[20];
};
@@ -1061,7 +1061,6 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
for (end = target; *end && *end != '\n'; end++)
;
strbuf_add(&cb->buf, target, end - target);
- cb->found = 1;
return 1;
}
@@ -1178,7 +1177,10 @@ void wt_status_print(struct wt_status *s)
branch_name += 11;
else if (!strcmp(branch_name, "HEAD")) {
branch_status_color = color(WT_STATUS_NOBRANCH, s);
- if (state.detached_from) {
+ if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
+ on_what = _("rebase in progress; onto ");
+ branch_name = state.onto;
+ } else if (state.detached_from) {
unsigned char sha1[20];
branch_name = state.detached_from;
if (!get_sha1("HEAD", sha1) &&
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 219a3bbca6..c0339919cc 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -39,6 +39,8 @@ extern "C" {
#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
#define XDL_EMIT_FUNCNAMES (1 << 0)
#define XDL_EMIT_COMMON (1 << 1)
#define XDL_EMIT_FUNCCONTEXT (1 << 2)
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index b2eb6db2c8..2358a2d632 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -394,6 +394,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
xch->i2 = i2;
xch->chg1 = chg1;
xch->chg2 = chg2;
+ xch->ignore = 0;
return xch;
}
@@ -544,7 +545,9 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdchange_t *xch, *xche;
for (xch = xscr; xch; xch = xche->next) {
- xche = xdl_get_hunk(xch, xecfg);
+ xche = xdl_get_hunk(&xch, xecfg);
+ if (!xch)
+ break;
if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
xch->i2, xche->i2 + xche->chg2 - xch->i2,
ecb->priv) < 0)
@@ -553,6 +556,27 @@ static int xdl_call_hunk_func(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
return 0;
}
+static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+ xdchange_t *xch;
+
+ for (xch = xscr; xch; xch = xch->next) {
+ int ignore = 1;
+ xrecord_t **rec;
+ long i;
+
+ rec = &xe->xdf1.recs[xch->i1];
+ for (i = 0; i < xch->chg1 && ignore; i++)
+ ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+ rec = &xe->xdf2.recs[xch->i2];
+ for (i = 0; i < xch->chg2 && ignore; i++)
+ ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+ xch->ignore = ignore;
+ }
+}
+
int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
xdchange_t *xscr;
@@ -571,6 +595,9 @@ int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
return -1;
}
if (xscr) {
+ if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+ xdl_mark_ignorable(xscr, &xe, xpp->flags);
+
if (ef(&xe, xscr, ecb, xecfg) < 0) {
xdl_free_script(xscr);
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index 7a92ea9c4d..8b81206c9a 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -41,6 +41,7 @@ typedef struct s_xdchange {
struct s_xdchange *next;
long i1, i2;
long chg1, chg2;
+ int ignore;
} xdchange_t;
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index d11dbf9f13..4d8645867e 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -56,16 +56,51 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
/*
* Starting at the passed change atom, find the latest change atom to be included
* inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
*/
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
- xdchange_t *xch, *xchp;
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+ xdchange_t *xch, *xchp, *lxch;
long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+ long max_ignorable = xecfg->ctxlen;
+ unsigned long ignored = 0; /* number of ignored blank lines */
+
+ /* remove ignorable changes that are too far before other changes */
+ for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+ xch = xchp->next;
+
+ if (xch == NULL ||
+ xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+ *xscr = xch;
+ }
+
+ if (*xscr == NULL)
+ return NULL;
+
+ lxch = *xscr;
- for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
- if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+ for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+ long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+ if (distance > max_common)
break;
- return xchp;
+ if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+ lxch = xch;
+ ignored = 0;
+ } else if (distance < max_ignorable && xch->ignore) {
+ ignored += xch->chg2;
+ } else if (lxch != xchp &&
+ xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
+ break;
+ } else if (!xch->ignore) {
+ lxch = xch;
+ ignored = 0;
+ } else {
+ ignored += xch->chg2;
+ }
+ }
+
+ return lxch;
}
@@ -139,7 +174,9 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
return xdl_emit_common(xe, xscr, ecb, xecfg);
for (xch = xscr; xch; xch = xche->next) {
- xche = xdl_get_hunk(xch, xecfg);
+ xche = xdl_get_hunk(&xch, xecfg);
+ if (!xch)
+ break;
s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
diff --git a/xdiff/xemit.h b/xdiff/xemit.h
index c2e2e83027..d29710770c 100644
--- a/xdiff/xemit.h
+++ b/xdiff/xemit.h
@@ -27,7 +27,7 @@
typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
-xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
xdemitconf_t const *xecfg);
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 9504eaecb8..62cb23dfd3 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -143,6 +143,19 @@ long xdl_guess_lines(mmfile_t *mf, long sample) {
return nl + 1;
}
+int xdl_blankline(const char *line, long size, long flags)
+{
+ long i;
+
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return (size <= 1);
+
+ for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+ ;
+
+ return (i == size);
+}
+
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
{
int i1, i2;
diff --git a/xdiff/xutils.h b/xdiff/xutils.h
index ad1428ed69..4646ce5752 100644
--- a/xdiff/xutils.h
+++ b/xdiff/xutils.h
@@ -32,6 +32,7 @@ int xdl_cha_init(chastore_t *cha, long isize, long icount);
void xdl_cha_free(chastore_t *cha);
void *xdl_cha_alloc(chastore_t *cha);
long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
unsigned long xdl_hash_record(char const **data, char const *top, long flags);
unsigned int xdl_hashbits(unsigned int size);