before committing
- do not check in commented out code or unneeded files
- the first line of the commit message should be a short
- description and should skip the full stop
+ description (50 characters is the soft limit, see DISCUSSION
+ in git-commit(1)), and should skip the full stop
- the body should provide a meaningful commit message, which:
- uses the imperative, present tense: "change",
not "changed" or "changes".
- includes motivation for the change, and contrasts
its implementation with previous behaviour
- - if you want your work included in git.git, add a
- "Signed-off-by: Your Name <you@example.com>" line to the
- commit message (or just use the option "-s" when
- committing) to confirm that you agree to the Developer's
- Certificate of Origin
+ - add a "Signed-off-by: Your Name <you@example.com>" line to the
+ commit message (or just use the option "-s" when committing)
+ to confirm that you agree to the Developer's Certificate of Origin
- make sure that you have tests for the bug you are fixing
- make sure that the test suite passes after your commit
when writing commit messages. Default: true.
commitBeforeMerge::
Advice shown when linkgit:git-merge[1] refuses to
- merge to avoid overwritting local changes.
+ merge to avoid overwriting local changes.
Default: true.
resolveConflict::
Advices shown by various commands when conflicts
core.deltaBaseCacheLimit::
Maximum number of bytes to reserve for caching base objects
- that multiple deltafied objects reference. By storing the
+ that may be referenced by multiple deltified objects. By storing the
entire decompressed base objects in a cache Git is able
to avoid unpacking and decompressing frequently used base
objects multiple times.
am.keepcr::
If true, git-am will call git-mailsplit for patches in mbox format
with parameter '--keep-cr'. In this case git-mailsplit will
- not remove `\r` from lines ending with `\r\n`. Can be overrriden
+ not remove `\r` from lines ending with `\r\n`. Can be overridden
by giving '--no-keep-cr' from the command line.
See linkgit:git-am[1], linkgit:git-mailsplit[1].
will enable basic rename detection. If set to "copies" or
"copy", it will detect copies, as well.
+diff.ignoreSubmodules::
+ Sets the default value of --ignore-submodules. Note that this
+ affects only 'git diff' Porcelain, and not lower level 'diff'
+ commands such as 'git diff-files'.
+
diff.suppressBlankEmpty::
A boolean to inhibit the standard behavior of printing a space
before each empty output line. Defaults to false.
If true, the server will look up the end-of-line conversion
attributes for files to determine the '-k' modes to use. If
the attributes force git to treat a file as text,
- the '-k' mode will be left blank so cvs clients will
+ the '-k' mode will be left blank so CVS clients will
treat it as text. If they suppress text conversion, the file
will be set with '-kb' mode, which suppresses any newline munging
the client might otherwise do. If the attributes do not allow
be bound to the local IP (127.0.0.1).
instaweb.modulepath::
- The module path for an apache httpd used by linkgit:git-instaweb[1].
+ The default module path for linkgit:git-instaweb[1] to use
+ instead of /usr/lib/apache2/modules. Only used if httpd
+ is Apache.
instaweb.port::
The port number to bind the gitweb httpd to. See
URL and other values found in the `.gitmodules` file. See
linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
+submodule.<name>.ignore::
+ Defines under what circumstances "git status" and the diff family show
+ a submodule as modified. When set to "all", it will never be considered
+ modified, "dirty" will ignore all changes to the submodules work tree and
+ takes only differences between the HEAD of the submodule and the commit
+ recorded in the superproject into account. "untracked" will additionally
+ let submodules with modified tracked files in their work tree show up.
+ Using "none" (the default when this option is not set) also shows
+ submodules that have untracked files in their work tree as changed.
+ This setting overrides any setting made in .gitmodules for this submodule,
+ both settings can be overriden on the command line by using the
+ "--ignore-submodules" option.
+
tar.umask::
This variable can be used to restrict the permission bits of
tar archive entries. The default is 0002, which turns off the
--ignore-submodules[=<when>]::
Ignore changes to submodules in the diff generation. <when> can be
- either "untracked", "dirty" or "all", which is the default. When
+ either "none", "untracked", "dirty" or "all", which is the default
+ Using "none" will consider the submodule modified when it either contains
+ untracked or modified files or its HEAD differs from the commit recorded
+ in the superproject and can be used to override any settings of the
+ 'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
"untracked" is used submodules are not considered dirty when they only
contain untracked content (but they are still scanned for modified
content). Using "dirty" ignores all changes to the work tree of submodules,
The smaller the changes in your commit, the most effective "git
bisect" will be. And you will probably need "git bisect" less in the
first place, as small changes are easier to review even if they are
-only reviewed by the commiter.
+only reviewed by the committer.
Another good idea is to have good commit messages. They can be very
helpful to understand why some changes were made.
--------
[verse]
'git checkout' [-q] [-f] [-m] [<branch>]
-'git checkout' [-q] [-f] [-m] [[-b|--orphan] <new_branch>] [<start_point>]
+'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
'git checkout' --patch [<tree-ish>] [--] [<paths>...]
branch.
'git checkout' [<branch>]::
-'git checkout' -b <new branch> [<start point>]::
+'git checkout' -b|-B <new_branch> [<start point>]::
This form switches branches by updating the index, working
tree, and HEAD to reflect the specified branch.
use the `--track` or `--no-track` options, which will be passed to
'git branch'. As a convenience, `--track` without `-b` implies branch
creation; see the description of `--track` below.
++
+If `-B` is given, <new_branch> is created if it doesn't exist; otherwise, it
+is reset. This is the transactional equivalent of
++
+------------
+$ git branch -f <branch> [<start point>]
+$ git checkout <branch>
+------------
++
+that is to say, the branch is not reset/created unless "git checkout" is
+successful.
'git checkout' [--patch] [<tree-ish>] [--] <pathspec>...::
Create a new branch named <new_branch> and start it at
<start_point>; see linkgit:git-branch[1] for details.
+-B::
+ Creates the branch <new_branch> and start it at <start_point>;
+ if it already exists, then reset it to <start_point>. This is
+ equivalent to running "git branch" with "-f"; see
+ linkgit:git-branch[1] for details.
+
-t::
--track::
When creating a new branch, set up "upstream" configuration. See
SYNOPSIS
--------
[verse]
-'git clean' [-d] [-f] [-n] [-q] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
DESCRIPTION
-----------
Be quiet, only report errors, but not the files that are
successfully removed.
+-e <pattern>::
+--exclude=<pattern>::
+ Specify special exceptions to not be cleaned. Each <pattern> is
+ the same form as in $GIT_DIR/info/excludes and this option can be
+ given multiple times.
+
-x::
Don't use the ignore rules. This allows removing all untracked
files, including build products. This can be used (possibly in
Usually recording a commit that has the exact same tree as its
sole parent commit is a mistake, and the command prevents you
from making such a commit. This option bypasses the safety, and
- is primarily for use by foreign scm interface scripts.
+ is primarily for use by foreign SCM interface scripts.
--allow-empty-message::
Like --allow-empty this command is primarily for use by foreign
- scm interface scripts. It allows you to create a commit with an
+ SCM interface scripts. It allows you to create a commit with an
empty commit message without using plumbing commands like
linkgit:git-commit-tree[1].
------
Problems related to timestamps:
- * If timestamps of commits in the cvs repository are not stable enough
+ * If timestamps of commits in the CVS repository are not stable enough
to be used for ordering commits changes may show up in the wrong
order.
* If any files were ever "cvs import"ed more than once (e.g., import of
* Branches on which no commits have been made are not imported.
* All files from the branching point are added to a branch even if
- never added in cvs.
+ never added in CVS.
* This applies to files added to the source branch *after* a daughter
branch was created: if previously no commit was made on the daughter
branch they will erroneously be added to the daughter branch in git.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default the server leaves the '-k' mode blank for all files,
-which causes the cvs client to treat them as a text files, subject
-to crlf conversion on some platforms.
+which causes the CVS client to treat them as a text files, subject
+to end-of-line conversion on some platforms.
You can make the server use the end-of-line conversion attributes to
set the '-k' modes for files by setting the `gitcvs.usecrlfattr`
'M' SP <mode> SP <dataref> SP <path> LF
....
+
-Here `<dataref>` can be either a mark reference (`:<idnum>`)
+Here usually `<dataref>` must be either a mark reference (`:<idnum>`)
set by a prior `blob` command, or a full 40-byte SHA-1 of an
-existing Git blob object.
+existing Git blob object. If `<mode>` is `040000`` then
+`<dataref>` must be the full 40-byte SHA-1 of an existing
+Git tree object or a mark reference set with `--import-marks`.
Inline data format::
The data content for the file has not been supplied yet.
* `160000`: A gitlink, SHA-1 of the object refers to a commit in
another repository. Git links can only be specified by SHA or through
a commit mark. They are used to implement submodules.
+* `040000`: A subdirectory. Subdirectories can only be specified by
+ SHA or through a tree mark set with `--import-marks`.
In both formats `<path>` is the complete path of the file to be added
(if not already existing) or modified (if already existing).
--no-filters::
Hash the contents as is, ignoring any input filter that would
- have been chosen by the attributes mechanism, including crlf
+ have been chosen by the attributes mechanism, including the end-of-line
conversion. If the file is read from standard input then this
is always implied, unless the --path option is given.
linkgit:git-web--browse[1] for more information about this. If
the script fails, the URL will be printed to stdout.
+start::
--start::
Start the httpd instance and exit. This does not generate
any of the configuration files for spawning a new instance.
+stop::
--stop::
Stop the httpd instance and exit. This does not generate
any of the configuration files for spawning a new instance,
nor does it close the browser.
+restart::
--restart::
Restart the httpd instance and exit. This does not generate
any of the configuration files for spawning a new instance.
lines, show only a partial prefix.
Non default number of digits can be specified with --abbrev=<n>.
+--debug::
+ After each line that describes a file, add more data about its
+ cache entry. This is intended to show as much information as
+ possible for manual inspection; the exact format may change at
+ any time.
+
\--::
Do not interpret any more arguments as options.
SYNOPSIS
--------
-'git pull' <options> <repository> <refspec>...
+'git pull' [options] [<repository> [<refspec>...]]
DESCRIPTION
-----------
-Runs 'git fetch' with the given parameters, and calls 'git merge'
-to merge the retrieved head(s) into the current branch.
-With `--rebase`, calls 'git rebase' instead of 'git merge'.
-Note that you can use `.` (current directory) as the
-<repository> to pull from the local repository -- this is useful
-when merging local branches into the current branch.
+Incorporates changes from a remote repository into the current
+branch. In its default mode, `git pull` is shorthand for
+`git fetch` followed by `git merge FETCH_HEAD`.
-Also note that options meant for 'git pull' itself and underlying
-'git merge' must be given before the options meant for 'git fetch'.
+More precisely, 'git pull' runs 'git fetch' with the given
+parameters and calls 'git merge' to merge the retrieved branch
+heads into the current branch.
+With `--rebase`, it runs 'git rebase' instead of 'git merge'.
-*Warning*: Running 'git pull' (actually, the underlying 'git merge')
+<repository> should be the name of a remote repository as
+passed to linkgit:git-fetch[1]. <refspec> can name an
+arbitrary remote ref (for example, the name of a tag) or even
+a collection of refs with corresponding remote tracking branches
+(e.g., refs/heads/*:refs/remotes/origin/*), but usually it is
+the name of a branch in the remote repository.
+
+Default values for <repository> and <branch> are read from the
+"remote" and "merge" configuration for the current branch
+as set by linkgit:git-branch[1] `--track`.
+
+Assume the following history exists and the current branch is
+"`master`":
+
+------------
+ A---B---C master on origin
+ /
+ D---E---F---G master
+------------
+
+Then "`git pull`" will fetch and replay the changes from the remote
+`master` branch since it diverged from the local `master` (i.e., `E`)
+until its current commit (`C`) on top of `master` and record the
+result in a new commit along with the names of the two parent commits
+and a log message from the user describing the changes.
+
+------------
+ A---B---C remotes/origin/master
+ / \
+ D---E---F---G---H master
+------------
+
+See linkgit:git-merge[1] for details, including how conflicts
+are presented and handled.
+
+In git 1.7.0 or later, to cancel a conflicting merge, use
+`git reset --merge`. *Warning*: In older versions of git, running 'git pull'
with uncommitted changes is discouraged: while possible, it leaves you
-in a state that is hard to back out of in the case of a conflict.
+in a state that may be hard to back out of in the case of a conflict.
+
+If any of the remote changes overlap with local uncommitted changes,
+the merge will be automatically cancelled and the work tree untouched.
+It is generally best to get any local changes in working order before
+pulling or stash them away with linkgit:git-stash[1].
OPTIONS
-------
+Options meant for 'git pull' itself and the underlying 'git merge'
+must be given before the options meant for 'git fetch'.
+
-q::
--quiet::
This is passed to both underlying git-fetch to squelch reporting of
the 'ours' strategy simply discards all patches from the <branch>,
which makes little sense.
+-X <strategy-option>::
+--strategy-option=<strategy-option>::
+ Pass the <strategy-option> through to the merge strategy.
+ This implies `\--merge` and, if no strategy has been
+ specified, `-s recursive`. Note the reversal of 'ours' and
+ 'theirs' as noted in above for the `-m` option.
+
-q::
--quiet::
Be quiet. Implies --no-stat.
SYNOPSIS
--------
[verse]
-'git reset' [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
'git reset' --patch [<commit>] [--] [<paths>...]
+'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
-----------
-Sets the current head to the specified commit and optionally resets the
-index and working tree to match.
-
-This command is useful if you notice some small error in a recent
-commit (or set of commits) and want to redo that part without showing
-the undo in the history.
-
-If you want to undo a commit other than the latest on a branch,
-linkgit:git-revert[1] is your friend.
-
-The second and third forms with 'paths' and/or --patch are used to
-revert selected paths in the index from a given commit, without moving
-HEAD.
-
+In the first and second form, copy entries from <commit> to the index.
+In the third form, set the current branch to <commit>, optionally
+modifying index and worktree to match. The <commit> defaults to HEAD
+in all forms.
+
+'git reset' [-q] [<commit>] [--] <paths>...::
+ This form resets the index entries for all <paths> to their
+ state at the <commit>. (It does not affect the worktree, nor
+ the current branch.)
++
+This means that `git reset <paths>` is the opposite of `git add
+<paths>`.
-OPTIONS
--------
---mixed::
- Resets the index but not the working tree (i.e., the changed files
- are preserved but not marked for commit) and reports what has not
- been updated. This is the default action.
+'git reset' --patch|-p [<commit>] [--] [<paths>...]::
+ Interactively select hunks in the difference between the index
+ and <commit> (defaults to HEAD). The chosen hunks are applied
+ in reverse to the index.
++
+This means that `git reset -p` is the opposite of `git add -p` (see
+linkgit:git-add[1]).
+'git reset' [--<mode>] [<commit>]::
+ This form points the current branch to <commit> and then
+ updates index and working tree according to <mode>, which must
+ be one of the following:
++
+--
--soft::
Does not touch the index file nor the working tree at all, but
requires them to be in a good order. This leaves all your changed
files "Changes to be committed", as 'git status' would
put it.
+--mixed::
+ Resets the index but not the working tree (i.e., the changed files
+ are preserved but not marked for commit) and reports what has not
+ been updated. This is the default action.
+
--hard::
Matches the working tree and index to that of the tree being
switched to. Any changes to tracked files in the working tree
the given commit. If a file that is different between the
current commit and the given commit has local changes, reset
is aborted.
+--
--p::
---patch::
- Interactively select hunks in the difference between the index
- and <commit> (defaults to HEAD). The chosen hunks are applied
- in reverse to the index.
-+
-This means that `git reset -p` is the opposite of `git add -p` (see
-linkgit:git-add[1]).
+If you want to undo a commit other than the latest on a branch,
+linkgit:git-revert[1] is your friend.
+
+
+OPTIONS
+-------
-q::
--quiet::
Be quiet, only report errors.
-<commit>::
- Commit to make the current HEAD. If not given defaults to HEAD.
-
-DISCUSSION
-----------
-The tables below show what happens when running:
-
-----------
-git reset --option target
-----------
-
-to reset the HEAD to another commit (`target`) with the different
-reset options depending on the state of the files.
-
-In these tables, A, B, C and D are some different states of a
-file. For example, the first line of the first table means that if a
-file is in state A in the working tree, in state B in the index, in
-state C in HEAD and in state D in the target, then "git reset --soft
-target" will put the file in state A in the working tree, in state B
-in the index and in state D in HEAD.
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- A B C D --soft A B D
- --mixed A D D
- --hard D D D
- --merge (disallowed)
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- A B C C --soft A B C
- --mixed A C C
- --hard C C C
- --merge (disallowed)
- --keep A C C
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B B C D --soft B B D
- --mixed B D D
- --hard D D D
- --merge D D D
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B B C C --soft B B C
- --mixed B C C
- --hard C C C
- --merge C C C
- --keep B C C
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B C C D --soft B C D
- --mixed B D D
- --hard D D D
- --merge (disallowed)
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- B C C C --soft B C C
- --mixed B C C
- --hard C C C
- --merge B C C
- --keep B C C
-
-"reset --merge" is meant to be used when resetting out of a conflicted
-merge. Any mergy operation guarantees that the work tree file that is
-involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the work tree. So if
-we see some difference between the index and the target and also
-between the index and the work tree, then it means that we are not
-resetting out from a state that a mergy operation left after failing
-with a conflict. That is why we disallow --merge option in this case.
-
-"reset --keep" is meant to be used when removing some of the last
-commits in the current branch while keeping changes in the working
-tree. If there could be conflicts between the changes in the commit we
-want to remove and the changes in the working tree we want to keep,
-the reset is disallowed. That's why it is disallowed if there are both
-changes between the working tree and HEAD, and between HEAD and the
-target. To be safe, it is also disallowed when there are unmerged
-entries.
-
-The following tables show what happens when there are unmerged
-entries:
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- X U A B --soft (disallowed)
- --mixed X B B
- --hard B B B
- --merge B B B
- --keep (disallowed)
-
- working index HEAD target working index HEAD
- ----------------------------------------------------
- X U A A --soft (disallowed)
- --mixed X A A
- --hard A A A
- --merge A A A
- --keep (disallowed)
-
-X means any state and U means an unmerged index.
-
-Examples
+EXAMPLES
--------
+Undo add::
++
+------------
+$ edit <1>
+$ git add frotz.c filfre.c
+$ mailx <2>
+$ git reset <3>
+$ git pull git://info.example.com/ nitfol <4>
+------------
++
+<1> You are happily working on something, and find the changes
+in these files are in good order. You do not want to see them
+when you run "git diff", because you plan to work on other files
+and changes with these files are distracting.
+<2> Somebody asks you to pull, and the changes sounds worthy of merging.
+<3> However, you already dirtied the index (i.e. your index does
+not match the HEAD commit). But you know the pull you are going
+to make does not affect frotz.c nor filfre.c, so you revert the
+index changes for these two files. Your changes in working tree
+remain there.
+<4> Then you can pull and merge, leaving frotz.c and filfre.c
+changes still in the working tree.
+
Undo a commit and redo::
+
------------
+
See also the --amend option to linkgit:git-commit[1].
-Undo commits permanently::
-+
-------------
-$ git commit ...
-$ git reset --hard HEAD~3 <1>
-------------
-+
-<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
-and you do not want to ever see them again. Do *not* do this if
-you have already given these commits to somebody else. (See the
-"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
-the implications of doing so.)
-
Undo a commit, making it a topic branch::
+
------------
<2> Rewind the master branch to get rid of those three commits.
<3> Switch to "topic/wip" branch and keep working.
-Undo add::
+Undo commits permanently::
+
------------
-$ edit <1>
-$ git add frotz.c filfre.c
-$ mailx <2>
-$ git reset <3>
-$ git pull git://info.example.com/ nitfol <4>
+$ git commit ...
+$ git reset --hard HEAD~3 <1>
------------
+
-<1> You are happily working on something, and find the changes
-in these files are in good order. You do not want to see them
-when you run "git diff", because you plan to work on other files
-and changes with these files are distracting.
-<2> Somebody asks you to pull, and the changes sounds worthy of merging.
-<3> However, you already dirtied the index (i.e. your index does
-not match the HEAD commit). But you know the pull you are going
-to make does not affect frotz.c nor filfre.c, so you revert the
-index changes for these two files. Your changes in working tree
-remain there.
-<4> Then you can pull and merge, leaving frotz.c and filfre.c
-changes still in the working tree.
+<1> The last three commits (HEAD, HEAD^, and HEAD~2) were bad
+and you do not want to ever see them again. Do *not* do this if
+you have already given these commits to somebody else. (See the
+"RECOVERING FROM UPSTREAM REBASE" section in linkgit:git-rebase[1] for
+the implications of doing so.)
Undo a merge or pull::
+
Suppose you are working on something and you commit it, and then you
continue working a bit more, but now you think that what you have in
your working tree should be in another branch that has nothing to do
-with what you commited previously. You can start a new branch and
+with what you committed previously. You can start a new branch and
reset it while keeping the changes in your work tree.
+
------------
<3> But you can use "reset --keep" to remove the unwanted commit after
you switched to "branch2".
+
+DISCUSSION
+----------
+
+The tables below show what happens when running:
+
+----------
+git reset --option target
+----------
+
+to reset the HEAD to another commit (`target`) with the different
+reset options depending on the state of the files.
+
+In these tables, A, B, C and D are some different states of a
+file. For example, the first line of the first table means that if a
+file is in state A in the working tree, in state B in the index, in
+state C in HEAD and in state D in the target, then "git reset --soft
+target" will put the file in state A in the working tree, in state B
+in the index and in state D in HEAD.
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ A B C D --soft A B D
+ --mixed A D D
+ --hard D D D
+ --merge (disallowed)
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ A B C C --soft A B C
+ --mixed A C C
+ --hard C C C
+ --merge (disallowed)
+ --keep A C C
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B B C D --soft B B D
+ --mixed B D D
+ --hard D D D
+ --merge D D D
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B B C C --soft B B C
+ --mixed B C C
+ --hard C C C
+ --merge C C C
+ --keep B C C
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B C C D --soft B C D
+ --mixed B D D
+ --hard D D D
+ --merge (disallowed)
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ B C C C --soft B C C
+ --mixed B C C
+ --hard C C C
+ --merge B C C
+ --keep B C C
+
+"reset --merge" is meant to be used when resetting out of a conflicted
+merge. Any mergy operation guarantees that the work tree file that is
+involved in the merge does not have local change wrt the index before
+it starts, and that it writes the result out to the work tree. So if
+we see some difference between the index and the target and also
+between the index and the work tree, then it means that we are not
+resetting out from a state that a mergy operation left after failing
+with a conflict. That is why we disallow --merge option in this case.
+
+"reset --keep" is meant to be used when removing some of the last
+commits in the current branch while keeping changes in the working
+tree. If there could be conflicts between the changes in the commit we
+want to remove and the changes in the working tree we want to keep,
+the reset is disallowed. That's why it is disallowed if there are both
+changes between the working tree and HEAD, and between HEAD and the
+target. To be safe, it is also disallowed when there are unmerged
+entries.
+
+The following tables show what happens when there are unmerged
+entries:
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ X U A B --soft (disallowed)
+ --mixed X B B
+ --hard B B B
+ --merge B B B
+ --keep (disallowed)
+
+ working index HEAD target working index HEAD
+ ----------------------------------------------------
+ X U A A --soft (disallowed)
+ --mixed X A A
+ --hard A A A
+ --merge A A A
+ --keep (disallowed)
+
+X means any state and U means an unmerged index.
+
+
Author
------
Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds <torvalds@osdl.org>
--ignore-submodules[=<when>]::
Ignore changes to submodules when looking for changes. <when> can be
- either "untracked", "dirty" or "all", which is the default. When
+ either "none", "untracked", "dirty" or "all", which is the default.
+ Using "none" will consider the submodule modified when it either contains
+ untracked or modified files or its HEAD differs from the commit recorded
+ in the superproject and can be used to override any settings of the
+ 'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When
"untracked" is used submodules are not considered dirty when they only
contain untracked content (but they are still scanned for modified
content). Using "dirty" ignores all changes to the work tree of submodules,
revision fetched. If unset, 'git svn' assumes this option to
be "true".
+svn.pathnameencoding::
+ This instructs git svn to recode pathnames to a given encoding.
+ It can be used by windows users and by those who work in non-utf8
+ locales to avoid corrupted file names with non-ASCII characters.
+ Valid encodings are the ones supported by Perl's Encode module.
+
Since the noMetadata, rewriteRoot, rewriteUUID, useSvnsyncProps and useSvmProps
options all affect the metadata generated and used by 'git svn'; they
*must* be set in the configuration file before any history is imported
This config option is overridden if 'git submodule update' is given
the '--merge' or '--rebase' options.
+submodule.<name>.ignore::
+ Defines under what circumstances "git status" and the diff family show
+ a submodule as modified. When set to "all", it will never be considered
+ modified, "dirty" will ignore all changes to the submodules work tree and
+ takes only differences between the HEAD of the submodule and the commit
+ recorded in the superproject into account. "untracked" will additionally
+ let submodules with modified tracked files in their work tree show up.
+ Using "none" (the default when this option is not set) also shows
+ submodules that have untracked files in their work tree as changed.
+ If this option is also present in the submodules entry in .git/config of
+ the superproject, the setting there will override the one found in
+ .gitmodules.
+ Both settings can be overriden on the command line by using the
+ "--ignore-submodule" option.
+
EXAMPLES
--------
directory for a repository associated with your working tree, or
`<project>.git` directory for a public 'bare' repository. It is
also possible to have a working tree where `.git` is a plain
-ascii file containing `gitdir: <path>`, i.e. the path to the
+ASCII file containing `gitdir: <path>`, i.e. the path to the
real git repository).
objects::
- git://host.xz{startsb}:port{endsb}/~{startsb}user{endsb}/path/to/repo.git/
- {startsb}user@{endsb}host.xz:/~{startsb}user{endsb}/path/to/repo.git/
-For local respositories, also supported by git natively, the following
+For local repositories, also supported by git natively, the following
syntaxes may be used:
- /path/to/repo.git/
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.7.2.2
+DEF_VER=v1.7.2.GIT
LF='
'
bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
{ test "$$bindir/" = "$$execdir/" || \
- { $(RM) "$$execdir/git$X" && \
+ for p in git$X $(filter $(install_bindir_programs),$(ALL_PROGRAMS)); do \
+ $(RM) "$$execdir/$$p" && \
test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
- ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
- cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
+ ln "$$bindir/$$p" "$$execdir/$$p" 2>/dev/null || \
+ cp "$$bindir/$$p" "$$execdir/$$p" || exit; \
+ done; \
+ } && \
+ for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
+ $(RM) "$$bindir/$$p" && \
+ ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
+ ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
+ cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
+ done && \
for p in $(BUILT_INS); do \
$(RM) "$$execdir/$$p" && \
ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
struct strbuf fmt = STRBUF_INIT;
struct pretty_print_context ctx = {0};
ctx.date_mode = DATE_NORMAL;
+ ctx.abbrev = DEFAULT_ABBREV;
if (src == buf->buf)
to_free = strbuf_detach(buf, NULL);
enum object_type type;
unsigned long size;
char *buf = read_sha1_file(commit->object.sha1, &type, &size);
- char *ep, *sp;
+ const char *subject_start;
+ int subject_len;
fprintf(stderr, "%c%c%c ",
(flags & TREESAME) ? ' ' : 'T',
fprintf(stderr, " %.*s", 8,
sha1_to_hex(pp->item->object.sha1));
- sp = strstr(buf, "\n\n");
- if (sp) {
- sp += 2;
- for (ep = sp; *ep && *ep != '\n'; ep++)
- ;
- fprintf(stderr, " %.*s", (int)(ep - sp), sp);
- }
+ subject_len = find_commit_subject(buf, &subject_start);
+ if (subject_len)
+ fprintf(stderr, " %.*s", subject_len, subject_start);
fprintf(stderr, "\n");
}
}
dont_change_ref = 1;
else if (!force)
die("A branch named '%s' already exists.", name);
- else if (!is_bare_repository() && !strcmp(head, name))
+ else if (!is_bare_repository() && head && !strcmp(head, name))
die("Cannot force update the current branch.");
forcing = 1;
}
else if (get_sha1(patch->old_sha1_prefix, sha1))
/* git diff has no index line for mode/type changes */
if (!patch->lines_added && !patch->lines_deleted) {
- if (get_current_sha1(patch->new_name, sha1) ||
- get_current_sha1(patch->old_name, sha1))
+ if (get_current_sha1(patch->old_name, sha1))
die("mode change for %s, which is not "
"in current HEAD", name);
sha1_ptr = sha1;
int detailed)
{
int len;
- char *tmp, *endp, *reencoded, *message;
+ const char *subject;
+ char *reencoded, *message;
static char author_name[1024];
static char author_mail[1024];
static char committer_name[1024];
&ret->committer_time, &ret->committer_tz);
ret->summary = summary_buf;
- tmp = strstr(message, "\n\n");
- if (!tmp) {
- error_out:
+ len = find_commit_subject(message, &subject);
+ if (len && len < sizeof(summary_buf)) {
+ memcpy(summary_buf, subject, len);
+ summary_buf[len] = 0;
+ } else {
sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
- free(reencoded);
- return;
}
- tmp += 2;
- endp = strchr(tmp, '\n');
- if (!endp)
- endp = tmp + strlen(tmp);
- len = endp - tmp;
- if (len >= sizeof(summary_buf) || len == 0)
- goto error_out;
- memcpy(summary_buf, tmp, len);
- summary_buf[len] = 0;
free(reencoded);
}
int writeout_stage;
int writeout_error;
+ /* not set by parse_options */
+ int branch_exists;
+
const char *new_branch;
+ const char *new_branch_force;
const char *new_orphan_branch;
int new_branch_log;
enum branch_track track;
struct rev_info rev;
/* I think we want full paths, even if we're in a subdirectory. */
init_revisions(&rev, NULL);
- rev.abbrev = 0;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
if (diff_setup_done(&rev.diffopt) < 0)
die("diff_setup_done failed");
}
}
else
- create_branch(old->name, opts->new_branch, new->name, 0,
+ create_branch(old->name, opts->new_branch, new->name,
+ opts->new_branch_force ? 1 : 0,
opts->new_branch_log, opts->track);
new->name = opts->new_branch;
setup_branch_path(new);
new->name);
else
fprintf(stderr, "Switched to%s branch '%s'\n",
- opts->new_branch ? " a new" : "",
+ opts->branch_exists ? " and reset" : " a new",
new->name);
}
if (old->path && old->name) {
int dwim_new_local_branch = 1;
struct option options[] = {
OPT__QUIET(&opts.quiet),
- OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
+ OPT_STRING('b', NULL, &opts.new_branch, "branch",
+ "create and checkout a new branch"),
+ OPT_STRING('B', NULL, &opts.new_branch_force, "branch",
+ "create/reset and checkout a branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
argc = parse_options(argc, argv, prefix, options, checkout_usage,
PARSE_OPT_KEEP_DASHDASH);
+ /* we can assume from now on new_branch = !new_branch_force */
+ if (opts.new_branch && opts.new_branch_force)
+ die("-B cannot be used with -b");
+
+ /* copy -B over to -b, so that we can just check the latter */
+ if (opts.new_branch_force)
+ opts.new_branch = opts.new_branch_force;
+
if (patch_mode && (opts.track > 0 || opts.new_branch
|| opts.new_branch_log || opts.merge || opts.force))
die ("--patch is incompatible with all other options");
if (opts.new_orphan_branch) {
if (opts.new_branch)
- die("--orphan and -b are mutually exclusive");
+ die("--orphan and -b|-B are mutually exclusive");
if (opts.track > 0)
die("--orphan cannot be used with -t");
opts.new_branch = opts.new_orphan_branch;
if (strbuf_check_branch_ref(&buf, opts.new_branch))
die("git checkout: we do not like '%s' as a branch name.",
opts.new_branch);
- if (!get_sha1(buf.buf, rev))
- die("git checkout: branch %s already exists", opts.new_branch);
+ if (!get_sha1(buf.buf, rev)) {
+ opts.branch_exists = 1;
+ if (!opts.new_branch_force)
+ die("git checkout: branch %s already exists",
+ opts.new_branch);
+ }
strbuf_release(&buf);
}
#include "cache.h"
#include "dir.h"
#include "parse-options.h"
+#include "string-list.h"
#include "quote.h"
static int force = -1; /* unset */
static const char *const builtin_clean_usage[] = {
- "git clean [-d] [-f] [-n] [-q] [-x | -X] [--] <paths>...",
+ "git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...",
NULL
};
return git_default_config(var, value, cb);
}
+static int exclude_cb(const struct option *opt, const char *arg, int unset)
+{
+ struct string_list *exclude_list = opt->value;
+ string_list_append(exclude_list, arg);
+ return 0;
+}
+
int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i;
struct dir_struct dir;
static const char **pathspec;
struct strbuf buf = STRBUF_INIT;
+ struct string_list exclude_list = { NULL, 0, 0, 0 };
const char *qname;
char *seen = NULL;
struct option options[] = {
OPT_BOOLEAN('f', "force", &force, "force"),
OPT_BOOLEAN('d', NULL, &remove_directories,
"remove whole directories"),
+ { OPTION_CALLBACK, 'e', "exclude", &exclude_list, "pattern",
+ "exclude <pattern>", PARSE_OPT_NONEG, exclude_cb },
OPT_BOOLEAN('x', NULL, &ignored, "remove ignored files, too"),
OPT_BOOLEAN('X', NULL, &ignored_only,
"remove only ignored files"),
if (!ignored)
setup_standard_excludes(&dir);
+ for (i = 0; i < exclude_list.nr; i++)
+ add_exclude(exclude_list.items[i].string, "", 0, dir.exclude_list);
+
pathspec = get_pathspec(prefix, argv);
fill_directory(&dir, pathspec);
free(seen);
strbuf_release(&directory);
+ string_list_clear(&exclude_list, 0);
return (errors != 0);
}
#include "rerere.h"
#include "unpack-trees.h"
#include "quote.h"
+#include "submodule.h"
static const char * const builtin_commit_usage[] = {
"git commit [options] [--] <filepattern>...",
status_format = STATUS_FORMAT_PORCELAIN;
wt_status_prepare(&s);
+ gitmodules_config();
git_config(git_status_config, &s);
in_merge = file_exists(git_path("MERGE_HEAD"));
argc = parse_options(argc, argv, prefix,
init_revisions(&rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
- rev.abbrev = 0;
rev.diff = 1;
rev.diffopt.output_format =
DIFF_FORMAT_SHORTSTAT | DIFF_FORMAT_SUMMARY;
#include "commit.h"
#include "revision.h"
#include "builtin.h"
+#include "submodule.h"
static const char diff_files_usage[] =
"git diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
unsigned options = 0;
init_revisions(&rev, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
#include "commit.h"
#include "revision.h"
#include "builtin.h"
+#include "submodule.h"
static const char diff_cache_usage[] =
"git diff-index [-m] [--cached] "
int result;
init_revisions(&rev, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
#include "commit.h"
#include "log-tree.h"
#include "builtin.h"
+#include "submodule.h"
static struct rev_info log_tree_opt;
int read_stdin = 0;
init_revisions(opt, prefix);
+ gitmodules_config();
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
opt->abbrev = 0;
opt->diff = 1;
#include "revision.h"
#include "log-tree.h"
#include "builtin.h"
+#include "submodule.h"
struct blobinfo {
unsigned char sha1[20];
*/
prefix = setup_git_directory_gently(&nongit);
+ gitmodules_config();
git_config(git_diff_ui_config, NULL);
if (diff_use_color_default == -1)
{
struct rev_info revs;
struct object_array commits = { 0, 0, NULL };
- struct string_list extra_refs = { NULL, 0, 0, 0 };
+ struct string_list extra_refs = STRING_LIST_INIT_NODUP;
struct commit *commit;
char *export_filename = NULL, *import_filename = NULL;
struct option options[] = {
return 0;
}
-struct tag_data {
- struct ref **head;
- struct ref ***tail;
-};
-
-static int add_to_tail(struct string_list_item *item, void *cb_data)
-{
- struct tag_data *data = (struct tag_data *)cb_data;
- struct ref *rm = NULL;
-
- /* We have already decided to ignore this item */
- if (!item->util)
- return 0;
-
- rm = alloc_ref(item->string);
- rm->peer_ref = alloc_ref(item->string);
- hashcpy(rm->old_sha1, item->util);
-
- **data->tail = rm;
- *data->tail = &rm->next;
-
- return 0;
-}
-
static void find_non_local_tags(struct transport *transport,
struct ref **head,
struct ref ***tail)
{
- struct string_list existing_refs = { NULL, 0, 0, 0 };
- struct string_list remote_refs = { NULL, 0, 0, 0 };
- struct tag_data data;
+ struct string_list existing_refs = STRING_LIST_INIT_NODUP;
+ struct string_list remote_refs = STRING_LIST_INIT_NODUP;
const struct ref *ref;
struct string_list_item *item = NULL;
- data.head = head; data.tail = tail;
for_each_ref(add_existing, &existing_refs);
for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
item->util = NULL;
/*
- * For all the tags in the remote_refs string list, call
- * add_to_tail to add them to the list of refs to be fetched
+ * For all the tags in the remote_refs string list,
+ * add them to the list of refs to be fetched
*/
- for_each_string_list(&remote_refs, add_to_tail, &data);
+ for_each_string_list_item(item, &remote_refs) {
+ /* Unless we have already decided to ignore this item... */
+ if (item->util)
+ {
+ struct ref *rm = alloc_ref(item->string);
+ rm->peer_ref = alloc_ref(item->string);
+ hashcpy(rm->old_sha1, item->util);
+ **tail = rm;
+ *tail = &rm->next;
+ }
+ }
string_list_clear(&remote_refs, 0);
}
static int do_fetch(struct transport *transport,
struct refspec *refs, int ref_count)
{
- struct string_list existing_refs = { NULL, 0, 0, 0 };
+ struct string_list existing_refs = STRING_LIST_INIT_NODUP;
struct string_list_item *peer_item = NULL;
struct ref *ref_map;
struct ref *rm;
int cmd_fetch(int argc, const char **argv, const char *prefix)
{
int i;
- struct string_list list = { NULL, 0, 0, 0 };
+ struct string_list list = STRING_LIST_INIT_NODUP;
struct remote *remote;
int result = 0;
data->generic.strdup_strings = 1;
}
-static struct string_list srcs = { NULL, 0, 0, 1 };
-static struct string_list origins = { NULL, 0, 0, 1 };
+static struct string_list srcs = STRING_LIST_INIT_DUP;
+static struct string_list origins = STRING_LIST_INIT_DUP;
static int handle_line(char *line)
{
int i, count = 0;
struct commit *commit;
struct object *branch;
- struct string_list subjects = { NULL, 0, 0, 1 };
+ struct string_list subjects = STRING_LIST_INIT_DUP;
int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
struct strbuf sb = STRBUF_INIT;
struct grep_opt opt;
struct object_array list = { 0, 0, NULL };
const char **paths = NULL;
- struct string_list path_list = { NULL, 0, 0, 0 };
+ struct string_list path_list = STRING_LIST_INIT_NODUP;
int i;
int dummy;
int nongit = 0, use_index = 1;
static int show_killed;
static int show_valid_bit;
static int line_terminator = '\n';
+static int debug_mode;
static const char *prefix;
static int max_prefix_len;
ce_stage(ce));
}
write_name(ce->name, ce_namelen(ce));
-}
-
-static int show_one_ru(struct string_list_item *item, void *cbdata)
-{
- const char *path = item->string;
- struct resolve_undo_info *ui = item->util;
- int i, len;
-
- len = strlen(path);
- if (len < max_prefix_len)
- return 0; /* outside of the prefix */
- if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
- return 0; /* uninterested */
- for (i = 0; i < 3; i++) {
- if (!ui->mode[i])
- continue;
- printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
- find_unique_abbrev(ui->sha1[i], abbrev),
- i + 1);
- write_name(path, len);
+ if (debug_mode) {
+ printf(" ctime: %d:%d\n", ce->ce_ctime.sec, ce->ce_ctime.nsec);
+ printf(" mtime: %d:%d\n", ce->ce_mtime.sec, ce->ce_mtime.nsec);
+ printf(" dev: %d\tino: %d\n", ce->ce_dev, ce->ce_ino);
+ printf(" uid: %d\tgid: %d\n", ce->ce_uid, ce->ce_gid);
+ printf(" size: %d\tflags: %x\n", ce->ce_size, ce->ce_flags);
}
- return 0;
}
static void show_ru_info(void)
{
+ struct string_list_item *item;
+
if (!the_index.resolve_undo)
return;
- for_each_string_list(the_index.resolve_undo, show_one_ru, NULL);
+
+ for_each_string_list_item(item, the_index.resolve_undo) {
+ const char *path = item->string;
+ struct resolve_undo_info *ui = item->util;
+ int i, len;
+
+ len = strlen(path);
+ if (len < max_prefix_len)
+ continue; /* outside of the prefix */
+ if (!match_pathspec(pathspec, path, len, max_prefix_len, ps_matched))
+ continue; /* uninterested */
+ for (i = 0; i < 3; i++) {
+ if (!ui->mode[i])
+ continue;
+ printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
+ find_unique_abbrev(ui->sha1[i], abbrev),
+ i + 1);
+ write_name(path, len);
+ }
+ }
}
static void show_files(struct dir_struct *dir)
OPT_STRING(0, "with-tree", &with_tree, "tree-ish",
"pretend that paths removed since <tree-ish> are still present"),
OPT__ABBREV(&abbrev),
+ OPT_BOOLEAN(0, "debug", &debug_mode, "show debugging data"),
OPT_END()
};
char name[PATH_MAX];
int ret = -1;
int i;
- struct string_list list = {NULL, 0, 0, 1};
+ struct string_list list = STRING_LIST_INIT_DUP;
if (populate_maildir_list(&list, maildir) < 0)
goto out;
const char **source, **destination, **dest_path;
enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
struct stat st;
- struct string_list src_for_dst = {NULL, 0, 0, 0};
+ struct string_list src_for_dst = STRING_LIST_INIT_NODUP;
git_config(git_default_config, NULL);
static const char **refspec;
static int refspec_nr;
+static int refspec_alloc;
static void add_refspec(const char *ref)
{
- int nr = refspec_nr + 1;
- refspec = xrealloc(refspec, nr * sizeof(char *));
- refspec[nr-1] = ref;
- refspec_nr = nr;
+ refspec_nr++;
+ ALLOC_GROW(refspec, refspec_nr, refspec_alloc);
+ refspec[refspec_nr-1] = ref;
}
static void set_refspecs(const char **refs, int nr)
static void check_aliased_updates(struct command *commands)
{
struct command *cmd;
- struct string_list ref_list = { NULL, 0, 0, 0 };
+ struct string_list ref_list = STRING_LIST_INIT_NODUP;
for (cmd = commands; cmd; cmd = cmd->next) {
struct string_list_item *item =
static int add(int argc, const char **argv)
{
int fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;
- struct string_list track = { NULL, 0, 0 };
+ struct string_list track = STRING_LIST_INIT_NODUP;
const char *master = NULL;
struct remote *remote;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;
};
struct remote *oldremote, *newremote;
struct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;
- struct string_list remote_branches = { NULL, 0, 0, 0 };
+ struct string_list remote_branches = STRING_LIST_INIT_NODUP;
struct rename_info rename;
int i;
struct remote *remote;
struct strbuf buf = STRBUF_INIT;
struct known_remotes known_remotes = { NULL, NULL };
- struct string_list branches = { NULL, 0, 0, 1 };
- struct string_list skipped = { NULL, 0, 0, 1 };
+ struct string_list branches = STRING_LIST_INIT_DUP;
+ struct string_list skipped = STRING_LIST_INIT_DUP;
struct branches_for_remote cb_data;
int i, result;
OPT_END()
};
struct ref_states states;
- struct string_list info_list = { NULL, 0, 0, 0 };
+ struct string_list info_list = STRING_LIST_INIT_NODUP;
struct show_info info;
argc = parse_options(argc, argv, NULL, options, builtin_remote_show_usage,
static int show_all(void)
{
- struct string_list list = { NULL, 0, 0 };
+ struct string_list list = STRING_LIST_INIT_NODUP;
int result;
list.strdup_strings = 1;
static void garbage_collect(struct string_list *rr)
{
- struct string_list to_remove = { NULL, 0, 0, 1 };
+ struct string_list to_remove = STRING_LIST_INIT_DUP;
DIR *dir;
struct dirent *e;
int i, cutoff;
int cmd_rerere(int argc, const char **argv, const char *prefix)
{
- struct string_list merge_rr = { NULL, 0, 0, 1 };
+ struct string_list merge_rr = STRING_LIST_INIT_DUP;
int i, fd, flags = 0;
if (2 < argc) {
static int get_message(const char *raw_message, struct commit_message *out)
{
const char *encoding;
- const char *p, *abbrev, *eol;
+ const char *abbrev, *subject;
+ int abbrev_len, subject_len;
char *q;
- int abbrev_len, oneline_len;
if (!raw_message)
return -1;
abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
abbrev_len = strlen(abbrev);
- /* Find beginning and end of commit subject. */
- p = out->message;
- while (*p && (*p != '\n' || p[1] != '\n'))
- p++;
- if (*p) {
- p += 2;
- for (eol = p + 1; *eol && *eol != '\n'; eol++)
- ; /* do nothing */
- } else
- eol = p;
- oneline_len = eol - p;
+ subject_len = find_commit_subject(out->message, &subject);
out->parent_label = xmalloc(strlen("parent of ") + abbrev_len +
- strlen("... ") + oneline_len + 1);
+ strlen("... ") + subject_len + 1);
q = out->parent_label;
q = mempcpy(q, "parent of ", strlen("parent of "));
out->label = q;
q = mempcpy(q, abbrev, abbrev_len);
q = mempcpy(q, "... ", strlen("... "));
out->subject = q;
- q = mempcpy(q, p, oneline_len);
+ q = mempcpy(q, subject, subject_len);
*q = '\0';
return 0;
}
*/
static int exclude_existing(const char *match)
{
- static struct string_list existing_refs = { NULL, 0, 0, 0 };
+ static struct string_list existing_refs = STRING_LIST_INIT_NODUP;
char buf[1024];
int matchlen = match ? strlen(match) : 0;
mode = ce->ce_mode;
entlen = pathlen - baselen;
}
- if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
+ if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
+ strbuf_release(&buffer);
return error("invalid object %06o %s for '%.*s'",
mode, sha1_to_hex(sha1), entlen+baselen, path);
+ }
if (ce->ce_flags & CE_REMOVE)
continue; /* entry being removed */
char *timebuf,
size_t timebuf_size);
int parse_date(const char *date, char *buf, int bufsize);
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
void datestamp(char *buf, int bufsize);
#define approxidate(s) approxidate_careful((s), NULL)
unsigned long approxidate_careful(const char *, int *);
return ret;
}
+int find_commit_subject(const char *commit_buffer, const char **subject)
+{
+ const char *eol;
+ const char *p = commit_buffer;
+
+ while (*p && (*p != '\n' || p[1] != '\n'))
+ p++;
+ if (*p) {
+ p += 2;
+ for (eol = p; *eol && *eol != '\n'; eol++)
+ ; /* do nothing */
+ } else
+ eol = p;
+
+ *subject = p;
+
+ return eol - p;
+}
+
struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
{
struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
int parse_commit(struct commit *item);
+/* Find beginning and length of commit subject. */
+int find_commit_subject(const char *commit_buffer, const char **subject);
+
struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
unsigned commit_list_count(const struct commit_list *l);
struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
if test -z "$quiet"
then
commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
- --summary --root HEAD --`
+ --abbrev --summary --root HEAD --`
echo "Created${initial_commit:+ initial} commit $commit"
fi
fi
# "t=%s; printf 'http://.../?id=%%s' \$t; echo;echo; git show -C \$t; echo"
# Be careful if "..." contains things that will be expanded by shell "eval"
# or printf.
+# hooks.emailmaxlines
+# The maximum number of lines that should be included in the generated
+# email body. If not specified, there is no limit.
+# Lines beyond the limit are suppressed and counted, and a final
+# line is added indicating the number of suppressed lines.
#
# Notes
# -----
oldrev=$(git rev-parse $1)
newrev=$(git rev-parse $2)
refname="$3"
+ maxlines=$4
# --- Interpret
# 0000->1234 (create)
fn_name=atag
;;
esac
- generate_${change_type}_${fn_name}_email
+
+ if [ -z "$maxlines" ]; then
+ generate_${change_type}_${fn_name}_email
+ else
+ generate_${change_type}_${fn_name}_email | limit_lines $maxlines
+ fi
generate_email_footer
}
}
+limit_lines()
+{
+ lines=0
+ skipped=0
+ while IFS="" read -r line; do
+ lines=$((lines + 1))
+ if [ $lines -gt $1 ]; then
+ skipped=$((skipped + 1))
+ else
+ printf "%s\n" "$line"
+ fi
+ done
+ if [ $skipped -ne 0 ]; then
+ echo "... $skipped lines suppressed ..."
+ fi
+}
+
+
send_mail()
{
if [ -n "$envelopesender" ]; then
envelopesender=$(git config hooks.envelopesender)
emailprefix=$(git config hooks.emailprefix || echo '[SCM] ')
custom_showrev=$(git config hooks.showrev)
+maxlines=$(git config hooks.emailmaxlines)
# --- Main loop
# Allow dual mode: run from the command line just like the update hook, or
else
while read oldrev newrev refname
do
- generate_email $oldrev $newrev $refname | send_mail
+ generate_email $oldrev $newrev $refname $maxlines | send_mail
done
fi
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date_toffset(const char *date, unsigned long *timestamp, int *offset)
+int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
{
struct tm tm;
int tm_gmt;
if (!tm_gmt)
*timestamp -= *offset * 60;
- return 1; /* success */
+ return 0; /* success */
}
int parse_date(const char *date, char *result, int maxlen)
{
unsigned long timestamp;
int offset;
- if (parse_date_toffset(date, ×tamp, &offset) > 0)
- return date_string(timestamp, offset, result, maxlen);
- else
+ if (parse_date_basic(date, ×tamp, &offset))
return -1;
+ return date_string(timestamp, offset, result, maxlen);
}
enum date_mode parse_date_format(const char *format)
int offset;
int errors = 0;
- if (parse_date_toffset(date, ×tamp, &offset) > 0)
+ if (!parse_date_basic(date, ×tamp, &offset))
return timestamp;
-
return approxidate_str(date, tv, &errors);
}
if (!error_ret)
error_ret = &dummy;
- if (parse_date_toffset(date, ×tamp, &offset) > 0) {
+ if (!parse_date_basic(date, ×tamp, &offset)) {
*error_ret = 0;
return timestamp;
}
unsigned ce_option, unsigned *dirty_submodule)
{
int changed = ce_match_stat(ce, st, ce_option);
- if (S_ISGITLINK(ce->ce_mode)
- && !DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES)
- && !DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
- && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES))) {
- *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+ if (S_ISGITLINK(ce->ce_mode)) {
+ unsigned orig_flags = diffopt->flags;
+ if (!DIFF_OPT_TST(diffopt, OVERRIDE_SUBMODULE_CONFIG))
+ set_diffopt_flags_from_submodule_config(diffopt, ce->name);
+ if (DIFF_OPT_TST(diffopt, IGNORE_SUBMODULES))
+ changed = 0;
+ else if (!DIFF_OPT_TST(diffopt, IGNORE_DIRTY_SUBMODULES)
+ && (!changed || DIFF_OPT_TST(diffopt, DIRTY_SUBMODULES)))
+ *dirty_submodule = is_submodule_modified(ce->name, DIFF_OPT_TST(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES));
+ diffopt->flags = orig_flags;
}
return changed;
}
if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
char buffer1[PATH_MAX], buffer2[PATH_MAX];
- struct string_list p1 = {NULL, 0, 0, 1}, p2 = {NULL, 0, 0, 1};
+ struct string_list p1 = STRING_LIST_INIT_DUP;
+ struct string_list p2 = STRING_LIST_INIT_DUP;
int len1 = 0, len2 = 0, i1, i2, ret = 0;
if (name1 && read_directory(name1, &p1))
int diff_auto_refresh_index = 1;
static int diff_mnemonic_prefix;
static int diff_no_prefix;
+static struct diff_options default_diff_options;
static char diff_colors[][COLOR_MAXLEN] = {
GIT_COLOR_RESET,
if (!strcmp(var, "diff.wordregex"))
return git_config_string(&diff_word_regex_cfg, var, value);
+ if (!strcmp(var, "diff.ignoresubmodules"))
+ handle_ignore_submodules_arg(&default_diff_options, value);
+
return git_diff_basic_config(var, value, cb);
}
return 0;
}
+ if (!prefixcmp(var, "submodule."))
+ return parse_submodule_config_option(var, value);
+
return git_color_default_config(var, value, cb);
}
void diff_setup(struct diff_options *options)
{
- memset(options, 0, sizeof(*options));
+ memcpy(options, &default_diff_options, sizeof(*options));
options->file = stdout;
DIFF_OPT_SET(options, ALLOW_TEXTCONV);
else if (!strcmp(arg, "--no-textconv"))
DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
- else if (!strcmp(arg, "--ignore-submodules"))
+ else if (!strcmp(arg, "--ignore-submodules")) {
+ DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, "all");
- else if (!prefixcmp(arg, "--ignore-submodules="))
+ } else if (!prefixcmp(arg, "--ignore-submodules=")) {
+ DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(options, arg + 20);
- else if (!strcmp(arg, "--submodule"))
+ } else if (!strcmp(arg, "--submodule"))
DIFF_OPT_SET(options, SUBMODULE_LOG);
else if (!prefixcmp(arg, "--submodule=")) {
if (!strcmp(arg + 12, "log"))
void diffcore_std(struct diff_options *options)
{
- /* We never run this function more than one time, because the
- * rename/copy detection logic can only run once.
- */
- if (diff_queued_diff.run)
- return;
-
if (options->skip_stat_unmatch)
diffcore_skip_stat_unmatch(options);
- if (options->break_opt != -1)
- diffcore_break(options->break_opt);
- if (options->detect_rename)
- diffcore_rename(options);
- if (options->break_opt != -1)
- diffcore_merge_broken();
+ if (!options->found_follow) {
+ /* See try_to_follow_renames() in tree-diff.c */
+ if (options->break_opt != -1)
+ diffcore_break(options->break_opt);
+ if (options->detect_rename)
+ diffcore_rename(options);
+ if (options->break_opt != -1)
+ diffcore_merge_broken();
+ }
if (options->pickaxe)
diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
if (options->orderfile)
diffcore_order(options->orderfile);
- diff_resolve_rename_copy();
+ if (!options->found_follow)
+ /* See try_to_follow_renames() in tree-diff.c */
+ diff_resolve_rename_copy();
diffcore_apply_filter(options->filter);
if (diff_queued_diff.nr && !DIFF_OPT_TST(options, DIFF_FROM_CONTENTS))
else
DIFF_OPT_CLR(options, HAS_CHANGES);
- diff_queued_diff.run = 1;
+ options->found_follow = 0;
}
int diff_result_code(struct diff_options *opt, int status)
return result;
}
+/*
+ * Shall changes to this submodule be ignored?
+ *
+ * Submodule changes can be configured to be ignored separately for each path,
+ * but that configuration can be overridden from the command line.
+ */
+static int is_submodule_ignored(const char *path, struct diff_options *options)
+{
+ int ignored = 0;
+ unsigned orig_flags = options->flags;
+ if (!DIFF_OPT_TST(options, OVERRIDE_SUBMODULE_CONFIG))
+ set_diffopt_flags_from_submodule_config(options, path);
+ if (DIFF_OPT_TST(options, IGNORE_SUBMODULES))
+ ignored = 1;
+ options->flags = orig_flags;
+ return ignored;
+}
+
void diff_addremove(struct diff_options *options,
int addremove, unsigned mode,
const unsigned char *sha1,
{
struct diff_filespec *one, *two;
- if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(mode))
+ if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options))
return;
/* This may look odd, but it is a preparation for
{
struct diff_filespec *one, *two;
- if (DIFF_OPT_TST(options, IGNORE_SUBMODULES) && S_ISGITLINK(old_mode)
- && S_ISGITLINK(new_mode))
+ if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) &&
+ is_submodule_ignored(concatpath, options))
return;
if (DIFF_OPT_TST(options, REVERSE_DIFF)) {
#define DIFF_OPT_DIRTY_SUBMODULES (1 << 24)
#define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)
#define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)
+#define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)
#define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag)
#define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag)
/* this is set by diffcore for DIFF_FORMAT_PATCH */
int found_changes;
+ /* to support internal diff recursion by --follow hack*/
+ int found_follow;
+
FILE *file;
int close_file;
struct diff_filepair **queue;
int alloc;
int nr;
- int run;
};
#define DIFF_QUEUE_CLEAR(q) \
do { \
(q)->queue = NULL; \
(q)->nr = (q)->alloc = 0; \
- (q)->run = 0; \
} while (0)
extern struct diff_queue_struct diff_queued_diff;
case S_IFREG | 0644:
case S_IFREG | 0755:
case S_IFLNK:
+ case S_IFDIR:
case S_IFGITLINK:
/* ok */
break;
* another repository.
*/
} else if (inline_data) {
+ if (S_ISDIR(mode))
+ die("Directories cannot be specified 'inline': %s",
+ command_buf.buf);
if (p != uq.buf) {
strbuf_addstr(&uq, p);
p = uq.buf;
}
read_next_command();
parse_and_store_blob(&last_blob, sha1, 0);
- } else if (oe) {
- if (oe->type != OBJ_BLOB)
- die("Not a blob (actually a %s): %s",
- typename(oe->type), command_buf.buf);
} else {
- enum object_type type = sha1_object_info(sha1, NULL);
+ enum object_type expected = S_ISDIR(mode) ?
+ OBJ_TREE: OBJ_BLOB;
+ enum object_type type = oe ? oe->type :
+ sha1_object_info(sha1, NULL);
if (type < 0)
- die("Blob not found: %s", command_buf.buf);
- if (type != OBJ_BLOB)
- die("Not a blob (actually a %s): %s",
- typename(type), command_buf.buf);
+ die("%s not found: %s",
+ S_ISDIR(mode) ? "Tree" : "Blob",
+ command_buf.buf);
+ if (type != expected)
+ die("Not a %s (actually a %s): %s",
+ typename(expected), typename(type),
+ command_buf.buf);
}
tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
httpd_only="${httpd%% *}" # cut on first space
return
;;
+ *webrick*)
+ # server is started by running via generated webrick.rb in
+ # $fqgitdir/gitweb
+ full_httpd="$fqgitdir/gitweb/webrick.rb"
+ httpd_only="${httpd%% *}" # cut on first space
+ return
+ ;;
esac
httpd_only="$(echo $httpd | cut -f1 -d' ')"
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
webrick_conf () {
+ # webrick seems to have no way of passing arbitrary environment
+ # variables to the underlying CGI executable, so we wrap the
+ # actual gitweb.cgi using a shell script to force it
+ wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
+ cat > "$wrapper" <<EOF
+#!/bin/sh
+# we use this shell script wrapper around the real gitweb.cgi since
+# there appears to be no other way to pass arbitrary environment variables
+# into the CGI process
+GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
+export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
+exec $root/gitweb.cgi
+EOF
+ chmod +x "$wrapper"
+
+ # This assumes _ruby_ is in the user's $PATH. that's _one_
+ # portable way to run ruby, which could be installed anywhere, really.
# generate a standalone server script in $fqgitdir/gitweb.
cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
+#!/usr/bin/env ruby
require 'webrick'
-require 'yaml'
-options = YAML::load_file(ARGV[0])
-options[:StartCallback] = proc do
- File.open(options[:PidFile],"w") do |f|
- f.puts Process.pid
- end
-end
-options[:ServerType] = WEBrick::Daemon
+require 'logger'
+options = {
+ :Port => $port,
+ :DocumentRoot => "$root",
+ :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
+ :AccessLog => [
+ [ Logger.new('$fqgitdir/gitweb/access.log'),
+ WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
+ ],
+ :DirectoryIndex => ["gitweb.cgi"],
+ :CGIInterpreter => "$wrapper",
+ :StartCallback => lambda do
+ File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
+ end,
+ :ServerType => WEBrick::Daemon,
+}
+options[:BindAddress] = '127.0.0.1' if "$local" == "true"
server = WEBrick::HTTPServer.new(options)
['INT', 'TERM'].each do |signal|
trap(signal) {server.shutdown}
end
server.start
EOF
- # generate a shell script to invoke the above ruby script,
- # which assumes _ruby_ is in the user's $PATH. that's _one_
- # portable way to run ruby, which could be installed anywhere,
- # really.
- cat >"$fqgitdir/gitweb/$httpd" <<EOF
-#!/bin/sh
-exec ruby "$fqgitdir/gitweb/$httpd.rb" \$*
-EOF
- chmod +x "$fqgitdir/gitweb/$httpd"
-
- cat >"$conf" <<EOF
-:Port: $port
-:DocumentRoot: "$root"
-:DirectoryIndex: ["gitweb.cgi"]
-:PidFile: "$fqgitdir/pid"
-EOF
- test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
+ chmod +x "$fqgitdir/gitweb/$httpd.rb"
+ # configuration is embedded in server script file, webrick.rb
+ rm -f "$conf"
}
lighttpd_conf () {
last_status=0
rollup_status=0
+rerere=false
+
+files_to_merge() {
+ if test "$rerere" = true
+ then
+ git rerere status
+ else
+ git ls-files -u | sed -e 's/^[^ ]* //' | sort -u
+ fi
+}
+
if test $# -eq 0 ; then
- files=$(git ls-files -u | sed -e 's/^[^ ]* //' | sort -u)
+ cd_to_toplevel
+
+ if test -e "$GIT_DIR/MERGE_RR"
+ then
+ rerere=true
+ fi
+
+ files=$(files_to_merge)
if test -z "$files" ; then
echo "No files need merging"
exit 0
fi
- echo Merging the files: "$files"
- git ls-files -u |
- sed -e 's/^[^ ]* //' |
- sort -u |
+ printf "Merging:\n"
+ printf "$files\n"
+
+ files_to_merge |
while IFS= read i
do
if test $last_status -ne 0; then
"
unset newbase
strategy=recursive
+strategy_opts=
do_merge=
dotest="$GIT_DIR"/rebase-merge
prec=4
then
export GIT_MERGE_VERBOSITY=1
fi
- git-merge-$strategy "$cmt^" -- "$hd" "$cmt"
+ eval 'git-merge-$strategy' $strategy_opts '"$cmt^" -- "$hd" "$cmt"'
rv=$?
case "$rv" in
0)
-M|-m|--m|--me|--mer|--merg|--merge)
do_merge=t
;;
+ -X*|--strategy-option*)
+ case "$#,$1" in
+ 1,-X|1,--strategy-option)
+ usage ;;
+ *,-X|*,--strategy-option)
+ newopt="$2"
+ shift ;;
+ *,--strategy-option=*)
+ newopt="$(expr " $1" : ' --strategy-option=\(.*\)')" ;;
+ *,-X*)
+ newopt="$(expr " $1" : ' -X\(.*\)')" ;;
+ 1,*)
+ usage ;;
+ esac
+ strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--$newopt")"
+ do_merge=t
+ if test -n "$strategy"
+ then
+ strategy=recursive
+ fi
+ ;;
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
--strateg=*|--strategy=*|\
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
if test -z "$do_merge"
then
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
- $root_flag "$revisions" |
+ --no-renames $root_flag "$revisions" |
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
move_to_original_branch
ret=$?
sub cmd_dcommit {
my $head = shift;
+ command_noisy(qw/update-index --refresh/);
git_cmd_try { command_oneline(qw/diff-index --quiet HEAD/) }
'Cannot dcommit with a dirty index. Commit your changes first, '
. "or stash them with `git stash'.\n";
die("svn-remote.$remote: remote ref '$remote_ref' "
. "must start with 'refs/'\n")
unless $remote_ref =~ m{^refs/};
+ $local_ref = uri_decode($local_ref);
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
$r->{$remote}->{svm} = {} if $use_svm_props;
} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
. "must start with 'refs/'\n")
unless $remote_ref =~ m{^refs/};
+ $local_ref = uri_decode($local_ref);
my $rs = {
t => $t,
remote => $remote,
my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
unless ($gs) {
my $ref_id = $old_ref_id;
- $ref_id =~ s/\@\d+$//;
+ $ref_id =~ s/\@\d+-*$//;
$ref_id .= "\@$r";
# just grow a tail if we're not unique enough :x
$ref_id .= '-' while find_ref($ref_id);
- print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
my ($u, $p, $repo_id) = ($new_url, '', $ref_id);
if ($u =~ s#^\Q$url\E(/|$)##) {
$p = $u;
$u = $url;
$repo_id = $self->{repo_id};
}
- $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+ while (1) {
+ # It is possible to tag two different subdirectories at
+ # the same revision. If the url for an existing ref
+ # does not match, we must either find a ref with a
+ # matching url or create a new ref by growing a tail.
+ $gs = Git::SVN->init($u, $p, $repo_id, $ref_id, 1);
+ my (undef, $max_commit) = $gs->rev_map_max(1);
+ last if (!$max_commit);
+ my ($url) = ::cmt_metadata($max_commit);
+ last if ($url eq $gs->full_url);
+ $ref_id .= '-';
+ }
+ print STDERR "Initializing parent: $ref_id\n" unless $::_q > 1;
}
$gs
}
$self->{absent_dir} = {};
$self->{absent_file} = {};
$self->{gii} = $git_svn->tmp_index_do(sub { Git::IndexInfo->new });
+ $self->{pathnameencoding} = Git::config('svn.pathnameencoding');
$self;
}
sub git_path {
my ($self, $path) = @_;
+ if (my $enc = $self->{pathnameencoding}) {
+ require Encode;
+ Encode::from_to($path, 'UTF-8', $enc);
+ }
if ($self->{path_strip}) {
$path =~ s!$self->{path_strip}!! or
die "Failed to strip path '$path' ($self->{path_strip})\n";
sub repo_path {
my ($self, $path) = @_;
+ if (my $enc = $self->{pathnameencoding}) {
+ require Encode;
+ Encode::from_to($path, $enc, 'UTF-8');
+ }
$self->{path_prefix}.(defined $path ? $path : '');
}
# Leave it undefined (or set to 'undef') to turn off load checking.
our $maxload = 300;
+# configuration for 'highlight' (http://www.andre-simon.de/)
+# match by basename
+our %highlight_basename = (
+ #'Program' => 'py',
+ #'Library' => 'py',
+ 'SConstruct' => 'py', # SCons equivalent of Makefile
+ 'Makefile' => 'make',
+);
+# match by extension
+our %highlight_ext = (
+ # main extensions, defining name of syntax;
+ # see files in /usr/share/highlight/langDefs/ directory
+ map { $_ => $_ }
+ qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
+ # alternate extensions, see /etc/highlight/filetypes.conf
+ 'h' => 'c',
+ map { $_ => 'cpp' } qw(cxx c++ cc),
+ map { $_ => 'php' } qw(php3 php4),
+ map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi'
+ 'mak' => 'make',
+ map { $_ => 'xml' } qw(xhtml html htm),
+);
+
# You define site-wide feature defaults here; override them with
# $GITWEB_CONFIG as necessary.
our %feature = (
reset_timer();
evaluate_uri();
+ evaluate_gitweb_config();
check_loadavg();
+ # $projectroot and $projects_list might be set in gitweb config file
+ $projects_list ||= $projectroot;
+
evaluate_query_params();
evaluate_path_info();
evaluate_and_validate_params();
sub run {
evaluate_argv();
- evaluate_gitweb_config();
evaluate_git_version();
- # $projectroot and $projects_list might be set in gitweb config file
- $projects_list ||= $projectroot;
-
$pre_listen_hook->()
if $pre_listen_hook;
run_request();
- $pre_dispatch_hook->()
+ $post_dispatch_hook->()
if $post_dispatch_hook;
last REQUEST if ($is_last_request->());
sub guess_file_syntax {
my ($highlight, $mimetype, $file_name) = @_;
return undef unless ($highlight && defined $file_name);
-
- # configuration for 'highlight' (http://www.andre-simon.de/)
- # match by basename
- my %highlight_basename = (
- #'Program' => 'py',
- #'Library' => 'py',
- 'SConstruct' => 'py', # SCons equivalent of Makefile
- 'Makefile' => 'make',
- );
- # match by extension
- my %highlight_ext = (
- # main extensions, defining name of syntax;
- # see files in /usr/share/highlight/langDefs/ directory
- map { $_ => $_ }
- qw(py c cpp rb java css php sh pl js tex bib xml awk bat ini spec tcl),
- # alternate extensions, see /etc/highlight/filetypes.conf
- 'h' => 'c',
- map { $_ => 'cpp' } qw(cxx c++ cc),
- map { $_ => 'php' } qw(php3 php4),
- map { $_ => 'pl' } qw(perl pm), # perhaps also 'cgi'
- 'mak' => 'make',
- map { $_ => 'xml' } qw(xhtml html htm),
- );
-
my $basename = basename($file_name, '.in');
return $highlight_basename{$basename}
if exists $highlight_basename{$basename};
/* Internal API */
-/*
- * Output the next line for a graph.
- * This formats the next graph line into the specified strbuf. It is not
- * terminated with a newline.
- *
- * Returns 1 if the line includes the current commit, and 0 otherwise.
- * graph_next_line() will return 1 exactly once for each time
- * graph_update() is called.
- */
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb);
-
/*
* Output a padding line in the graph.
* This is similar to graph_next_line(). However, it is guaranteed to
/*
* The list of available column colors.
*/
-static char column_colors[][COLOR_MAXLEN] = {
+static const char *column_colors_ansi[] = {
GIT_COLOR_RED,
GIT_COLOR_GREEN,
GIT_COLOR_YELLOW,
GIT_COLOR_BOLD_BLUE,
GIT_COLOR_BOLD_MAGENTA,
GIT_COLOR_BOLD_CYAN,
+ GIT_COLOR_RESET,
};
-#define COLUMN_COLORS_MAX (ARRAY_SIZE(column_colors))
+#define COLUMN_COLORS_ANSI_MAX (ARRAY_SIZE(column_colors_ansi) - 1)
+
+static const char **column_colors;
+static unsigned short column_colors_max;
-static const char *column_get_color_code(const struct column *c)
+void graph_set_column_colors(const char **colors, unsigned short colors_max)
{
- return column_colors[c->color];
+ column_colors = colors;
+ column_colors_max = colors_max;
+}
+
+static const char *column_get_color_code(unsigned short color)
+{
+ return column_colors[color];
}
static void strbuf_write_column(struct strbuf *sb, const struct column *c,
char col_char)
{
- if (c->color < COLUMN_COLORS_MAX)
- strbuf_addstr(sb, column_get_color_code(c));
+ if (c->color < column_colors_max)
+ strbuf_addstr(sb, column_get_color_code(c->color));
strbuf_addch(sb, col_char);
- if (c->color < COLUMN_COLORS_MAX)
- strbuf_addstr(sb, GIT_COLOR_RESET);
+ if (c->color < column_colors_max)
+ strbuf_addstr(sb, column_get_color_code(column_colors_max));
}
struct git_graph {
struct git_graph *graph_init(struct rev_info *opt)
{
struct git_graph *graph = xmalloc(sizeof(struct git_graph));
+
+ if (!column_colors)
+ graph_set_column_colors(column_colors_ansi,
+ COLUMN_COLORS_ANSI_MAX);
+
graph->commit = NULL;
graph->revs = opt;
graph->num_parents = 0;
* always increment it for the first commit we output.
* This way we start at 0 for the first commit.
*/
- graph->default_column_color = COLUMN_COLORS_MAX - 1;
+ graph->default_column_color = column_colors_max - 1;
/*
* Allocate a reasonably large default number of columns
static unsigned short graph_get_current_column_color(const struct git_graph *graph)
{
if (!DIFF_OPT_TST(&graph->revs->diffopt, COLOR_DIFF))
- return COLUMN_COLORS_MAX;
+ return column_colors_max;
return graph->default_column_color;
}
static void graph_increment_column_color(struct git_graph *graph)
{
graph->default_column_color = (graph->default_column_color + 1) %
- COLUMN_COLORS_MAX;
+ column_colors_max;
}
static unsigned short graph_find_commit_color(const struct git_graph *graph,
graph_update_state(graph, GRAPH_PADDING);
}
-static int graph_next_line(struct git_graph *graph, struct strbuf *sb)
+int graph_next_line(struct git_graph *graph, struct strbuf *sb)
{
switch (graph->state) {
case GRAPH_PADDING:
/* A graph is a pointer to this opaque structure */
struct git_graph;
+/*
+ * Set up a custom scheme for column colors.
+ *
+ * The default column color scheme inserts ANSI color escapes to colorize
+ * the graph. The various color escapes are stored in an array of strings
+ * where each entry corresponds to a color, except for the last entry,
+ * which denotes the escape for resetting the color back to the default.
+ * When generating the graph, strings from this array are inserted before
+ * and after the various column characters.
+ *
+ * This function allows you to enable a custom array of color escapes.
+ * The 'colors_max' argument is the index of the last "reset" entry.
+ *
+ * This functions must be called BEFORE graph_init() is called.
+ */
+void graph_set_column_colors(const char **colors, unsigned short colors_max);
+
/*
* Create a new struct git_graph.
*/
*/
int graph_is_commit_finished(struct git_graph const *graph);
+/*
+ * Output the next line for a graph.
+ * This formats the next graph line into the specified strbuf. It is not
+ * terminated with a newline.
+ *
+ * Returns 1 if the line includes the current commit, and 0 otherwise.
+ * graph_next_line() will return 1 exactly once for each time
+ * graph_update() is called.
+ */
+int graph_next_line(struct git_graph *graph, struct strbuf *sb);
+
/*
* graph_show_*: helper functions for printing to stdout
if (parse_commit(commit) != 0)
printf("(bad commit)\n");
else {
- const char *s;
- int len;
- for (s = commit->buffer; *s; s++)
- if (*s == '\n' && s[1] == '\n') {
- s += 2;
- break;
- }
- for (len = 0; s[len] && '\n' != s[len]; len++)
- ; /* do nothing */
- printf("%.*s\n", len, s);
+ const char *title;
+ int len = find_commit_subject(commit->buffer, &title);
+ if (len)
+ printf("%.*s\n", len, title);
}
}
}
struct string_list *b_renames)
{
int clean_merge = 1, i, j;
- struct string_list a_by_dst = {NULL, 0, 0, 0}, b_by_dst = {NULL, 0, 0, 0};
+ struct string_list a_by_dst = STRING_LIST_INIT_NODUP;
+ struct string_list b_by_dst = STRING_LIST_INIT_NODUP;
const struct rename *sre;
for (i = 0; i < a_renames->nr; i++) {
strbuf_release(&globbuf);
}
-static int string_list_add_refs_from_list(struct string_list_item *item,
- void *cb)
-{
- struct string_list *list = cb;
- string_list_add_refs_by_glob(list, item->string);
- return 0;
-}
-
static int notes_display_config(const char *k, const char *v, void *cb)
{
int *load_refs = cb;
load_subtree(t, &root_tree, t->root, 0);
}
-struct load_notes_cb_data {
- int counter;
- struct notes_tree **trees;
-};
-
-static int load_one_display_note_ref(struct string_list_item *item,
- void *cb_data)
-{
- struct load_notes_cb_data *c = cb_data;
- struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
- init_notes(t, item->string, combine_notes_ignore, 0);
- c->trees[c->counter++] = t;
- return 0;
-}
-
struct notes_tree **load_notes_trees(struct string_list *refs)
{
+ struct string_list_item *item;
+ int counter = 0;
struct notes_tree **trees;
- struct load_notes_cb_data cb_data;
trees = xmalloc((refs->nr+1) * sizeof(struct notes_tree *));
- cb_data.counter = 0;
- cb_data.trees = trees;
- for_each_string_list(refs, load_one_display_note_ref, &cb_data);
- trees[cb_data.counter] = NULL;
+ for_each_string_list_item(item, refs) {
+ struct notes_tree *t = xcalloc(1, sizeof(struct notes_tree));
+ init_notes(t, item->string, combine_notes_ignore, 0);
+ trees[counter++] = t;
+ }
+ trees[counter] = NULL;
return trees;
}
git_config(notes_display_config, &load_config_refs);
- if (opt && opt->extra_notes_refs)
- for_each_string_list(opt->extra_notes_refs,
- string_list_add_refs_from_list,
- &display_notes_refs);
+ if (opt && opt->extra_notes_refs) {
+ struct string_list_item *item;
+ for_each_string_list_item(item, opt->extra_notes_refs)
+ string_list_add_refs_by_glob(&display_notes_refs,
+ item->string);
+ }
display_notes_trees = load_notes_trees(&display_notes_refs);
string_list_clear(&display_notes_refs, 0);
return 0;
}
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+ char *p, *q;
+ int i;
+ p = name;
+ for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+ while (*p && *p != '/')
+ p++;
+ /* tolerate duplicate slashes; see check_ref_format() */
+ while (*p == '/')
+ p++;
+ }
+ for (q = p; *q; q++)
+ ;
+ while (1) {
+ while (q > p && *q != '/')
+ q--;
+ while (q > p && *(q-1) == '/')
+ q--;
+ if (q == p)
+ break;
+ *q = '\0';
+ if (rmdir(git_path("%s", name)))
+ break;
+ }
+}
+
/* make sure nobody touched the ref, and unlink */
static void prune_ref(struct ref_to_prune *r)
{
if (lock) {
unlink_or_warn(git_path("%s", r->name));
unlock_ref(lock);
+ try_remove_empty_parents(r->name);
}
}
int size = ondisk_ce_size(ce);
struct ondisk_cache_entry *ondisk = xcalloc(1, size);
char *name;
+ int result;
ondisk->ctime.sec = htonl(ce->ce_ctime.sec);
ondisk->mtime.sec = htonl(ce->ce_mtime.sec);
name = ondisk->name;
memcpy(name, ce->name, ce_namelen(ce));
- return ce_write(c, fd, ondisk, size);
+ result = ce_write(c, fd, ondisk, size);
+ free(ondisk);
+ return result;
}
int write_index(struct index_state *istate, int newfd)
void ref_remove_duplicates(struct ref *ref_map)
{
- struct string_list refs = { NULL, 0, 0, 0 };
+ struct string_list refs = STRING_LIST_INIT_NODUP;
struct string_list_item *item = NULL;
struct ref *prev = NULL, *next = NULL;
for (; ref_map; prev = ref_map, ref_map = next) {
struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
{
struct ref *ref, *stale_refs = NULL;
- struct string_list ref_names = { NULL, 0, 0, 0 };
+ struct string_list ref_names = STRING_LIST_INIT_NODUP;
struct stale_heads_info info;
info.remote = remote;
info.ref_names = &ref_names;
static int do_plain_rerere(struct string_list *rr, int fd)
{
- struct string_list conflict = { NULL, 0, 0, 1 };
- struct string_list update = { NULL, 0, 0, 1 };
+ struct string_list conflict = STRING_LIST_INIT_DUP;
+ struct string_list update = STRING_LIST_INIT_DUP;
int i;
find_conflict(&conflict);
int rerere(int flags)
{
- struct string_list merge_rr = { NULL, 0, 0, 1 };
+ struct string_list merge_rr = STRING_LIST_INIT_DUP;
int fd;
fd = setup_rerere(&merge_rr, flags);
int rerere_forget(const char **pathspec)
{
int i, fd;
- struct string_list conflict = { NULL, 0, 0, 1 };
- struct string_list merge_rr = { NULL, 0, 0, 1 };
+ struct string_list conflict = STRING_LIST_INIT_DUP;
+ struct string_list merge_rr = STRING_LIST_INIT_DUP;
if (read_cache() < 0)
return error("Could not read index");
ui->mode[stage - 1] = ce->ce_mode;
}
-static int write_one(struct string_list_item *item, void *cbdata)
+void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
{
- struct strbuf *sb = cbdata;
- struct resolve_undo_info *ui = item->util;
- int i;
+ struct string_list_item *item;
+ for_each_string_list_item(item, resolve_undo) {
+ struct resolve_undo_info *ui = item->util;
+ int i;
- if (!ui)
- return 0;
- strbuf_addstr(sb, item->string);
- strbuf_addch(sb, 0);
- for (i = 0; i < 3; i++)
- strbuf_addf(sb, "%o%c", ui->mode[i], 0);
- for (i = 0; i < 3; i++) {
- if (!ui->mode[i])
+ if (!ui)
continue;
- strbuf_add(sb, ui->sha1[i], 20);
+ strbuf_addstr(sb, item->string);
+ strbuf_addch(sb, 0);
+ for (i = 0; i < 3; i++)
+ strbuf_addf(sb, "%o%c", ui->mode[i], 0);
+ for (i = 0; i < 3; i++) {
+ if (!ui->mode[i])
+ continue;
+ strbuf_add(sb, ui->sha1[i], 20);
+ }
}
- return 0;
-}
-
-void resolve_undo_write(struct strbuf *sb, struct string_list *resolve_undo)
-{
- for_each_string_list(resolve_undo, write_one, sb);
}
struct string_list *resolve_undo_read(const char *data, unsigned long size)
{
const unsigned char *repl = lookup_replace_object(sha1);
void *data = read_object(repl, type, size);
+ char *path;
/* die if we replaced an object with one that does not exist */
if (!data && repl != sha1)
sha1_to_hex(repl), sha1_to_hex(sha1));
/* legacy behavior is to die on corrupted objects */
- if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
- die("object %s is corrupted", sha1_to_hex(repl));
+ if (!data) {
+ if (has_loose_object(repl)) {
+ path = sha1_file_name(sha1);
+ die("loose object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+ }
+ if (has_packed_and_bad(repl)) {
+ path = sha1_pack_name(sha1);
+ die("packed object %s (stored in %s) is corrupted", sha1_to_hex(repl), path);
+ }
+ }
if (replacement)
*replacement = repl;
unsigned int strdup_strings:1;
};
+#define STRING_LIST_INIT_NODUP { NULL, 0, 0, 0 }
+#define STRING_LIST_INIT_DUP { NULL, 0, 0, 1 }
+
void print_string_list(const struct string_list *p, const char *text);
void string_list_clear(struct string_list *list, int free_util);
typedef void (*string_list_clear_func_t)(void *p, const char *str);
void string_list_clear_func(struct string_list *list, string_list_clear_func_t clearfunc);
-/* Use this function to iterate over each item */
+/* Use this function or the macro below to iterate over each item */
typedef int (*string_list_each_func_t)(struct string_list_item *, void *);
int for_each_string_list(struct string_list *list,
string_list_each_func_t, void *cb_data);
+#define for_each_string_list_item(item,list) \
+ for (item = (list)->items; item < (list)->items + (list)->nr; ++item)
/* Use these functions only on sorted lists: */
int string_list_has_string(const struct string_list *list, const char *string);
#include "revision.h"
#include "run-command.h"
#include "diffcore.h"
+#include "string-list.h"
+
+struct string_list config_name_for_path;
+struct string_list config_ignore_for_name;
static int add_submodule_odb(const char *path)
{
return ret;
}
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+ const char *path)
+{
+ struct string_list_item *path_option, *ignore_option;
+ path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+ if (path_option) {
+ ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
+ if (ignore_option)
+ handle_ignore_submodules_arg(diffopt, ignore_option->util);
+ }
+}
+
+static int submodule_config(const char *var, const char *value, void *cb)
+{
+ if (!prefixcmp(var, "submodule."))
+ return parse_submodule_config_option(var, value);
+ return 0;
+}
+
+void gitmodules_config(void)
+{
+ const char *work_tree = get_git_work_tree();
+ if (work_tree) {
+ struct strbuf gitmodules_path = STRBUF_INIT;
+ strbuf_addstr(&gitmodules_path, work_tree);
+ strbuf_addstr(&gitmodules_path, "/.gitmodules");
+ git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
+ strbuf_release(&gitmodules_path);
+ }
+}
+
+int parse_submodule_config_option(const char *var, const char *value)
+{
+ int len;
+ struct string_list_item *config;
+ struct strbuf submodname = STRBUF_INIT;
+
+ var += 10; /* Skip "submodule." */
+
+ len = strlen(var);
+ if ((len > 5) && !strcmp(var + len - 5, ".path")) {
+ strbuf_add(&submodname, var, len - 5);
+ config = unsorted_string_list_lookup(&config_name_for_path, value);
+ if (config)
+ free(config->util);
+ else
+ config = string_list_append(&config_name_for_path, xstrdup(value));
+ config->util = strbuf_detach(&submodname, NULL);
+ strbuf_release(&submodname);
+ } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
+ if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
+ strcmp(value, "all") && strcmp(value, "none")) {
+ warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
+ return 0;
+ }
+
+ strbuf_add(&submodname, var, len - 7);
+ config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
+ if (config)
+ free(config->util);
+ else
+ config = string_list_append(&config_ignore_for_name,
+ strbuf_detach(&submodname, NULL));
+ strbuf_release(&submodname);
+ config->util = xstrdup(value);
+ return 0;
+ }
+ return 0;
+}
+
void handle_ignore_submodules_arg(struct diff_options *diffopt,
const char *arg)
{
+ DIFF_OPT_CLR(diffopt, IGNORE_SUBMODULES);
+ DIFF_OPT_CLR(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
+ DIFF_OPT_CLR(diffopt, IGNORE_DIRTY_SUBMODULES);
+
if (!strcmp(arg, "all"))
DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
else if (!strcmp(arg, "untracked"))
DIFF_OPT_SET(diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
else if (!strcmp(arg, "dirty"))
DIFF_OPT_SET(diffopt, IGNORE_DIRTY_SUBMODULES);
- else
+ else if (strcmp(arg, "none"))
die("bad --ignore-submodules argument: %s", arg);
}
struct diff_options;
+void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
+ const char *path);
+void gitmodules_config();
+int parse_submodule_config_option(const char *var, const char *value);
void handle_ignore_submodules_arg(struct diff_options *diffopt, const char *);
void show_submodule_summary(FILE *f, const char *path,
unsigned char one[20], unsigned char two[20],
/trash directory*
/test-results
+/.prove
$(RM) -r 'trash directory'.* test-results
$(RM) t????/cvsroot/CVSROOT/?*
$(RM) -r valgrind/bin
+ $(RM) .prove
aggregate-results-and-cleanup: $(T)
$(MAKE) aggregate-results
test_expect_success \
'a/b (untracked) vs a, plus c/d case test.' \
- '! git read-tree -u -m "$treeH" "$treeM" &&
+ 'test_must_fail git read-tree -u -m "$treeH" "$treeM" &&
git ls-files --stage &&
test -f a/b'
--- /dev/null
+#!/bin/sh
+
+test_description='checkout '
+
+. ./test-lib.sh
+
+# Arguments: <branch> <sha> [<checkout options>]
+#
+# Runs "git checkout" to switch to <branch>, testing that
+#
+# 1) we are on the specified branch, <branch>;
+# 2) HEAD is <sha>; if <sha> is not specified, the old HEAD is used.
+#
+# If <checkout options> is not specified, "git checkout" is run with -b.
+do_checkout() {
+ exp_branch=$1 &&
+ exp_ref="refs/heads/$exp_branch" &&
+
+ # if <sha> is not specified, use HEAD.
+ exp_sha=${2:-$(git rev-parse --verify HEAD)} &&
+
+ # default options for git checkout: -b
+ if [ -z "$3" ]; then
+ opts="-b"
+ else
+ opts="$3"
+ fi
+
+ git checkout $opts $exp_branch $exp_sha &&
+
+ test $exp_ref = $(git rev-parse --symbolic-full-name HEAD) &&
+ test $exp_sha = $(git rev-parse --verify HEAD)
+}
+
+test_dirty_unmergeable() {
+ ! git diff --exit-code >/dev/null
+}
+
+setup_dirty_unmergeable() {
+ echo >>file1 change2
+}
+
+test_dirty_mergeable() {
+ ! git diff --cached --exit-code >/dev/null
+}
+
+setup_dirty_mergeable() {
+ echo >file2 file2 &&
+ git add file2
+}
+
+test_expect_success 'setup' '
+ test_commit initial file1 &&
+ HEAD1=$(git rev-parse --verify HEAD) &&
+
+ test_commit change1 file1 &&
+ HEAD2=$(git rev-parse --verify HEAD) &&
+
+ git branch -m branch1
+'
+
+test_expect_success 'checkout -b to a new branch, set to HEAD' '
+ do_checkout branch2
+'
+
+test_expect_success 'checkout -b to a new branch, set to an explicit ref' '
+ git checkout branch1 &&
+ git branch -D branch2 &&
+
+ do_checkout branch2 $HEAD1
+'
+
+test_expect_success 'checkout -b to a new branch with unmergeable changes fails' '
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_unmergeable &&
+ test_must_fail do_checkout branch2 $HEAD1 &&
+ test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with unmergeable changes discards changes' '
+ # still dirty and on branch1
+ do_checkout branch2 $HEAD1 "-f -b" &&
+ test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -b to a new branch preserves mergeable changes' '
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 &&
+ test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -b to a new branch with mergeable changes discards changes' '
+ # clean up from previous test
+ git reset --hard &&
+
+ git checkout branch1 &&
+
+ # clean up from previous test
+ git branch -D branch2 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 "-f -b" &&
+ test_must_fail test_dirty_mergeable
+'
+
+test_expect_success 'checkout -b to an existing branch fails' '
+ git reset --hard HEAD &&
+
+ test_must_fail do_checkout branch2 $HEAD2
+'
+
+test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
+ git checkout branch1 &&
+
+ do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch from detached HEAD resets branch to HEAD' '
+ git checkout $(git rev-parse --verify HEAD) &&
+
+ do_checkout branch2 "" -B
+'
+
+test_expect_success 'checkout -B to an existing branch with an explicit ref resets branch to that ref' '
+ git checkout branch1 &&
+
+ do_checkout branch2 $HEAD1 -B
+'
+
+test_expect_success 'checkout -B to an existing branch with unmergeable changes fails' '
+ git checkout branch1 &&
+
+ setup_dirty_unmergeable &&
+ test_must_fail do_checkout branch2 $HEAD1 -B &&
+ test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with unmergeable changes discards changes' '
+ # still dirty and on branch1
+ do_checkout branch2 $HEAD1 "-f -B" &&
+ test_must_fail test_dirty_unmergeable
+'
+
+test_expect_success 'checkout -B to an existing branch preserves mergeable changes' '
+ git checkout branch1 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 -B &&
+ test_dirty_mergeable
+'
+
+test_expect_success 'checkout -f -B to an existing branch with mergeable changes discards changes' '
+ # clean up from previous test
+ git reset --hard &&
+
+ git checkout branch1 &&
+
+ setup_dirty_mergeable &&
+ do_checkout branch2 $HEAD1 "-f -B" &&
+ test_must_fail test_dirty_mergeable
+'
+
+test_done
! test -f .git/refs/heads/f
'
+test_expect_success 'see if git pack-refs --prune removes empty dirs' '
+ git branch r/s/t &&
+ git pack-refs --all --prune &&
+ ! test -e .git/refs/heads/r
+'
+
test_expect_success \
'git branch g should work when git branch g/h has been deleted' \
'git branch g/h &&
test_expect_success 'verify note removal with -F /dev/null' '
git log -4 > output &&
test_cmp expect-rm-F output &&
- ! git notes show
+ test_must_fail git notes show
'
test_expect_success 'do not create empty note with -m "" (setup)' '
test_expect_success 'verify non-creation of note with -m ""' '
git log -4 > output &&
test_cmp expect-rm-F output &&
- ! git notes show
+ test_must_fail git notes show
'
cat > expect-combine_m_and_F << EOF
test_expect_success 'verify note removal with "git notes remove"' '
git log -4 > output &&
test_cmp expect-rm-remove output &&
- ! git notes show HEAD^
+ test_must_fail git notes show HEAD^
'
cat > expect << EOF
test_expect_success 'verify that commits are gone' '
- ! git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
'
test_expect_success 'verify that notes are gone' '
- ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
'
test_expect_success 'verify that notes are gone' '
- ! git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
- ! git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
+ test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+ test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
'
GIT_AUTHOR_EMAIL=bogus@email@address
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
-test_expect_success \
- 'prepare repository with topic branches' \
- 'git config core.logAllRefUpdates true &&
- echo First > A &&
- git update-index --add A &&
- git commit -m "Add A." &&
- git checkout -b my-topic-branch &&
- echo Second > B &&
- git update-index --add B &&
- git commit -m "Add B." &&
- git checkout -f master &&
- echo Third >> A &&
- git update-index A &&
- git commit -m "Modify A." &&
- git checkout -b side my-topic-branch &&
- echo Side >> C &&
- git add C &&
- git commit -m "Add C" &&
- git checkout -b nonlinear my-topic-branch &&
- echo Edit >> B &&
- git add B &&
- git commit -m "Modify B" &&
- git merge side &&
- git checkout -b upstream-merged-nonlinear &&
- git merge master &&
- git checkout -f my-topic-branch &&
- git tag topic
+test_expect_success 'prepare repository with topic branches' '
+ git config core.logAllRefUpdates true &&
+ echo First >A &&
+ git update-index --add A &&
+ git commit -m "Add A." &&
+ git checkout -b force-3way &&
+ echo Dummy >Y &&
+ git update-index --add Y &&
+ git commit -m "Add Y." &&
+ git checkout -b filemove &&
+ git reset --soft master &&
+ mkdir D &&
+ git mv A D/A &&
+ git commit -m "Move A." &&
+ git checkout -b my-topic-branch master &&
+ echo Second >B &&
+ git update-index --add B &&
+ git commit -m "Add B." &&
+ git checkout -f master &&
+ echo Third >>A &&
+ git update-index A &&
+ git commit -m "Modify A." &&
+ git checkout -b side my-topic-branch &&
+ echo Side >>C &&
+ git add C &&
+ git commit -m "Add C" &&
+ git checkout -b nonlinear my-topic-branch &&
+ echo Edit >>B &&
+ git add B &&
+ git commit -m "Modify B" &&
+ git merge side &&
+ git checkout -b upstream-merged-nonlinear &&
+ git merge master &&
+ git checkout -f my-topic-branch &&
+ git tag topic
'
test_expect_success 'rebase on dirty worktree' '
- echo dirty >> A &&
- test_must_fail git rebase master'
+ echo dirty >>A &&
+ test_must_fail git rebase master
+'
test_expect_success 'rebase on dirty cache' '
- git add A &&
- test_must_fail git rebase master'
+ git add A &&
+ test_must_fail git rebase master
+'
test_expect_success 'rebase against master' '
- git reset --hard HEAD &&
- git rebase master'
+ git reset --hard HEAD &&
+ git rebase master
+'
test_expect_success 'rebase against master twice' '
- git rebase master >out &&
- grep "Current branch my-topic-branch is up to date" out
+ git rebase master >out &&
+ grep "Current branch my-topic-branch is up to date" out
'
test_expect_success 'rebase against master twice with --force' '
- git rebase --force-rebase master >out &&
- grep "Current branch my-topic-branch is up to date, rebase forced" out
+ git rebase --force-rebase master >out &&
+ grep "Current branch my-topic-branch is up to date, rebase forced" out
'
test_expect_success 'rebase against master twice from another branch' '
- git checkout my-topic-branch^ &&
- git rebase master my-topic-branch >out &&
- grep "Current branch my-topic-branch is up to date" out
+ git checkout my-topic-branch^ &&
+ git rebase master my-topic-branch >out &&
+ grep "Current branch my-topic-branch is up to date" out
'
test_expect_success 'rebase fast-forward to master' '
- git checkout my-topic-branch^ &&
- git rebase my-topic-branch >out &&
- grep "Fast-forwarded HEAD to my-topic-branch" out
+ git checkout my-topic-branch^ &&
+ git rebase my-topic-branch >out &&
+ grep "Fast-forwarded HEAD to my-topic-branch" out
'
-test_expect_success \
- 'the rebase operation should not have destroyed author information' \
- '! (git log | grep "Author:" | grep "<>")'
+test_expect_success 'the rebase operation should not have destroyed author information' '
+ ! (git log | grep "Author:" | grep "<>")
+'
-test_expect_success \
- 'the rebase operation should not have destroyed author information (2)' \
- "git log -1 | grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'"
+test_expect_success 'the rebase operation should not have destroyed author information (2)' "
+ git log -1 |
+ grep 'Author: $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL>'
+"
test_expect_success 'HEAD was detached during rebase' '
- test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
+ test $(git rev-parse HEAD@{1}) != $(git rev-parse my-topic-branch@{1})
'
test_expect_success 'rebase after merge master' '
- git reset --hard topic &&
- git merge master &&
- git rebase master &&
- ! (git show | grep "^Merge:")
+ git reset --hard topic &&
+ git merge master &&
+ git rebase master &&
+ ! (git show | grep "^Merge:")
'
test_expect_success 'rebase of history with merges is linearized' '
- git checkout nonlinear &&
- test 4 = $(git rev-list master.. | wc -l) &&
- git rebase master &&
- test 3 = $(git rev-list master.. | wc -l)
+ git checkout nonlinear &&
+ test 4 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
'
-test_expect_success \
- 'rebase of history with merges after upstream merge is linearized' '
- git checkout upstream-merged-nonlinear &&
- test 5 = $(git rev-list master.. | wc -l) &&
- git rebase master &&
- test 3 = $(git rev-list master.. | wc -l)
+test_expect_success 'rebase of history with merges after upstream merge is linearized' '
+ git checkout upstream-merged-nonlinear &&
+ test 5 = $(git rev-list master.. | wc -l) &&
+ git rebase master &&
+ test 3 = $(git rev-list master.. | wc -l)
'
test_expect_success 'rebase a single mode change' '
- git checkout master &&
- echo 1 > X &&
- git add X &&
- test_tick &&
- git commit -m prepare &&
- git checkout -b modechange HEAD^ &&
- echo 1 > X &&
- git add X &&
- test_chmod +x A &&
- test_tick &&
- git commit -m modechange &&
- GIT_TRACE=1 git rebase master
+ git checkout master &&
+ echo 1 >X &&
+ git add X &&
+ test_tick &&
+ git commit -m prepare &&
+ git checkout -b modechange HEAD^ &&
+ echo 1 >X &&
+ git add X &&
+ test_chmod +x A &&
+ test_tick &&
+ git commit -m modechange &&
+ GIT_TRACE=1 git rebase master
+'
+
+test_expect_success 'rebase is not broken by diff.renames' '
+ git config diff.renames copies &&
+ test_when_finished "git config --unset diff.renames" &&
+ git checkout filemove &&
+ GIT_TRACE=1 git rebase force-3way
+'
+
+test_expect_success 'setup: recover' '
+ test_might_fail git rebase --abort &&
+ git reset --hard &&
+ git checkout modechange
'
test_expect_success 'Show verbose error when HEAD could not be detached' '
- : > B &&
- test_must_fail git rebase topic 2> output.err > output.out &&
- grep "Untracked working tree file .B. would be overwritten" output.err
+ >B &&
+ test_must_fail git rebase topic 2>output.err >output.out &&
+ grep "Untracked working tree file .B. would be overwritten" output.err
'
rm -f B
test_expect_success 'dump usage when upstream arg is missing' '
- git checkout -b usage topic &&
- test_must_fail git rebase 2>error1 &&
- grep "[Uu]sage" error1 &&
- test_must_fail git rebase --abort 2>error2 &&
- grep "No rebase in progress" error2 &&
- test_must_fail git rebase --onto master 2>error3 &&
- grep "[Uu]sage" error3 &&
- ! grep "can.t shift" error3
+ git checkout -b usage topic &&
+ test_must_fail git rebase 2>error1 &&
+ grep "[Uu]sage" error1 &&
+ test_must_fail git rebase --abort 2>error2 &&
+ grep "No rebase in progress" error2 &&
+ test_must_fail git rebase --onto master 2>error3 &&
+ grep "[Uu]sage" error3 &&
+ ! grep "can.t shift" error3
'
test_expect_success 'rebase -q is quiet' '
- git checkout -b quiet topic &&
- git rebase -q master > output.out 2>&1 &&
- test ! -s output.out
+ git checkout -b quiet topic &&
+ git rebase -q master >output.out 2>&1 &&
+ test ! -s output.out
'
test_expect_success 'Rebase a commit that sprinkles CRs in' '
git rebase --merge side
'
+test_expect_success 'rebase -Xtheirs' '
+ git checkout -b conflicting master~2 &&
+ echo "AB $T" >> original &&
+ git commit -mconflicting original &&
+ git rebase -Xtheirs master &&
+ grep AB original &&
+ ! grep 11 original
+'
+
test_expect_success 'merge and rebase should match' '
git diff-tree -r test-rebase test-merge >difference &&
if test -s difference
# G2 = same changes as G
test_expect_success 'skip same-resolution merges with -p' '
git checkout H &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit L file1 23 &&
git checkout I &&
test_commit G2 file1 3 &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit J file1 23 &&
test_commit K file7 file7 &&
git rebase -i -p L &&
# G2 = different changes as G
test_expect_success 'keep different-resolution merges with -p' '
git checkout H &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit L2 file1 23 &&
git checkout I &&
test_commit G3 file1 4 &&
- ! git merge E &&
+ test_must_fail git merge E &&
test_commit J2 file1 24 &&
test_commit K2 file7 file7 &&
test_must_fail git rebase -i -p L2 &&
git checkout -b empty-branch &&
test_tick &&
- git commit --allow-empty -m "empty"
+ git commit --allow-empty -m "empty" &&
+
+ echo third >> file1 &&
+ git add file1 &&
+ test_tick &&
+ git commit --allow-empty-message -m ""
'
test_expect_success 'cherry-pick an empty commit' '
+ git checkout master && {
+ git cherry-pick empty-branch^
+ test "$?" = 1
+ }
+'
+
+test_expect_success 'index lockfile was removed' '
+
+ test ! -f .git/index.lock
+
+'
+
+test_expect_success 'cherry-pick a commit with an empty message' '
git checkout master && {
git cherry-pick empty-branch
test "$?" = 1
! test -s actual4
'
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.git/config]' '
+ git config diff.ignoreSubmodules all &&
+ git diff HEAD >actual &&
+ ! test -s actual &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore all &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git config submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git diff HEAD --ignore-submodules=none >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (work tree, refs match) [.gitmodules]' '
+ git config diff.ignoreSubmodules dirty &&
+ git diff HEAD >actual &&
+ ! test -s actual &&
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config -f .gitmodules submodule.subname.ignore all &&
+ git config -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config -f .gitmodules submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ sed -e "1,/^@@/d" actual3 >actual3.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual3.body &&
+ git config -f .gitmodules submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ git config --unset diff.ignoreSubmodules &&
+ rm .gitmodules
+'
+
test_expect_success 'git diff HEAD with dirty submodule (index, refs match)' '
(
cd sub &&
! test -s actual4
'
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.git/config]' '
+ git config submodule.subname.ignore all &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ ! test -s actual3 &&
+ git config submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git diff --ignore-submodules=none HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff HEAD with dirty submodule (untracked, refs match) [.gitmodules]' '
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD >actual2 &&
+ ! test -s actual2 &&
+ git config -f .gitmodules submodule.subname.ignore untracked &&
+ git diff HEAD >actual3 &&
+ ! test -s actual3 &&
+ git config -f .gitmodules submodule.subname.ignore dirty &&
+ git diff HEAD >actual4 &&
+ ! test -s actual4 &&
+ git config submodule.subname.ignore none &&
+ git config submodule.subname.path sub &&
+ git diff HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subprev $subprev-dirty &&
+ test_cmp expect.body actual.body &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ rm .gitmodules
+'
+
+test_expect_success 'git diff between submodule commits' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git diff --ignore-submodules HEAD^..HEAD >actual &&
+ ! test -s actual
+'
+
+test_expect_success 'git diff between submodule commits [.git/config]' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore dirty &&
+ git config submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config submodule.subname.ignore all &&
+ git diff HEAD^..HEAD >actual &&
+ ! test -s actual &&
+ git diff --ignore-submodules=dirty HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ git config --remove-section submodule.subname
+'
+
+test_expect_success 'git diff between submodule commits [.gitmodules]' '
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ test_cmp expect.body actual.body &&
+ git config -f .gitmodules submodule.subname.ignore all &&
+ git diff HEAD^..HEAD >actual &&
+ ! test -s actual &&
+ git config submodule.subname.ignore dirty &&
+ git config submodule.subname.path sub &&
+ git diff HEAD^..HEAD >actual &&
+ sed -e "1,/^@@/d" actual >actual.body &&
+ expect_from_to >expect.body $subtip $subprev &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname &&
+ rm .gitmodules
+'
+
test_expect_success 'git diff (empty submodule dir)' '
: >empty &&
rm -rf sub/* sub/.git &&
. ./test-lib.sh
-cat >msg <<EOF
-second
-
-Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
-eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
-voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
-kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
-ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
-tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
-vero eos et accusam et justo duo dolores et ea rebum.
-
- Duis autem vel eum iriure dolor in hendrerit in vulputate velit
- esse molestie consequat, vel illum dolore eu feugiat nulla facilisis
- at vero eros et accumsan et iusto odio dignissim qui blandit
- praesent luptatum zzril delenit augue duis dolore te feugait nulla
- facilisi.
-
-
-Lorem ipsum dolor sit amet,
-consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
-laoreet dolore magna aliquam erat volutpat.
-
- git
- ---
- +++
-
-Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
-lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
-dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
-dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
-dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
-feugait nulla facilisi.
-EOF
-
-cat >failmail <<EOF
-From foo@example.com Fri May 23 10:43:49 2008
-From: foo@example.com
-To: bar@example.com
-Subject: Re: [RFC/PATCH] git-foo.sh
-Date: Fri, 23 May 2008 05:23:42 +0200
-
-Sometimes we have to find out that there's nothing left.
-
-EOF
-
-cat >pine <<EOF
-From MAILER-DAEMON Fri May 23 10:43:49 2008
-Date: 23 May 2008 05:23:42 +0200
-From: Mail System Internal Data <MAILER-DAEMON@example.com>
-Subject: DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
-Message-ID: <foo-0001@example.com>
-
-This text is part of the internal format of your mail folder, and is not
-a real message. It is created automatically by the mail system software.
-If deleted, important folder data will be lost, and it will be re-created
-with the data reset to initial values.
-
-EOF
-
-echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected
+test_expect_success 'setup: messages' '
+ cat >msg <<-\EOF &&
+ second
+
+ Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, sed diam nonumy
+ eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam
+ voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita
+ kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem
+ ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod
+ tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At
+ vero eos et accusam et justo duo dolores et ea rebum.
+
+ EOF
+ q_to_tab <<-\EOF >>msg &&
+ QDuis autem vel eum iriure dolor in hendrerit in vulputate velit
+ Qesse molestie consequat, vel illum dolore eu feugiat nulla facilisis
+ Qat vero eros et accumsan et iusto odio dignissim qui blandit
+ Qpraesent luptatum zzril delenit augue duis dolore te feugait nulla
+ Qfacilisi.
+ EOF
+ cat >>msg <<-\EOF &&
+
+ Lorem ipsum dolor sit amet,
+ consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut
+ laoreet dolore magna aliquam erat volutpat.
+
+ git
+ ---
+ +++
+
+ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure
+ dolor in hendrerit in vulputate velit esse molestie consequat, vel illum
+ dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio
+ dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te
+ feugait nulla facilisi.
+ EOF
+
+ cat >failmail <<-\EOF &&
+ From foo@example.com Fri May 23 10:43:49 2008
+ From: foo@example.com
+ To: bar@example.com
+ Subject: Re: [RFC/PATCH] git-foo.sh
+ Date: Fri, 23 May 2008 05:23:42 +0200
+
+ Sometimes we have to find out that there'\''s nothing left.
+
+ EOF
+
+ cat >pine <<-\EOF &&
+ From MAILER-DAEMON Fri May 23 10:43:49 2008
+ Date: 23 May 2008 05:23:42 +0200
+ From: Mail System Internal Data <MAILER-DAEMON@example.com>
+ Subject: DON'\''T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA
+ Message-ID: <foo-0001@example.com>
+
+ This text is part of the internal format of your mail folder, and is not
+ a real message. It is created automatically by the mail system software.
+ If deleted, important folder data will be lost, and it will be re-created
+ with the data reset to initial values.
+
+ EOF
+
+ signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
+'
test_expect_success setup '
echo hello >file &&
test_tick &&
git commit -m first &&
git tag first &&
+
echo world >>file &&
git add file &&
test_tick &&
git commit -s -F msg &&
git tag second &&
+
git format-patch --stdout first >patch1 &&
{
echo "X-Fake-Field: Line One" &&
echo "X-Fake-Field: Line Three" &&
git format-patch --stdout first | sed -e "1d"
} | append_cr >patch1-crlf.eml &&
+
sed -n -e "3,\$p" msg >file &&
git add file &&
test_tick &&
git commit -m third &&
+
git format-patch --stdout first >patch2 &&
+
git checkout -b lorem &&
sed -n -e "11,\$p" msg >file &&
head -n 9 msg >>file &&
test_tick &&
git commit -a -m "moved stuff" &&
+
echo goodbye >another &&
git add another &&
test_tick &&
git commit -m "added another file" &&
- git format-patch --stdout master >lorem-move.patch
-'
-# reset time
-unset test_tick
-test_tick
+ git format-patch --stdout master >lorem-move.patch &&
+
+ git checkout -b rename &&
+ git mv file renamed &&
+ git commit -m "renamed a file" &&
+
+ git format-patch -M --stdout lorem >rename.patch &&
+
+ git reset --soft lorem^ &&
+ git commit -m "renamed a file and added another" &&
+
+ git format-patch -M --stdout lorem^ >rename-add.patch &&
+
+ # reset time
+ unset test_tick &&
+ test_tick
+'
test_expect_success 'am applies patch correctly' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am <patch1 &&
! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
test_expect_success 'am applies patch e-mail not in a mbox' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch1.eml &&
! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
test_expect_success 'am applies patch e-mail not in a mbox with CRLF' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch1-crlf.eml &&
! test -d .git/rebase-apply &&
- test -z "$(git diff second)" &&
+ git diff --exit-code second &&
test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
-GIT_AUTHOR_NAME="Another Thor"
-GIT_AUTHOR_EMAIL="a.thor@example.com"
-GIT_COMMITTER_NAME="Co M Miter"
-GIT_COMMITTER_EMAIL="c.miter@example.com"
-export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+test_expect_success 'setup: new author and committer' '
+ GIT_AUTHOR_NAME="Another Thor" &&
+ GIT_AUTHOR_EMAIL="a.thor@example.com" &&
+ GIT_COMMITTER_NAME="Co M Miter" &&
+ GIT_COMMITTER_EMAIL="c.miter@example.com" &&
+ export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_COMMITTER_NAME GIT_COMMITTER_EMAIL
+'
compare () {
- test "$(git cat-file commit "$2" | grep "^$1 ")" = \
- "$(git cat-file commit "$3" | grep "^$1 ")"
+ a=$(git cat-file commit "$2" | grep "^$1 ") &&
+ b=$(git cat-file commit "$3" | grep "^$1 ") &&
+ test "$a" = "$b"
}
test_expect_success 'am changes committer and keeps author' '
test_tick &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
git am patch2 &&
! test -d .git/rebase-apply &&
test "$(git rev-parse master^^)" = "$(git rev-parse HEAD^^)" &&
- test -z "$(git diff master..HEAD)" &&
- test -z "$(git diff master^..HEAD^)" &&
+ git diff --exit-code master..HEAD &&
+ git diff --exit-code master^..HEAD^ &&
compare author master HEAD &&
compare author master^ HEAD^ &&
test "$GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" = \
'
test_expect_success 'am --signoff adds Signed-off-by: line' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout -b master2 first &&
git am --signoff <patch2 &&
+ printf "%s\n" "$signoff" >expected &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >>expected &&
git cat-file commit HEAD^ | grep "Signed-off-by:" >actual &&
- test_cmp actual expected &&
+ test_cmp expected actual &&
echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>" >expected &&
git cat-file commit HEAD | grep "Signed-off-by:" >actual &&
- test_cmp actual expected
+ test_cmp expected actual
'
test_expect_success 'am stays in branch' '
- test "refs/heads/master2" = "$(git symbolic-ref HEAD)"
+ echo refs/heads/master2 >expected &&
+ git symbolic-ref HEAD >actual &&
+ test_cmp expected actual
'
test_expect_success 'am --signoff does not add Signed-off-by: line if already there' '
git format-patch --stdout HEAD^ >patch3 &&
sed -e "/^Subject/ s,\[PATCH,Re: Re: Re: & 1/5 v2," patch3 >patch4
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout HEAD^ &&
git am --signoff patch4 &&
- test "$(git cat-file commit HEAD | grep -c "^Signed-off-by:")" -eq 1
+ git cat-file commit HEAD >actual &&
+ test $(grep -c "^Signed-off-by:" actual) -eq 1
'
test_expect_success 'am without --keep removes Re: and [PATCH] stuff' '
- test "$(git rev-parse HEAD)" = "$(git rev-parse master2)"
+ git rev-parse HEAD >expected &&
+ git rev-parse master2 >actual &&
+ test_cmp expected actual
'
test_expect_success 'am --keep really keeps the subject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout HEAD^ &&
git am --keep patch4 &&
! test -d .git/rebase-apply &&
- git cat-file commit HEAD |
- fgrep "Re: Re: Re: [PATCH 1/5 v2] third"
+ git cat-file commit HEAD >actual &&
+ grep "Re: Re: Re: \[PATCH 1/5 v2\] third" actual
'
test_expect_success 'am -3 falls back to 3-way merge' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout -b lorem2 master2 &&
sed -n -e "3,\$p" msg >file &&
head -n 9 msg >>file &&
git commit -m "copied stuff" &&
git am -3 lorem-move.patch &&
! test -d .git/rebase-apply &&
- test -z "$(git diff lorem)"
+ git diff --exit-code lorem
+'
+
+test_expect_success 'am can rename a file' '
+ grep "^rename from" rename.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am rename.patch &&
+ ! test -d .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file' '
+ grep "^rename from" rename.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am -3 rename.patch &&
+ ! test -d .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
+'
+
+test_expect_success 'am -3 can rename a file after falling back to 3-way merge' '
+ grep "^rename from" rename-add.patch &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
+ git checkout lorem^0 &&
+ git am -3 rename-add.patch &&
+ ! test -d .git/rebase-apply &&
+ git update-index --refresh &&
+ git diff --exit-code rename
'
test_expect_success 'am -3 -q is quiet' '
+ rm -fr .git/rebase-apply &&
+ git checkout -f lorem2 &&
git reset master2 --hard &&
sed -n -e "3,\$p" msg >file &&
head -n 9 msg >>file &&
git add file &&
test_tick &&
git commit -m "copied stuff" &&
- git am -3 -q lorem-move.patch > output.out 2>&1 &&
+ git am -3 -q lorem-move.patch >output.out 2>&1 &&
! test -s output.out
'
test_expect_success 'am pauses on conflict' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout lorem2^^ &&
test_must_fail git am lorem-move.patch &&
test -d .git/rebase-apply
'
test_expect_success 'am --skip works' '
+ echo goodbye >expected &&
git am --skip &&
! test -d .git/rebase-apply &&
- test -z "$(git diff lorem2^^ -- file)" &&
- test goodbye = "$(cat another)"
+ git diff --exit-code lorem2^^ -- file &&
+ test_cmp expected another
'
test_expect_success 'am --resolved works' '
+ echo goodbye >expected &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout lorem2^^ &&
test_must_fail git am lorem-move.patch &&
test -d .git/rebase-apply &&
git add file &&
git am --resolved &&
! test -d .git/rebase-apply &&
- test goodbye = "$(cat another)"
+ test_cmp expected another
'
test_expect_success 'am takes patches from a Pine mailbox' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
cat pine patch1 | git am &&
! test -d .git/rebase-apply &&
- test -z "$(git diff master^..HEAD)"
+ git diff --exit-code master^..HEAD
'
test_expect_success 'am fails on mail without patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
test_must_fail git am <failmail &&
- rm -r .git/rebase-apply/
+ git am --abort &&
+ ! test -d .git/rebase-apply
'
test_expect_success 'am fails on empty patch' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
echo "---" >>failmail &&
test_must_fail git am <failmail &&
git am --skip &&
test_expect_success 'am works from stdin in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
(
mkdir -p subdir &&
cd subdir &&
git am <../patch1
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am works from file (relative path given) in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
(
mkdir -p subdir &&
cd subdir &&
git am ../patch1
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am works from file (absolute path given) in subdirectory' '
rm -fr subdir &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
P=$(pwd) &&
(
cd subdir &&
git am "$P/patch1"
) &&
- test -z "$(git diff second)"
+ git diff --exit-code second
'
test_expect_success 'am --committer-date-is-author-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am --committer-date-is-author-date patch1 &&
git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
- at=$(sed -ne "/^author /s/.*> //p" head1) &&
- ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
- test "$at" = "$ct"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ sed -ne "/^committer /s/.*> //p" head1 >ct &&
+ test_cmp at ct
'
test_expect_success 'am without --committer-date-is-author-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am patch1 &&
git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
- at=$(sed -ne "/^author /s/.*> //p" head1) &&
- ct=$(sed -ne "/^committer /s/.*> //p" head1) &&
- test "$at" != "$ct"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ sed -ne "/^committer /s/.*> //p" head1 >ct &&
+ ! test_cmp at ct
'
# This checks for +0000 because TZ is set to UTC and that should
# by test_tick that uses -0700 timezone; if this feature does not
# work, we will see that instead of +0000.
test_expect_success 'am --ignore-date' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
git am --ignore-date patch1 &&
git cat-file commit HEAD | sed -e "/^\$/q" >head1 &&
- at=$(sed -ne "/^author /s/.*> //p" head1) &&
- echo "$at" | grep "+0000"
+ sed -ne "/^author /s/.*> //p" head1 >at &&
+ grep "+0000" at
'
test_expect_success 'am into an unborn branch' '
+ git rev-parse first^{tree} >expected &&
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
rm -fr subdir &&
- mkdir -p subdir &&
+ mkdir subdir &&
git format-patch --numbered-files -o subdir -1 first &&
(
cd subdir &&
git init &&
git am 1
) &&
- result=$(
- cd subdir && git rev-parse HEAD^{tree}
+ (
+ cd subdir &&
+ git rev-parse HEAD^{tree} >../actual
) &&
- test "z$result" = "z$(git rev-parse first^{tree})"
+ test_cmp expected actual
'
test_expect_success 'am newline in subject' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
- sed -e "s/second/second \\\n foo/" patch1 > patchnl &&
- git am < patchnl > output.out 2>&1 &&
+ sed -e "s/second/second \\\n foo/" patch1 >patchnl &&
+ git am <patchnl >output.out 2>&1 &&
grep "^Applying: second \\\n foo$" output.out
'
test_expect_success 'am -q is quiet' '
+ rm -fr .git/rebase-apply &&
+ git reset --hard &&
git checkout first &&
test_tick &&
- git am -q < patch1 > output.out 2>&1 &&
+ git am -q <patch1 >output.out 2>&1 &&
! test -s output.out
'
'
-test_done
+test_expect_success 'show added path under "--follow -M"' '
+ # This tests for a regression introduced in v1.7.2-rc0~103^2~2
+ test_create_repo regression &&
+ (
+ cd regression &&
+ test_commit needs-another-commit &&
+ test_commit foo.bar &&
+ git log -M --follow -p foo.bar.t &&
+ git log -M --follow --stat foo.bar.t &&
+ git log -M --follow --name-only foo.bar.t
+ )
+'
+test_done
. ./test-lib.sh
-SUBSTFORMAT=%H%n
+SUBSTFORMAT='%H (%h)%n'
test_expect_exists() {
test_expect_success " $1 exists" "test -e $1"
grep "bad tree object" output.err
'
+test_expect_success 'upload-pack error message when bad ref requested' '
+
+ printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
+ "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+ test_must_fail git upload-pack . <input >output 2>output.err &&
+ grep -q "not our ref" output.err &&
+ ! grep -q multi_ack_detailed output.err
+'
+
test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
printf "0032want %s\n00000009done\n0000" \
git reset --hard master && git pull -s recursive -X ours . side &&
git reset --hard master && git pull -s recursive -Xtheirs . side &&
git reset --hard master && git pull -s recursive -X theirs . side &&
- git reset --hard master && ! git pull -s recursive -X bork . side
+ git reset --hard master && test_must_fail git pull -s recursive -X bork . side
'
test_done
! test -d bar
'
+test_expect_success 'git clean -e' '
+ rm -fr repo &&
+ mkdir repo &&
+ (
+ cd repo &&
+ git init &&
+ touch 1 2 3 known &&
+ git add known &&
+ git clean -f -e 1 -e 2 &&
+ test -e 1 &&
+ test -e 2 &&
+ ! (test -e 3) &&
+ test -e known
+ )
+'
+
test_done
(exit $status)
'
+(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar' && cd .. && git add sm)
+new_head=$(cd sm && git rev-parse --short=7 --verify HEAD)
+touch .gitmodules
+
cat > expect << EOF
# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: sm
+#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: dir1/modified
#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+# > Add bar
+#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
-no changes added to commit (use "git add" and/or "git commit -a")
EOF
test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
test_cmp expect output
'
+test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
+ git config diff.ignoreSubmodules dirty &&
+ git status >output &&
+ test_cmp expect output &&
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config --remove-section -f .gitmodules submodule.subname
+'
+
test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
git status --ignore-submodules=dirty > output &&
test_cmp expect output
'
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
+ git config diff.ignoreSubmodules dirty &&
+ git status >output &&
+ ! test -s actual &&
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname &&
+ git config --unset diff.ignoreSubmodules
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
echo modified > sm/foo &&
git status --ignore-submodules=dirty > output &&
test_cmp expect output
'
+test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
cat > expect << EOF
# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: sm
+#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# modified: dir1/modified
# modified: sm (modified content)
#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+# > Add bar
+#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
-no changes added to commit (use "git add" and/or "git commit -a")
EOF
test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
test_cmp expect output
'
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD)
cat > expect << EOF
# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: sm
+#
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
# modified: dir1/modified
# modified: sm (new commits)
#
+# Submodule changes to be committed:
+#
+# * sm $head...$new_head (1):
+# > Add bar
+#
# Submodules changed but not updated:
#
-# * sm $head...$head2 (1):
+# * sm $new_head...$head2 (1):
# > 2nd commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
# expect
# output
# untracked
-no changes added to commit (use "git add" and/or "git commit -a")
EOF
test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
test_cmp expect output
'
+test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore untracked &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore untracked &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
git status --ignore-submodules=dirty > output &&
test_cmp expect output
'
+test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore dirty &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore dirty &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
cat > expect << EOF
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
+# .gitmodules
# dir1/untracked
# dir2/modified
# dir2/untracked
test_cmp expect output
'
+test_expect_failure '.gitmodules ignore=all suppresses submodule summary' '
+ git config --add -f .gitmodules submodule.subname.ignore all &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
+test_expect_failure '.git/config ignore=all suppresses submodule summary' '
+ git config --add -f .gitmodules submodule.subname.ignore none &&
+ git config --add -f .gitmodules submodule.subname.path sm &&
+ git config --add submodule.subname.ignore all &&
+ git config --add submodule.subname.path sm &&
+ git status > output &&
+ test_cmp expect output &&
+ git config --remove-section submodule.subname &&
+ git config -f .gitmodules --remove-section submodule.subname
+'
+
test_done
test_when_finished "git checkout Initial" &&
echo "Empty author test" >>foo &&
test_tick &&
- ! git commit -a -m "empty author" --amend 2>err &&
+ test_must_fail git commit -a -m "empty author" --amend 2>err &&
grep "empty ident" err
'
test_when_finished "git checkout Initial" &&
echo "Missing author test" >>foo &&
test_tick &&
- ! git commit -a -m "malformed author" --amend 2>err &&
+ test_must_fail git commit -a -m "malformed author" --amend 2>err &&
grep "empty ident" err
'
test_expect_success 'will not overwrite untracked file' '
git reset --hard c1 &&
cat important > c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
test_cmp important c2.c
'
git reset --hard c1 &&
cat important > c2.c &&
git add c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
test_cmp important c2.c
'
cat important > c2.c &&
git add c2.c &&
rm c2.c &&
- ! git merge c2 &&
+ test_must_fail git merge c2 &&
git checkout c2.c &&
test_cmp important c2.c
'
git rm c1.c &&
git commit -m "rm c1.c" &&
cat important > c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
test_cmp important c1.c
'
git commit -m "rm c1.c" &&
cat important > c1.c &&
git add c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
test_cmp important c1.c
'
cat important > c1.c &&
git add c1.c &&
rm c1.c &&
- ! git merge c1a &&
+ test_must_fail git merge c1a &&
git checkout c1.c &&
test_cmp important c1.c
'
# running mergetool
test_expect_success 'setup' '
+ git config rerere.enabled true &&
echo master >file1 &&
mkdir subdir &&
echo master sub >subdir/file3 &&
cd subdir && (
test_must_fail git merge master >/dev/null 2>&1 &&
( yes "" | git mergetool file3 >/dev/null 2>&1 ) &&
- test "$(cat file3)" = "master new sub" )
+ test "$(cat file3)" = "master new sub") &&
+ cd ..
'
-# We can't merge files from parent directories when running mergetool
-# from a subdir. Is this a bug?
-#
-#test_expect_failure 'mergetool in subdir' '
-# cd subdir && (
-# ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
-# ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
-# test "$(cat ../file1)" = "master updated" &&
-# test "$(cat ../file2)" = "master new" &&
-# git commit -m "branch1 resolved with mergetool - subdir" )
-#'
+test_expect_success 'mergetool on file in parent dir' '
+ cd subdir && (
+ ( yes "" | git mergetool ../file1 >/dev/null 2>&1 ) &&
+ ( yes "" | git mergetool ../file2 >/dev/null 2>&1 ) &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ git commit -m "branch1 resolved with mergetool - subdir") &&
+ cd ..
+'
+
+test_expect_success 'mergetool skips autoresolved' '
+ git checkout -b test4 branch1 &&
+ test_must_fail git merge master &&
+ test -n "$(git ls-files -u)" &&
+ output="$(git mergetool --no-prompt)" &&
+ test "$output" = "No files need merging" &&
+ git reset --hard
+'
+
+test_expect_success 'mergetool merges all from subdir' '
+ cd subdir && (
+ git config rerere.enabled false &&
+ test_must_fail git merge master &&
+ git mergetool --no-prompt &&
+ test "$(cat ../file1)" = "master updated" &&
+ test "$(cat ../file2)" = "master new" &&
+ test "$(cat file3)" = "master new sub" &&
+ git add ../file1 ../file2 file3 &&
+ git commit -m "branch2 resolved by mergetool from subdir") &&
+ cd ..
+'
test_done
test_expect_success "grep -w $L (w)" '
: >expected &&
- ! git grep -n -w -e "^w" >actual &&
+ test_must_fail git grep -n -w -e "^w" >actual &&
test_cmp expected actual
'
test -z \"\`git diff refs/heads/my-bar refs/remotes/bar\`\"
"
+test_expect_success 'dcommit should not fail with a touched file' '
+ test_commit "commit-new-file-foo2" foo2 &&
+ test-chmtime =-60 foo &&
+ git svn dcommit
+'
+
+test_expect_success 'rebase should not fail with a touched file' '
+ test-chmtime =-60 foo &&
+ git svn rebase
+'
+
test_expect_success 'able to set-tree to a subdirectory' "
echo cba > d &&
git update-index d &&
'
test_expect_success 'start import with incomplete authors file' '
- ! git svn clone --authors-file=svn-authors "$svnrepo" x
+ test_must_fail git svn clone --authors-file=svn-authors "$svnrepo" x
'
test_expect_success 'imported 2 revisions successfully' '
'
test_expect_success 'fetch fails on ee' '
- ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+ ( cd aa-work && test_must_fail git svn fetch --authors-file=../svn-authors )
'
tmp_config_get () {
(
cd $H &&
git config --unset i18n.commitencoding &&
- ! git svn dcommit
+ test_must_fail git svn dcommit
)
'
done
test_expect_success 'fetch fails on modified hidden file' '
( cd g &&
git svn find-rev refs/remotes/git-svn > ../expect &&
- ! git svn fetch 2> ../errors &&
+ test_must_fail git svn fetch 2> ../errors &&
git svn find-rev refs/remotes/git-svn > ../expect2 ) &&
fgrep "not found in commit" errors &&
test_cmp expect expect2
--- /dev/null
+#!/bin/sh
+
+test_description='git svn fetch deleted tag'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+ mkdir -p import/trunk/subdir &&
+ mkdir -p import/branches &&
+ mkdir -p import/tags &&
+ echo "base" >import/trunk/subdir/file &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" &&
+ rm -rf import &&
+
+ svn_cmd mkdir -m "create mybranch directory" "$svnrepo/branches/mybranch" &&
+ svn_cmd cp -m "create branch mybranch" "$svnrepo/trunk" "$svnrepo/branches/mybranch/trunk" &&
+
+ svn_cmd co "$svnrepo/trunk" svn_project &&
+ (cd svn_project &&
+ echo "trunk change" >>subdir/file &&
+ svn_cmd ci -m "trunk change" subdir/file &&
+
+ svn_cmd switch "$svnrepo/branches/mybranch/trunk" &&
+ echo "branch change" >>subdir/file &&
+ svn_cmd ci -m "branch change" subdir/file
+ ) &&
+
+ svn_cmd cp -m "create mytag attempt 1" -r5 "$svnrepo/trunk/subdir" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag attempt 1" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag attempt 2" -r5 "$svnrepo/branches/mybranch/trunk/subdir" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with checksum error' '
+ git svn init --stdlayout "$svnrepo" git_project &&
+ cd git_project &&
+ git svn fetch &&
+
+ git diff --exit-code mybranch:trunk/subdir/file tags/mytag:file &&
+ git diff --exit-code master:subdir/file tags/mytag^:file
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+
+test_description='git svn fetch deleted tag 2'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup svn repo' '
+ mkdir -p import/branches &&
+ mkdir -p import/tags &&
+ mkdir -p import/trunk/subdir1 &&
+ mkdir -p import/trunk/subdir2 &&
+ mkdir -p import/trunk/subdir3 &&
+ echo "file1" >import/trunk/subdir1/file &&
+ echo "file2" >import/trunk/subdir2/file &&
+ echo "file3" >import/trunk/subdir3/file &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" &&
+ rm -rf import &&
+
+ svn_cmd co "$svnrepo/trunk" svn_project &&
+ (cd svn_project &&
+ echo "change1" >>subdir1/file &&
+ echo "change2" >>subdir2/file &&
+ echo "change3" >>subdir3/file &&
+ svn_cmd ci -m "change" .
+ ) &&
+
+ svn_cmd cp -m "create mytag 1" -r2 "$svnrepo/trunk/subdir1" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag 1" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag 2" -r2 "$svnrepo/trunk/subdir2" "$svnrepo/tags/mytag" &&
+ svn_cmd rm -m "delete mytag 2" "$svnrepo/tags/mytag" &&
+ svn_cmd cp -m "create mytag 3" -r2 "$svnrepo/trunk/subdir3" "$svnrepo/tags/mytag"
+'
+
+test_expect_success 'fetch deleted tags from same revision with no checksum error' '
+ git svn init --stdlayout "$svnrepo" git_project &&
+ cd git_project &&
+ git svn fetch &&
+
+ git diff --exit-code master:subdir3/file tags/mytag:file &&
+ git diff --exit-code master:subdir2/file tags/mytag^:file &&
+ git diff --exit-code master:subdir1/file tags/mytag^^:file
+'
+
+test_done
'git fast-import <input &&
test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
+test_expect_success \
+ 'N: copy directory by id' \
+ 'cat >expect <<-\EOF &&
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N4
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+ compare_diff_raw expect actual'
+
+test_expect_success \
+ 'N: modify copied tree' \
+ 'cat >expect <<-\EOF &&
+ :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100 newdir/interesting file3/file5
+ :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100 file2/newf file3/newf
+ :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100 file2/oldf file3/oldf
+ EOF
+ subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+ cat >input <<-INPUT_END &&
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ copy by tree hash
+ COMMIT
+
+ from refs/heads/branch^0
+ M 040000 $subdir file3
+
+ commit refs/heads/N5
+ committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+ data <<COMMIT
+ modify directory copy
+ COMMIT
+
+ M 644 inline file3/file5
+ data <<EOF
+ $file5_data
+ EOF
+ INPUT_END
+ git fast-import <input &&
+ git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+ compare_diff_raw expect actual'
+
###
### series O
###
tr Q '\015'
}
+q_to_tab () {
+ tr Q '\011'
+}
+
append_cr () {
sed -e 's/$/Q/' | tr Q '\015'
}
struct child_process *helper, exporter;
struct helper_data *data = transport->data;
char *export_marks = NULL, *import_marks = NULL;
- struct string_list revlist_args = { NULL, 0, 0 };
+ struct string_list revlist_args = STRING_LIST_INIT_NODUP;
struct strbuf buf = STRBUF_INIT;
helper = get_helper(transport);
diff_tree_release_paths(&diff_opts);
/* Go through the new set of filepairing, and see if we find a more interesting one */
+ opt->found_follow = 0;
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
diff_tree_release_paths(opt);
opt->paths[0] = xstrdup(p->one->path);
diff_tree_setup_paths(opt->paths, opt);
+
+ /*
+ * The caller expects us to return a set of vanilla
+ * filepairs to let a later call to diffcore_std()
+ * it makes to sort the renames out (among other
+ * things), but we already have found renames
+ * ourselves; signal diffcore_std() not to muck with
+ * rename information.
+ */
+ opt->found_follow = 1;
break;
}
}
init_tree_desc(&t1, tree1, size1);
init_tree_desc(&t2, tree2, size2);
retval = diff_tree(&t1, &t2, base, opt);
- if (DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
+ if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
init_tree_desc(&t1, tree1, size1);
init_tree_desc(&t2, tree2, size2);
try_to_follow_renames(&t1, &t2, base, opt);
{
int i, ret, bottom;
struct tree_desc t[MAX_UNPACK_TREES];
+ void *buf[MAX_UNPACK_TREES];
struct traverse_info newinfo;
struct name_entry *p;
const unsigned char *sha1 = NULL;
if (dirmask & 1)
sha1 = names[i].sha1;
- fill_tree_descriptor(t+i, sha1);
+ buf[i] = fill_tree_descriptor(t+i, sha1);
}
bottom = switch_cache_bottom(&newinfo);
ret = traverse_trees(n, t, &newinfo);
restore_cache_bottom(&newinfo, bottom);
+
+ for (i = 0; i < n; i++)
+ free(buf[i]);
+
return ret;
}
fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
}
-static int do_rev_list(int in, int out, void *create_full_pack)
+static int do_rev_list(int in, int out, void *user_data)
{
int i;
struct rev_info revs;
if (use_thin_pack)
revs.edge_hint = 1;
- if (create_full_pack) {
- const char *args[] = {"rev-list", "--all", NULL};
- setup_revisions(2, args, &revs, NULL);
- } else {
- for (i = 0; i < want_obj.nr; i++) {
- struct object *o = want_obj.objects[i].item;
- /* why??? */
- o->flags &= ~UNINTERESTING;
- add_pending_object(&revs, o, NULL);
- }
- for (i = 0; i < have_obj.nr; i++) {
- struct object *o = have_obj.objects[i].item;
- o->flags |= UNINTERESTING;
- add_pending_object(&revs, o, NULL);
- }
- setup_revisions(0, NULL, &revs, NULL);
+ for (i = 0; i < want_obj.nr; i++) {
+ struct object *o = want_obj.objects[i].item;
+ /* why??? */
+ o->flags &= ~UNINTERESTING;
+ add_pending_object(&revs, o, NULL);
+ }
+ for (i = 0; i < have_obj.nr; i++) {
+ struct object *o = have_obj.objects[i].item;
+ o->flags |= UNINTERESTING;
+ add_pending_object(&revs, o, NULL);
}
+ setup_revisions(0, NULL, &revs, NULL);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
mark_edges_uninteresting(revs.commits, &revs, show_edge);
*/
o = lookup_object(sha1_buf);
if (!o || !(o->flags & OUR_REF))
- die("git upload-pack: not our ref %s", line+5);
+ die("git upload-pack: not our ref %s",
+ sha1_to_hex(sha1_buf));
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
add_object_array(o, NULL, &want_obj);
DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES);
if (!s->show_untracked_files)
DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES);
- if (s->ignore_submodule_arg)
+ if (s->ignore_submodule_arg) {
+ DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+ }
rev.diffopt.format_callback = wt_status_collect_changed_cb;
rev.diffopt.format_callback_data = s;
rev.prune_data = s->pathspec;
opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference;
setup_revisions(0, NULL, &rev, &opt);
- if (s->ignore_submodule_arg)
+ if (s->ignore_submodule_arg) {
+ DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG);
handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg);
+ }
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
rev.diffopt.format_callback = wt_status_collect_updated_cb;