diff options
72 files changed, 1488 insertions, 455 deletions
diff --git a/.gitignore b/.gitignore index 51a37b1af7..338c6d8881 100644 --- a/.gitignore +++ b/.gitignore @@ -1,184 +1,185 @@ -GIT-BUILD-OPTIONS -GIT-CFLAGS -GIT-GUI-VARS -GIT-VERSION-FILE -git -git-add -git-add--interactive -git-am -git-annotate -git-apply -git-archimport -git-archive -git-bisect -git-bisect--helper -git-blame -git-branch -git-bundle -git-cat-file -git-check-attr -git-check-ref-format -git-checkout -git-checkout-index -git-cherry -git-cherry-pick -git-clean -git-clone -git-commit -git-commit-tree -git-config -git-count-objects -git-cvsexportcommit -git-cvsimport -git-cvsserver -git-daemon -git-diff -git-diff-files -git-diff-index -git-diff-tree -git-difftool -git-difftool--helper -git-describe -git-fast-export -git-fast-import -git-fetch -git-fetch--tool -git-fetch-pack -git-filter-branch -git-fmt-merge-msg -git-for-each-ref -git-format-patch -git-fsck -git-fsck-objects -git-gc -git-get-tar-commit-id -git-grep -git-hash-object -git-help -git-http-fetch -git-http-push -git-imap-send -git-index-pack -git-init -git-init-db -git-instaweb -git-log -git-lost-found -git-ls-files -git-ls-remote -git-ls-tree -git-mailinfo -git-mailsplit -git-merge -git-merge-base -git-merge-index -git-merge-file -git-merge-tree -git-merge-octopus -git-merge-one-file -git-merge-ours -git-merge-recursive -git-merge-resolve -git-merge-subtree -git-mergetool -git-mergetool--lib -git-mktag -git-mktree -git-name-rev -git-mv -git-pack-redundant -git-pack-objects -git-pack-refs -git-parse-remote -git-patch-id -git-peek-remote -git-prune -git-prune-packed -git-pull -git-push -git-quiltimport -git-read-tree -git-rebase -git-rebase--interactive -git-receive-pack -git-reflog -git-relink -git-remote -git-remote-curl -git-repack -git-replace -git-repo-config -git-request-pull -git-rerere -git-reset -git-rev-list -git-rev-parse -git-revert -git-rm -git-send-email -git-send-pack -git-sh-setup -git-shell -git-shortlog -git-show -git-show-branch -git-show-index -git-show-ref -git-stage -git-stash -git-status -git-stripspace -git-submodule -git-svn -git-symbolic-ref -git-tag -git-tar-tree -git-unpack-file -git-unpack-objects -git-update-index -git-update-ref -git-update-server-info -git-upload-archive -git-upload-pack -git-var -git-verify-pack -git-verify-tag -git-web--browse -git-whatchanged -git-write-tree -git-core-*/?* -gitk-wish -gitweb/gitweb.cgi -test-chmtime -test-ctype -test-date -test-delta -test-dump-cache-tree -test-genrandom -test-match-trees -test-parse-options -test-path-utils -test-sha1 -test-sigchain -common-cmds.h +/GIT-BUILD-OPTIONS +/GIT-CFLAGS +/GIT-GUI-VARS +/GIT-VERSION-FILE +/git +/git-add +/git-add--interactive +/git-am +/git-annotate +/git-apply +/git-archimport +/git-archive +/git-bisect +/git-bisect--helper +/git-blame +/git-branch +/git-bundle +/git-cat-file +/git-check-attr +/git-check-ref-format +/git-checkout +/git-checkout-index +/git-cherry +/git-cherry-pick +/git-clean +/git-clone +/git-commit +/git-commit-tree +/git-config +/git-count-objects +/git-cvsexportcommit +/git-cvsimport +/git-cvsserver +/git-daemon +/git-diff +/git-diff-files +/git-diff-index +/git-diff-tree +/git-difftool +/git-difftool--helper +/git-describe +/git-fast-export +/git-fast-import +/git-fetch +/git-fetch--tool +/git-fetch-pack +/git-filter-branch +/git-fmt-merge-msg +/git-for-each-ref +/git-format-patch +/git-fsck +/git-fsck-objects +/git-gc +/git-get-tar-commit-id +/git-grep +/git-hash-object +/git-help +/git-http-fetch +/git-http-push +/git-imap-send +/git-index-pack +/git-init +/git-init-db +/git-instaweb +/git-log +/git-lost-found +/git-ls-files +/git-ls-remote +/git-ls-tree +/git-mailinfo +/git-mailsplit +/git-merge +/git-merge-base +/git-merge-index +/git-merge-file +/git-merge-tree +/git-merge-octopus +/git-merge-one-file +/git-merge-ours +/git-merge-recursive +/git-merge-resolve +/git-merge-subtree +/git-mergetool +/git-mergetool--lib +/git-mktag +/git-mktree +/git-name-rev +/git-mv +/git-pack-redundant +/git-pack-objects +/git-pack-refs +/git-parse-remote +/git-patch-id +/git-peek-remote +/git-prune +/git-prune-packed +/git-pull +/git-push +/git-quiltimport +/git-read-tree +/git-rebase +/git-rebase--interactive +/git-receive-pack +/git-reflog +/git-relink +/git-remote +/git-remote-curl +/git-repack +/git-replace +/git-repo-config +/git-request-pull +/git-rerere +/git-reset +/git-rev-list +/git-rev-parse +/git-revert +/git-rm +/git-send-email +/git-send-pack +/git-sh-setup +/git-shell +/git-shortlog +/git-show +/git-show-branch +/git-show-index +/git-show-ref +/git-stage +/git-stash +/git-status +/git-stripspace +/git-submodule +/git-svn +/git-symbolic-ref +/git-tag +/git-tar-tree +/git-unpack-file +/git-unpack-objects +/git-update-index +/git-update-ref +/git-update-server-info +/git-upload-archive +/git-upload-pack +/git-var +/git-verify-pack +/git-verify-tag +/git-web--browse +/git-whatchanged +/git-write-tree +/git-core-*/?* +/gitk-git/gitk-wish +/gitweb/gitweb.cgi +/test-chmtime +/test-ctype +/test-date +/test-delta +/test-dump-cache-tree +/test-genrandom +/test-match-trees +/test-parse-options +/test-path-utils +/test-sha1 +/test-sigchain +/common-cmds.h *.tar.gz *.dsc *.deb -git.spec +/git.spec *.exe *.[aos] *.py[co] -config.mak -autom4te.cache -config.cache -config.log -config.status -config.mak.autogen -config.mak.append -configure -tags -TAGS -cscope* +*+ +/config.mak +/autom4te.cache +/config.cache +/config.log +/config.status +/config.mak.autogen +/config.mak.append +/configure +/tags +/TAGS +/cscope* *.obj *.lib *.sln @@ -188,5 +189,5 @@ cscope* *.user *.idb *.pdb -Debug/ -Release/ +/Debug/ +/Release/ diff --git a/Documentation/Makefile b/Documentation/Makefile index 06b0c57b95..cd5b4396db 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -103,6 +103,14 @@ ifdef DOCBOOK_SUPPRESS_SP XMLTO_EXTRA += -m manpage-suppress-sp.xsl endif +# If your target system uses GNU groff, it may try to render +# apostrophes as a "pretty" apostrophe using unicode. This breaks +# cut&paste, so you should set GNU_ROFF to force them to be ASCII +# apostrophes. Unfortunately does not work with non-GNU roff. +ifdef GNU_ROFF +XMLTO_EXTRA += -m manpage-quote-apos.xsl +endif + SHELL_PATH ?= $(SHELL) # Shell quote; SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH)) diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 9276faeb11..e26b84706f 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -87,6 +87,13 @@ endif::git-format-patch[] Show only names and status of changed files. See the description of the `--diff-filter` option on what the status letters mean. +--submodule[=<format>]:: + Chose the output format for submodule differences. <format> can be one of + 'short' and 'log'. 'short' just shows pairs of commit names, this format + is used when this option is not given. 'log' is the default value for this + option and lists the commits in that commit range like the 'summary' + option of linkgit:git-submodule[1] does. + --color:: Show colored diff. diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index 5eb2b0ee07..28868747da 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -1,25 +1,13 @@ -ifndef::git-pull[] --q:: ---quiet:: - Pass --quiet to git-fetch-pack and silence any other internally - used git commands. - --v:: ---verbose:: - Be verbose. -endif::git-pull[] - -a:: --append:: Append ref names and object names of fetched refs to the existing contents of `.git/FETCH_HEAD`. Without this option old data in `.git/FETCH_HEAD` will be overwritten. ---upload-pack <upload-pack>:: - When given, and the repository to fetch from is handled - by 'git-fetch-pack', '--exec=<upload-pack>' is passed to - the command to specify non-default path for the command - run on the other end. +--depth=<depth>:: + Deepen the history of a 'shallow' repository created by + `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1]) + by the specified number of commits. -f:: --force:: @@ -29,6 +17,10 @@ endif::git-pull[] fetches is a descendant of `<lbranch>`. This option overrides that check. +-k:: +--keep:: + Keep downloaded pack. + ifdef::git-pull[] --no-tags:: endif::git-pull[] @@ -49,10 +41,6 @@ endif::git-pull[] flag lets all tags and their associated objects be downloaded. --k:: ---keep:: - Keep downloaded pack. - -u:: --update-head-ok:: By default 'git-fetch' refuses to update the head which @@ -62,7 +50,19 @@ endif::git-pull[] implementing your own Porcelain you are not supposed to use it. ---depth=<depth>:: - Deepen the history of a 'shallow' repository created by - `git clone` with `--depth=<depth>` option (see linkgit:git-clone[1]) - by the specified number of commits. +--upload-pack <upload-pack>:: + When given, and the repository to fetch from is handled + by 'git-fetch-pack', '--exec=<upload-pack>' is passed to + the command to specify non-default path for the command + run on the other end. + +ifndef::git-pull[] +-q:: +--quiet:: + Pass --quiet to git-fetch-pack and silence any other internally + used git commands. + +-v:: +--verbose:: + Be verbose. +endif::git-pull[] diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 45ebf87ca3..e93e606f45 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -76,10 +76,10 @@ OPTIONS work tree and add them to the index. This gives the user a chance to review the difference before adding modified contents to the index. - - This effectively runs ``add --interactive``, but bypasses the - initial command menu and directly jumps to `patch` subcommand. - See ``Interactive mode'' for details. ++ +This effectively runs `add --interactive`, but bypasses the +initial command menu and directly jumps to the `patch` subcommand. +See ``Interactive mode'' for details. -e, \--edit:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 63e7a42cb3..d2ffae0c10 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -20,7 +20,7 @@ on the subcommand: git bisect bad [<rev>] git bisect good [<rev>...] git bisect skip [(<rev>|<range>)...] - git bisect reset [<branch>] + git bisect reset [<commit>] git bisect visualize git bisect replay <logfile> git bisect log @@ -81,16 +81,27 @@ will have been left with the first bad kernel revision in "refs/bisect/bad". Bisect reset ~~~~~~~~~~~~ -To return to the original head after a bisect session, issue the -following command: +After a bisect session, to clean up the bisection state and return to +the original HEAD, issue the following command: ------------------------------------------------ $ git bisect reset ------------------------------------------------ -This resets the tree to the original branch instead of being on the -bisection commit ("git bisect start" will also do that, as it resets -the bisection state). +By default, this will return your tree to the commit that was checked +out before `git bisect start`. (A new `git bisect start` will also do +that, as it cleans up the old bisection state.) + +With an optional argument, you can return to a different commit +instead: + +------------------------------------------------ +$ git bisect reset <commit> +------------------------------------------------ + +For example, `git bisect reset HEAD` will leave you on the current +bisection commit and avoid switching commits at all, while `git bisect +reset bisect/bad` will check out the first bad revision. Bisect visualize ~~~~~~~~~~~~~~~~ diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt index e9b3b40af4..0aeef24780 100644 --- a/Documentation/git-check-ref-format.txt +++ b/Documentation/git-check-ref-format.txt @@ -9,6 +9,7 @@ SYNOPSIS -------- [verse] 'git check-ref-format' <refname> +'git check-ref-format' --print <refname> 'git check-ref-format' --branch <branchname-shorthand> DESCRIPTION @@ -63,19 +64,31 @@ reference name expressions (see linkgit:git-rev-parse[1]): . at-open-brace `@{` is used as a notation to access a reflog entry. +With the `--print` option, if 'refname' is acceptable, it prints the +canonicalized name of a hypothetical reference with that name. That is, +it prints 'refname' with any extra `/` characters removed. + With the `--branch` option, it expands the ``previous branch syntax'' `@{-n}`. For example, `@{-1}` is a way to refer the last branch you were on. This option should be used by porcelains to accept this syntax anywhere a branch name is expected, so they can act as if you typed the branch name. -EXAMPLE -------- - -git check-ref-format --branch @{-1}:: - -Print the name of the previous branch. +EXAMPLES +-------- +* Print the name of the previous branch: ++ +------------ +$ git check-ref-format --branch @{-1} +------------ + +* Determine the reference name to use for a new branch: ++ +------------ +$ ref=$(git check-ref-format --print "refs/heads/$newbranch") || +die "we do not like '$newbranch' as a branch name." +------------ GIT --- diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index b231dbb947..78b9808aa3 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -8,7 +8,9 @@ git-describe - Show the most recent tag that is reachable from a commit SYNOPSIS -------- +[verse] 'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] <committish>... +'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>] DESCRIPTION ----------- @@ -27,6 +29,11 @@ OPTIONS <committish>...:: Committish object names to describe. +--dirty[=<mark>]:: + Describe the working tree. + It means describe HEAD and appends <mark> (`-dirty` by + default) if the working tree is dirty. + --all:: Instead of using only the annotated tags, use any ref found in `.git/refs/`. This option enables matching @@ -44,7 +51,9 @@ OPTIONS --abbrev=<n>:: Instead of using the default 7 hexadecimal digits as the - abbreviated object name, use <n> digits. + abbreviated object name, use <n> digits, or as many digits + as needed to form a unique object name. An <n> of 0 + will suppress long format, only showing the closest tag. --candidates=<n>:: Instead of considering only the 10 most recent tags as @@ -68,8 +77,8 @@ OPTIONS This is useful when you want to see parts of the commit object name in "describe" output, even when the commit in question happens to be a tagged version. Instead of just emitting the tag name, it will - describe such a commit as v1.2-0-deadbeef (0th commit since tag v1.2 - that points at object deadbeef....). + describe such a commit as v1.2-0-gdeadbee (0th commit since tag v1.2 + that points at object deadbee....). --match <pattern>:: Only consider tags matching the given pattern (can be used to avoid @@ -108,7 +117,7 @@ the output shows the reference path as well: [torvalds@g5 git]$ git describe --all --abbrev=4 v1.0.5^2 tags/v1.0.0-21-g975b - [torvalds@g5 git]$ git describe --all HEAD^ + [torvalds@g5 git]$ git describe --all --abbrev=4 HEAD^ heads/lt/describe-7-g975b With --abbrev set to 0, the command can be used to find the @@ -117,6 +126,13 @@ closest tagname without any suffix: [torvalds@g5 git]$ git describe --abbrev=0 v1.0.5^2 tags/v1.0.0 +Note that the suffix you get if you type these commands today may be +longer than what Linus saw above when he ran these commands, as your +git repository may have new commits whose object names begin with +975b that did not exist back then, and "-g975b" suffix alone may not +be sufficient to disambiguate these commits. + + SEARCH STRATEGY --------------- diff --git a/Documentation/git-fetch.txt b/Documentation/git-fetch.txt index d3164c5c88..f2483d624e 100644 --- a/Documentation/git-fetch.txt +++ b/Documentation/git-fetch.txt @@ -37,6 +37,35 @@ include::pull-fetch-param.txt[] include::urls-remotes.txt[] + +EXAMPLES +-------- + +* Update the remote-tracking branches: ++ +------------------------------------------------ +$ git fetch origin +------------------------------------------------ ++ +The above command copies all branches from the remote refs/heads/ +namespace and stores them to the local refs/remotes/origin/ namespace, +unless the branch.<name>.fetch option is used to specify a non-default +refspec. + +* Using refspecs explicitly: ++ +------------------------------------------------ +$ git fetch origin +pu:pu maint:tmp +------------------------------------------------ ++ +This updates (or creates, as necessary) branches `pu` and `tmp` in +the local repository by fetching from the branches (respectively) +`pu` and `maint` from the remote repository. ++ +The `pu` branch will be updated even if it is does not fast-forward, +because it is prefixed with a plus sign; `tmp` will not be. + + SEE ALSO -------- linkgit:git-pull[1] diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt index 021066e95d..625723e41f 100644 --- a/Documentation/git-ls-files.txt +++ b/Documentation/git-ls-files.txt @@ -48,8 +48,10 @@ OPTIONS -i:: --ignored:: - Show ignored files in the output. - Note that this also reverses any exclude list present. + Show only ignored files in the output. When showing files in the + index, print only those matched by an exclude pattern. When + showing "other" files, show only those matched by an exclude + pattern. -s:: --stage:: diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt index d05f324462..e886c2ef54 100644 --- a/Documentation/git-merge.txt +++ b/Documentation/git-merge.txt @@ -212,6 +212,39 @@ You can work through the conflict with a number of tools: common ancestor, 'git show :2:filename' shows the HEAD version and 'git show :3:filename' shows the remote version. + +EXAMPLES +-------- + +* Merge branches `fixes` and `enhancements` on top of + the current branch, making an octopus merge: ++ +------------------------------------------------ +$ git merge fixes enhancements +------------------------------------------------ + +* Merge branch `obsolete` into the current branch, using `ours` + merge strategy: ++ +------------------------------------------------ +$ git merge -s ours obsolete +------------------------------------------------ + +* Merge branch `maint` into the current branch, but do not make + a new commit automatically: ++ +------------------------------------------------ +$ git merge --no-commit maint +------------------------------------------------ ++ +This can be used when you want to include further changes to the +merge, or want to write your own merge commit message. ++ +You should refrain from abusing this option to sneak substantial +changes into a merge commit. Small fixups like bumping +release/version name would be acceptable. + + SEE ALSO -------- linkgit:git-fmt-merge-msg[1], linkgit:git-pull[1], diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt index 7578623edb..b93201158f 100644 --- a/Documentation/git-pull.txt +++ b/Documentation/git-pull.txt @@ -26,6 +26,10 @@ Also note that options meant for 'git-pull' itself and underlying OPTIONS ------- + +Options related to merging +~~~~~~~~~~~~~~~~~~~~~~~~~~ + include::merge-options.txt[] :git-pull: 1 @@ -47,6 +51,9 @@ unless you have read linkgit:git-rebase[1] carefully. --no-rebase:: Override earlier --rebase. +Options related to fetching +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + include::fetch-options.txt[] include::pull-fetch-param.txt[] @@ -131,54 +138,13 @@ $ git pull origin next ------------------------------------------------ + This leaves a copy of `next` temporarily in FETCH_HEAD, but -does not update any remote-tracking branches. - -* Bundle local branch `fixes` and `enhancements` on top of - the current branch, making an Octopus merge: -+ ------------------------------------------------- -$ git pull . fixes enhancements ------------------------------------------------- -+ -This `git pull .` syntax is equivalent to `git merge`. - -* Merge local branch `obsolete` into the current branch, using `ours` - merge strategy: -+ ------------------------------------------------- -$ git pull -s ours . obsolete ------------------------------------------------- - -* Merge local branch `maint` into the current branch, but do not make - a commit automatically: +does not update any remote-tracking branches. Using remote-tracking +branches, the same can be done by invoking fetch and merge: + ------------------------------------------------ -$ git pull --no-commit . maint +$ git fetch origin +$ git merge origin/next ------------------------------------------------ -+ -This can be used when you want to include further changes to the -merge, or want to write your own merge commit message. -+ -You should refrain from abusing this option to sneak substantial -changes into a merge commit. Small fixups like bumping -release/version name would be acceptable. - -* Command line pull of multiple branches from one repository: -+ ------------------------------------------------- -$ git checkout master -$ git fetch origin +pu:pu maint:tmp -$ git pull . tmp ------------------------------------------------- -+ -This updates (or creates, as necessary) branches `pu` and `tmp` in -the local repository by fetching from the branches (respectively) -`pu` and `maint` from the remote repository. -+ -The `pu` branch will be updated even if it is does not fast-forward; -the others will not be. -+ -The final command then merges the newly fetched `tmp` into master. If you tried a pull which resulted in a complex conflicts and diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt index fafe728f89..3f14b727b8 100644 --- a/Documentation/git-stash.txt +++ b/Documentation/git-stash.txt @@ -78,8 +78,7 @@ stash@{1}: On master: 9cc0589... Add git-stash ---------------------------------------------------------------- + The command takes options applicable to the 'git-log' -command to control what is shown and how. If no options are set, the -default is `-n 10`. See linkgit:git-log[1]. +command to control what is shown and how. See linkgit:git-log[1]. show [<stash>]:: diff --git a/Documentation/git-update-index.txt b/Documentation/git-update-index.txt index 25e0bbea86..6052484ab9 100644 --- a/Documentation/git-update-index.txt +++ b/Documentation/git-update-index.txt @@ -99,6 +99,10 @@ in the index e.g. when merging in a commit; thus, in case the assumed-untracked file is changed upstream, you will need to handle the situation manually. +--really-refresh:: + Like '--refresh', but checks stat information unconditionally, + without regard to the "assume unchanged" setting. + -g:: --again:: Runs 'git-update-index' itself on the paths whose index @@ -308,7 +312,7 @@ Configuration ------------- The command honors `core.filemode` configuration variable. If -your repository is on an filesystem whose executable bits are +your repository is on a filesystem whose executable bits are unreliable, this should be set to 'false' (see linkgit:git-config[1]). This causes the command to ignore differences in file modes recorded in the index and the file mode on the filesystem if they differ only on diff --git a/Documentation/manpage-quote-apos.xsl b/Documentation/manpage-quote-apos.xsl new file mode 100644 index 0000000000..aeb8839f33 --- /dev/null +++ b/Documentation/manpage-quote-apos.xsl @@ -0,0 +1,16 @@ +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.0"> + +<!-- work around newer groff/man setups using a prettier apostrophe + that unfortunately does not quote anything when cut&pasting + examples to the shell --> +<xsl:template name="escape.apostrophe"> + <xsl:param name="content"/> + <xsl:call-template name="string.subst"> + <xsl:with-param name="string" select="$content"/> + <xsl:with-param name="target">'</xsl:with-param> + <xsl:with-param name="replacement">\(aq</xsl:with-param> + </xsl:call-template> +</xsl:template> + +</xsl:stylesheet> diff --git a/Documentation/merge-options.txt b/Documentation/merge-options.txt index adadf8e4bf..fec3394305 100644 --- a/Documentation/merge-options.txt +++ b/Documentation/merge-options.txt @@ -1,43 +1,42 @@ --q:: ---quiet:: - Operate quietly. - --v:: ---verbose:: - Be verbose. - ---stat:: - Show a diffstat at the end of the merge. The diffstat is also - controlled by the configuration option merge.stat. - --n:: ---no-stat:: - Do not show a diffstat at the end of the merge. +--commit:: +--no-commit:: + Perform the merge and commit the result. This option can + be used to override --no-commit. ++ +With --no-commit perform the merge but pretend the merge +failed and do not autocommit, to give the user a chance to +inspect and further tweak the merge result before committing. ---summary:: ---no-summary:: - Synonyms to --stat and --no-stat; these are deprecated and will be - removed in the future. +--ff:: +--no-ff:: + Do not generate a merge commit if the merge resolved as + a fast-forward, only update the branch pointer. This is + the default behavior of git-merge. ++ +With --no-ff Generate a merge commit even if the merge +resolved as a fast-forward. --log:: +--no-log:: In addition to branch names, populate the log message with one-line descriptions from the actual commits that are being merged. ++ +With --no-log do not list one-line descriptions from the +actual commits being merged. ---no-log:: - Do not list one-line descriptions from the actual commits being - merged. - ---no-commit:: - Perform the merge but pretend the merge failed and do - not autocommit, to give the user a chance to inspect and - further tweak the merge result before committing. ---commit:: - Perform the merge and commit the result. This option can - be used to override --no-commit. +--stat:: +-n:: +--no-stat:: + Show a diffstat at the end of the merge. The diffstat is also + controlled by the configuration option merge.stat. ++ +With -n or --no-stat do not show a diffstat at the end of the +merge. --squash:: +--no-squash:: Produce the working tree and index state as if a real merge happened (except for the merge information), but do not actually make a commit or @@ -46,19 +45,14 @@ commit. This allows you to create a single commit on top of the current branch whose effect is the same as merging another branch (or more in case of an octopus). ++ +With --no-squash perform the merge and commit the result. This +option can be used to override --squash. ---no-squash:: - Perform the merge and commit the result. This option can - be used to override --squash. - ---no-ff:: - Generate a merge commit even if the merge resolved as a - fast-forward. - ---ff:: - Do not generate a merge commit if the merge resolved as - a fast-forward, only update the branch pointer. This is - the default behavior of git-merge. +--ff-only:: + Refuse to merge and exit with a non-zero status unless the + current `HEAD` is already up-to-date or the merge can be + resolved as a fast-forward. -s <strategy>:: --strategy=<strategy>:: @@ -67,3 +61,16 @@ If there is no `-s` option, a built-in list of strategies is used instead ('git-merge-recursive' when merging a single head, 'git-merge-octopus' otherwise). + +--summary:: +--no-summary:: + Synonyms to --stat and --no-stat; these are deprecated and will be + removed in the future. + +-q:: +--quiet:: + Operate quietly. + +-v:: +--verbose:: + Be verbose. diff --git a/Documentation/pretty-formats.txt b/Documentation/pretty-formats.txt index 2a845b1e57..38b9904791 100644 --- a/Documentation/pretty-formats.txt +++ b/Documentation/pretty-formats.txt @@ -123,6 +123,9 @@ The placeholders are: - '%s': subject - '%f': sanitized subject line, suitable for a filename - '%b': body +- '%gD': reflog selector, e.g., `refs/stash@\{1\}` +- '%gd': shortened reflog selector, e.g., `stash@\{1\}` +- '%gs': reflog subject - '%Cred': switch color to red - '%Cgreen': switch color to green - '%Cblue': switch color to blue @@ -132,6 +135,12 @@ The placeholders are: - '%n': newline - '%x00': print a byte from a hex code +NOTE: Some placeholders may depend on other options given to the +revision traversal engine. For example, the `%g*` reflog options will +insert an empty string unless we are traversing reflog entries (e.g., by +`git log -g`). The `%d` placeholder will use the "short" decoration +format if `--decorate` was not already provided on the command line. + * 'tformat:' + The 'tformat:' format works exactly like 'format:', except that it @@ -159,6 +159,10 @@ all:: # Define ASCIIDOC_NO_ROFF if your DocBook XSL escapes raw roff directives # (versions 1.72 and later and 1.68.1 and earlier). # +# Define GNU_ROFF if your target system uses GNU groff. This forces +# apostrophes to be ASCII so that cut&pasting examples to the shell +# will work. +# # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's # MakeMaker (e.g. using ActiveState under Cygwin). # @@ -412,6 +416,7 @@ LIB_H += builtin.h LIB_H += cache.h LIB_H += cache-tree.h LIB_H += commit.h +LIB_H += compat/bswap.h LIB_H += compat/cygwin.h LIB_H += compat/mingw.h LIB_H += csum-file.h @@ -452,6 +457,7 @@ LIB_H += sideband.h LIB_H += sigchain.h LIB_H += strbuf.h LIB_H += string-list.h +LIB_H += submodule.h LIB_H += tag.h LIB_H += transport.h LIB_H += tree.h @@ -550,6 +556,7 @@ LIB_OBJS += sideband.o LIB_OBJS += sigchain.o LIB_OBJS += strbuf.o LIB_OBJS += string-list.o +LIB_OBJS += submodule.o LIB_OBJS += symlinks.o LIB_OBJS += tag.o LIB_OBJS += trace.o @@ -31,6 +31,8 @@ static void format_subst(const struct commit *commit, { char *to_free = NULL; struct strbuf fmt = STRBUF_INIT; + struct pretty_print_context ctx = {0}; + ctx.date_mode = DATE_NORMAL; if (src == buf->buf) to_free = strbuf_detach(buf, NULL); @@ -48,7 +50,7 @@ static void format_subst(const struct commit *commit, strbuf_add(&fmt, b + 8, c - b - 8); strbuf_add(buf, src, b - src); - format_commit_message(commit, fmt.buf, buf, DATE_NORMAL); + format_commit_message(commit, fmt.buf, buf, &ctx); len -= c + 1 - src; src = c + 1; } diff --git a/builtin-blame.c b/builtin-blame.c index 7512773b40..dd16b22297 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -1604,6 +1604,9 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent) } while (ch != '\n' && cp < sb->final_buf + sb->final_buf_size); } + + if (sb->final_buf_size && cp[-1] != '\n') + putchar('\n'); } static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) @@ -1667,6 +1670,9 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt) } while (ch != '\n' && cp < sb->final_buf + sb->final_buf_size); } + + if (sb->final_buf_size && cp[-1] != '\n') + putchar('\n'); } static void output(struct scoreboard *sb, int option) diff --git a/builtin-branch.c b/builtin-branch.c index 9f57992062..05e876e285 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -387,8 +387,9 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, commit = item->commit; if (commit && !parse_commit(commit)) { + struct pretty_print_context ctx = {0}; pretty_print_commit(CMIT_FMT_ONELINE, commit, - &subject, 0, NULL, NULL, 0, 0); + &subject, &ctx); sub = subject.buf; } diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c index f9381e07ea..513f134638 100644 --- a/builtin-check-ref-format.c +++ b/builtin-check-ref-format.c @@ -7,6 +7,32 @@ #include "builtin.h" #include "strbuf.h" +static const char builtin_check_ref_format_usage[] = +"git check-ref-format [--print] <refname>\n" +" or: git check-ref-format --branch <branchname-shorthand>"; + +/* + * Replace each run of adjacent slashes in src with a single slash, + * and write the result to dst. + * + * This function is similar to normalize_path_copy(), but stripped down + * to meet check_ref_format's simpler needs. + */ +static void collapse_slashes(char *dst, const char *src) +{ + char ch; + char prev = '\0'; + + while ((ch = *src++) != '\0') { + if (prev == '/' && ch == prev) + continue; + + *dst++ = ch; + prev = ch; + } + *dst = '\0'; +} + int cmd_check_ref_format(int argc, const char **argv, const char *prefix) { if (argc == 3 && !strcmp(argv[1], "--branch")) { @@ -17,7 +43,16 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix) printf("%s\n", sb.buf + 11); exit(0); } + if (argc == 3 && !strcmp(argv[1], "--print")) { + char *refname = xmalloc(strlen(argv[2]) + 1); + + if (check_ref_format(argv[2])) + exit(1); + collapse_slashes(refname, argv[2]); + printf("%s\n", refname); + exit(0); + } if (argc != 2) - usage("git check-ref-format refname"); + usage(builtin_check_ref_format_usage); return !!check_ref_format(argv[1]); } diff --git a/builtin-checkout.c b/builtin-checkout.c index d050c3789f..64f3a11ae1 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -302,8 +302,9 @@ static void show_local_changes(struct object *head) static void describe_detached_head(char *msg, struct commit *commit) { struct strbuf sb = STRBUF_INIT; + struct pretty_print_context ctx = {0}; parse_commit(commit); - pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, 0, NULL, NULL, 0, 0); + pretty_print_commit(CMIT_FMT_ONELINE, commit, &sb, &ctx); fprintf(stderr, "%s %s... %s\n", msg, find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf); strbuf_release(&sb); @@ -572,6 +573,40 @@ static int interactive_checkout(const char *revision, const char **pathspec, return run_add_interactive(revision, "--patch=checkout", pathspec); } +struct tracking_name_data { + const char *name; + char *remote; + int unique; +}; + +static int check_tracking_name(const char *refname, const unsigned char *sha1, + int flags, void *cb_data) +{ + struct tracking_name_data *cb = cb_data; + const char *slash; + + if (prefixcmp(refname, "refs/remotes/")) + return 0; + slash = strchr(refname + 13, '/'); + if (!slash || strcmp(slash + 1, cb->name)) + return 0; + if (cb->remote) { + cb->unique = 0; + return 0; + } + cb->remote = xstrdup(refname); + return 0; +} + +static const char *unique_tracking_name(const char *name) +{ + struct tracking_name_data cb_data = { name, NULL, 1 }; + for_each_ref(check_tracking_name, &cb_data); + if (cb_data.unique) + return cb_data.remote; + free(cb_data.remote); + return NULL; +} int cmd_checkout(int argc, const char **argv, const char *prefix) { @@ -582,6 +617,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) struct tree *source_tree = NULL; char *conflict_style = NULL; int patch_mode = 0; + int dwim_new_local_branch = 1; struct option options[] = { OPT__QUIET(&opts.quiet), OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), @@ -597,6 +633,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT_STRING(0, "conflict", &conflict_style, "style", "conflict style (merge or diff3)"), OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"), + { OPTION_BOOLEAN, 0, "guess", &dwim_new_local_branch, NULL, + "second guess 'git checkout no-such-branch'", + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_END(), }; int has_dash_dash; @@ -630,8 +669,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) opts.new_branch = argv0 + 1; } - if (opts.track == BRANCH_TRACK_UNSPECIFIED) - opts.track = git_branch_track; if (conflict_style) { opts.merge = 1; /* implied */ git_xmerge_config("merge.conflictstyle", conflict_style, NULL); @@ -655,6 +692,11 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) * With no paths, if <something> is a commit, that is to * switch to the branch or detach HEAD at it. * + * With no paths, if <something> is _not_ a commit, no -t nor -b + * was given, and there is a tracking branch whose name is + * <something> in one and only one remote, then this is a short-hand + * to fork local <something> from that remote tracking branch. + * * Otherwise <something> shall not be ambiguous. * - If it's *only* a reference, treat it like case (1). * - If it's only a path, treat it like case (2). @@ -677,7 +719,21 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) if (get_sha1(arg, rev)) { if (has_dash_dash) /* case (1) */ die("invalid reference: %s", arg); - goto no_reference; /* case (3 -> 2) */ + if (!patch_mode && + dwim_new_local_branch && + opts.track == BRANCH_TRACK_UNSPECIFIED && + !opts.new_branch && + !check_filename(NULL, arg) && + argc == 1) { + const char *remote = unique_tracking_name(arg); + if (!remote || get_sha1(remote, rev)) + goto no_reference; + opts.new_branch = arg; + arg = remote; + /* DWIMmed to create local branch */ + } + else + goto no_reference; } /* we can't end up being in (2) anymore, eat the argument */ @@ -715,6 +771,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) } no_reference: + + if (opts.track == BRANCH_TRACK_UNSPECIFIED) + opts.track = git_branch_track; + if (argc) { const char **pathspec = get_pathspec(prefix, argv); diff --git a/builtin-clone.c b/builtin-clone.c index 5762a6f9d8..caf3025031 100644 --- a/builtin-clone.c +++ b/builtin-clone.c @@ -51,7 +51,9 @@ static struct option builtin_clone_options[] = { OPT_BOOLEAN('n', "no-checkout", &option_no_checkout, "don't create a checkout"), OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"), - OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"), + { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL, + "create a bare repository", + PARSE_OPT_NOARG | PARSE_OPT_HIDDEN }, OPT_BOOLEAN(0, "mirror", &option_mirror, "create a mirror repository (implies bare)"), OPT_BOOLEAN('l', "local", &option_local, @@ -61,7 +63,7 @@ static struct option builtin_clone_options[] = { OPT_BOOLEAN('s', "shared", &option_shared, "setup as shared repository"), OPT_BOOLEAN(0, "recursive", &option_recursive, - "setup as shared repository"), + "initialize submodules in the clone"), OPT_STRING(0, "template", &option_template, "path", "path the template repository"), OPT_STRING(0, "reference", &option_reference, "repo", @@ -377,8 +379,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, builtin_clone_options, builtin_clone_usage, 0); + if (argc > 2) + usage_msg_opt("Too many arguments.", + builtin_clone_usage, builtin_clone_options); + if (argc == 0) - die("You must specify a repository to clone."); + usage_msg_opt("You must specify a repository to clone.", + builtin_clone_usage, builtin_clone_options); if (option_mirror) option_bare = 1; diff --git a/builtin-commit.c b/builtin-commit.c index c395cbf14f..d525b894ec 100644 --- a/builtin-commit.c +++ b/builtin-commit.c @@ -530,7 +530,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix, for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--) ; /* do nothing */ if (prefixcmp(sb.buf + i, sob.buf)) { - if (!ends_rfc2822_footer(&sb)) + if (!i || !ends_rfc2822_footer(&sb)) strbuf_addch(&sb, '\n'); strbuf_addbuf(&sb, &sob); } @@ -725,8 +725,10 @@ static const char *find_author_by_nickname(const char *name) prepare_revision_walk(&revs); commit = get_revision(&revs); if (commit) { + struct pretty_print_context ctx = {0}; + ctx.date_mode = DATE_NORMAL; strbuf_release(&buf); - format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL); + format_commit_message(commit, "%an <%ae>", &buf, &ctx); return strbuf_detach(&buf, NULL); } die("No existing author found with '%s'", name); @@ -983,8 +985,10 @@ static void print_summary(const char *prefix, const unsigned char *sha1) initial_commit ? " (root-commit)" : ""); if (!log_tree_commit(&rev, commit)) { + struct pretty_print_context ctx = {0}; struct strbuf buf = STRBUF_INIT; - format_commit_message(commit, format + 7, &buf, DATE_NORMAL); + ctx.date_mode = DATE_NORMAL; + format_commit_message(commit, format + 7, &buf, &ctx); printf("%s\n", buf.buf); strbuf_release(&buf); } diff --git a/builtin-describe.c b/builtin-describe.c index eaa8a9d229..d4efb10ddf 100644 --- a/builtin-describe.c +++ b/builtin-describe.c @@ -5,12 +5,14 @@ #include "builtin.h" #include "exec_cmd.h" #include "parse-options.h" +#include "diff.h" #define SEEN (1u<<0) #define MAX_TAGS (FLAG_BITS - 1) static const char * const describe_usage[] = { "git describe [options] <committish>*", + "git describe [options] --dirty", NULL }; @@ -23,6 +25,13 @@ static int max_candidates = 10; static int found_names; static const char *pattern; static int always; +static const char *dirty; + +/* diff-index command arguments to check if working tree is dirty. */ +static const char *diff_index_args[] = { + "diff-index", "--quiet", "HEAD", "--", NULL +}; + struct commit_name { struct tag *tag; @@ -96,8 +105,6 @@ static int get_name(const char *path, const unsigned char *sha1, int flag, void if (!all) { if (!prio) return 0; - if (!tags && prio < 2) - return 0; } add_to_known_names(all ? path + 5 : path + 10, commit, prio, sha1); return 0; @@ -184,6 +191,7 @@ static void describe(const char *arg, int last_one) struct possible_tag all_matches[MAX_TAGS]; unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; unsigned long seen_commits = 0; + unsigned int unannotated_cnt = 0; if (get_sha1(arg, sha1)) die("Not a valid object name %s", arg); @@ -199,6 +207,8 @@ static void describe(const char *arg, int last_one) display_name(n); if (longformat) show_suffix(0, n->tag ? n->tag->tagged->sha1 : sha1); + if (dirty) + printf("%s", dirty); printf("\n"); return; } @@ -217,7 +227,9 @@ static void describe(const char *arg, int last_one) seen_commits++; n = c->util; if (n) { - if (match_cnt < max_candidates) { + if (!tags && !all && n->prio < 2) { + unannotated_cnt++; + } else if (match_cnt < max_candidates) { struct possible_tag *t = &all_matches[match_cnt++]; t->name = n; t->depth = seen_commits - 1; @@ -256,10 +268,20 @@ static void describe(const char *arg, int last_one) if (!match_cnt) { const unsigned char *sha1 = cmit->object.sha1; if (always) { - printf("%s\n", find_unique_abbrev(sha1, abbrev)); + printf("%s", find_unique_abbrev(sha1, abbrev)); + if (dirty) + printf("%s", dirty); + printf("\n"); return; } - die("cannot describe '%s'", sha1_to_hex(sha1)); + if (unannotated_cnt) + die("No annotated tags can describe '%s'.\n" + "However, there were unannotated tags: try --tags.", + sha1_to_hex(sha1)); + else + die("No tags can describe '%s'.\n" + "Try --always, or create some tags.", + sha1_to_hex(sha1)); } qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt); @@ -291,6 +313,8 @@ static void describe(const char *arg, int last_one) display_name(all_matches[0].name); if (abbrev) show_suffix(all_matches[0].depth, cmit->object.sha1); + if (dirty) + printf("%s", dirty); printf("\n"); if (!last_one) @@ -315,6 +339,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix) "only consider tags matching <pattern>"), OPT_BOOLEAN(0, "always", &always, "show abbreviated commit object as fallback"), + {OPTION_STRING, 0, "dirty", &dirty, "mark", + "append <mark> on dirty working tree (default: \"-dirty\")", + PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"}, OPT_END(), }; @@ -355,7 +382,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix) die("No names found, cannot describe anything."); if (argc == 0) { + if (dirty && !cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1, diff_index_args, prefix)) + dirty = NULL; describe("HEAD", 1); + } else if (dirty) { + die("--dirty is incompatible with committishes"); } else { while (argc-- > 0) { describe(*argv++, argc == 0); diff --git a/builtin-log.c b/builtin-log.c index 25e21ed415..fb5308220d 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -891,6 +891,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) struct patch_ids ids; char *add_signoff = NULL; struct strbuf buf = STRBUF_INIT; + int use_patch_format = 0; const struct option builtin_format_patch_options[] = { { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL, "use [PATCH n/m] even with a single patch", @@ -920,6 +921,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback }, OPT_BOOLEAN(0, "no-binary", &no_binary_diff, "don't output binary diffs"), + OPT_BOOLEAN('p', NULL, &use_patch_format, + "show patch format instead of default (patch + stat)"), OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream, "don't include a patch matching a commit upstream"), OPT_GROUP("Messaging"), @@ -1027,8 +1030,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix) if (argc > 1) die ("unrecognized argument: %s", argv[1]); - if (!rev.diffopt.output_format - || rev.diffopt.output_format == DIFF_FORMAT_PATCH) + if (use_patch_format) + rev.diffopt.output_format |= DIFF_FORMAT_PATCH; + else if (!rev.diffopt.output_format || + rev.diffopt.output_format == DIFF_FORMAT_PATCH) rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH; if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff) @@ -1304,8 +1309,9 @@ int cmd_cherry(int argc, const char **argv, const char *prefix) if (verbose) { struct strbuf buf = STRBUF_INIT; + struct pretty_print_context ctx = {0}; pretty_print_commit(CMIT_FMT_ONELINE, commit, - &buf, 0, NULL, NULL, 0, 0); + &buf, &ctx); printf("%c %s %s\n", sign, sha1_to_hex(commit->object.sha1), buf.buf); strbuf_release(&buf); diff --git a/builtin-ls-files.c b/builtin-ls-files.c index c5c0407b0b..c9a03e5427 100644 --- a/builtin-ls-files.c +++ b/builtin-ls-files.c @@ -170,6 +170,10 @@ static void show_files(struct dir_struct *dir, const char *prefix) if (show_cached | show_stage) { for (i = 0; i < active_nr; i++) { struct cache_entry *ce = active_cache[i]; + int dtype = ce_to_dtype(ce); + if (dir->flags & DIR_SHOW_IGNORED && + !excluded(dir, ce->name, &dtype)) + continue; if (show_unmerged && !ce_stage(ce)) continue; if (ce->ce_flags & CE_UPDATE) @@ -182,6 +186,10 @@ static void show_files(struct dir_struct *dir, const char *prefix) struct cache_entry *ce = active_cache[i]; struct stat st; int err; + int dtype = ce_to_dtype(ce); + if (dir->flags & DIR_SHOW_IGNORED && + !excluded(dir, ce->name, &dtype)) + continue; if (ce->ce_flags & CE_UPDATE) continue; err = lstat(ce->name, &st); diff --git a/builtin-ls-remote.c b/builtin-ls-remote.c index 78a88f7476..b5bad0c184 100644 --- a/builtin-ls-remote.c +++ b/builtin-ls-remote.c @@ -86,10 +86,10 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix) pattern[j - i] = p; } } - remote = nongit ? NULL : remote_get(dest); - if (remote && !remote->url_nr) + remote = remote_get(dest); + if (!remote->url_nr) die("remote %s has no configured URL", dest); - transport = transport_get(remote, remote ? remote->url[0] : dest); + transport = transport_get(remote, remote->url[0]); if (uploadpack != NULL) transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack); diff --git a/builtin-merge.c b/builtin-merge.c index b6b84286b2..c8d7bdecc4 100644 --- a/builtin-merge.c +++ b/builtin-merge.c @@ -43,6 +43,7 @@ static const char * const builtin_merge_usage[] = { static int show_diffstat = 1, option_log, squash; static int option_commit = 1, allow_fast_forward = 1; +static int fast_forward_only; static int allow_trivial = 1, have_message; static struct strbuf merge_msg; static struct commit_list *remoteheads; @@ -167,6 +168,8 @@ static struct option builtin_merge_options[] = { "perform a commit if the merge succeeds (default)"), OPT_BOOLEAN(0, "ff", &allow_fast_forward, "allow fast forward (default)"), + OPT_BOOLEAN(0, "ff-only", &fast_forward_only, + "abort if fast forward is not possible"), OPT_CALLBACK('s', "strategy", &use_strategies, "strategy", "merge strategy to use", option_parse_strategy), OPT_CALLBACK('m', "message", &merge_msg, "message", @@ -264,6 +267,7 @@ static void squash_message(void) struct strbuf out = STRBUF_INIT; struct commit_list *j; int fd; + struct pretty_print_context ctx = {0}; printf("Squash commit -- not updating HEAD\n"); fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666); @@ -285,13 +289,15 @@ static void squash_message(void) if (prepare_revision_walk(&rev)) die("revision walk setup failed"); + ctx.abbrev = rev.abbrev; + ctx.date_mode = rev.date_mode; + strbuf_addstr(&out, "Squashed commit of the following:\n"); while ((commit = get_revision(&rev)) != NULL) { strbuf_addch(&out, '\n'); strbuf_addf(&out, "commit %s\n", sha1_to_hex(commit->object.sha1)); - pretty_print_commit(rev.commit_format, commit, &out, rev.abbrev, - NULL, NULL, rev.date_mode, 0); + pretty_print_commit(rev.commit_format, commit, &out, &ctx); } if (write(fd, out.buf, out.len) < 0) die_errno("Writing SQUASH_MSG"); @@ -840,7 +846,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix) const char *best_strategy = NULL, *wt_strategy = NULL; struct commit_list **remotes = &remoteheads; - setup_work_tree(); if (file_exists(git_path("MERGE_HEAD"))) die("You have not concluded your merge. (MERGE_HEAD exists)"); if (read_cache_unmerged()) @@ -874,6 +879,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) option_commit = 0; } + if (!allow_fast_forward && fast_forward_only) + die("You cannot combine --no-ff with --ff-only."); + if (!argc) usage_with_options(builtin_merge_usage, builtin_merge_options); @@ -1040,7 +1048,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix) * only one common. */ refresh_cache(REFRESH_QUIET); - if (allow_trivial) { + if (allow_trivial && !fast_forward_only) { /* See if it is really trivial. */ git_committer_info(IDENT_ERROR_ON_NO_NAME); printf("Trying really trivial in-index merge...\n"); @@ -1079,6 +1087,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix) } } + if (fast_forward_only) + die("Not possible to fast forward, aborting."); + /* We are going to make a new commit. */ git_committer_info(IDENT_ERROR_ON_NO_NAME); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 02f9246cdb..4c91e944c2 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -1629,6 +1629,8 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size, struct thread_params *p; int i, ret, active_threads = 0; + if (!delta_search_threads) /* --threads=0 means autodetect */ + delta_search_threads = online_cpus(); if (delta_search_threads <= 1) { find_deltas(list, &list_size, window, depth, processed); return; @@ -2324,11 +2326,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) if (keep_unreachable && unpack_unreachable) die("--keep-unreachable and --unpack-unreachable are incompatible."); -#ifdef THREADED_DELTA_SEARCH - if (!delta_search_threads) /* --threads=0 means autodetect */ - delta_search_threads = online_cpus(); -#endif - prepare_packed_git(); if (progress) diff --git a/builtin-push.c b/builtin-push.c index b5cd2cdad0..752121f247 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -66,7 +66,6 @@ static void setup_push_tracking(void) static void setup_default_push_refspecs(void) { - git_config(git_default_config, NULL); switch (push_default) { default: case PUSH_DEFAULT_MATCHING: @@ -173,7 +172,6 @@ int cmd_push(int argc, const char **argv, const char *prefix) int tags = 0; int rc; const char *repo = NULL; /* default repository */ - struct option options[] = { OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET), OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE), @@ -181,7 +179,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL), OPT_BIT( 0 , "mirror", &flags, "mirror all refs", (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)), - OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror"), + OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"), OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN), OPT_BIT( 0, "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN), OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE), @@ -191,6 +189,7 @@ int cmd_push(int argc, const char **argv, const char *prefix) OPT_END() }; + git_config(git_default_config, NULL); argc = parse_options(argc, argv, prefix, options, push_usage, 0); if (tags) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index 4ba1c12e0b..ac1136a3f5 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -96,9 +96,10 @@ static void show_commit(struct commit *commit, void *data) if (revs->verbose_header && commit->buffer) { struct strbuf buf = STRBUF_INIT; - pretty_print_commit(revs->commit_format, commit, - &buf, revs->abbrev, NULL, NULL, - revs->date_mode, 0); + struct pretty_print_context ctx = {0}; + ctx.abbrev = revs->abbrev; + ctx.date_mode = revs->date_mode; + pretty_print_commit(revs->commit_format, commit, &buf, &ctx); if (revs->graph) { if (buf.len) { if (revs->commit_format != CMIT_FMT_ONELINE) @@ -319,6 +320,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix) memset(&info, 0, sizeof(info)); info.revs = &revs; + if (revs.bisect) + bisect_list = 1; quiet = DIFF_OPT_TST(&revs.diffopt, QUIET); for (i = 1 ; i < argc; i++) { diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 45bead6545..9526aafc6c 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -180,6 +180,12 @@ static int show_reference(const char *refname, const unsigned char *sha1, int fl return 0; } +static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +{ + show_rev(REVERSED, sha1, refname); + return 0; +} + static void show_datestring(const char *flag, const char *datestr) { static char buffer[100]; @@ -548,6 +554,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) for_each_ref(show_reference, NULL); continue; } + if (!strcmp(arg, "--bisect")) { + for_each_ref_in("refs/bisect/bad", show_reference, NULL); + for_each_ref_in("refs/bisect/good", anti_reference, NULL); + continue; + } if (!strcmp(arg, "--branches")) { for_each_branch_ref(show_reference, NULL); continue; diff --git a/builtin-shortlog.c b/builtin-shortlog.c index 4d4a3c82d6..8aa63c7857 100644 --- a/builtin-shortlog.c +++ b/builtin-shortlog.c @@ -158,9 +158,12 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit) sha1_to_hex(commit->object.sha1)); if (log->user_format) { struct strbuf buf = STRBUF_INIT; - - pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &buf, - DEFAULT_ABBREV, "", "", DATE_NORMAL, 0); + struct pretty_print_context ctx = {0}; + ctx.abbrev = DEFAULT_ABBREV; + ctx.subject = ""; + ctx.after_subject = ""; + ctx.date_mode = DATE_NORMAL; + pretty_print_commit(CMIT_FMT_USERFORMAT, commit, &buf, &ctx); insert_one_record(log, author, buf.buf); strbuf_release(&buf); return; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index be95930b78..9f13caa76d 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -293,8 +293,8 @@ static void show_one_commit(struct commit *commit, int no_name) struct commit_name *name = commit->util; if (commit->object.parsed) { - pretty_print_commit(CMIT_FMT_ONELINE, commit, - &pretty, 0, NULL, NULL, 0, 0); + struct pretty_print_context ctx = {0}; + pretty_print_commit(CMIT_FMT_ONELINE, commit, &pretty, &ctx); pretty_str = pretty.buf; } if (!prefixcmp(pretty_str, "[PATCH] ")) @@ -396,6 +396,7 @@ extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); extern const char *prefix_path(const char *prefix, int len, const char *path); extern const char *prefix_filename(const char *prefix, int len, const char *path); +extern int check_filename(const char *prefix, const char *name); extern void verify_filename(const char *prefix, const char *name); extern void verify_non_filename(const char *prefix, const char *name); @@ -132,8 +132,8 @@ struct commit_graft *read_graft_line(char *buf, int len) int i; struct commit_graft *graft = NULL; - if (buf[len-1] == '\n') - buf[--len] = 0; + while (len && isspace(buf[len-1])) + buf[--len] = '\0'; if (buf[0] == '#' || buf[0] == '\0') return NULL; if ((len + 1) % 41) { @@ -63,6 +63,16 @@ enum cmit_fmt { CMIT_FMT_UNSPECIFIED, }; +struct pretty_print_context +{ + int abbrev; + const char *subject; + const char *after_subject; + enum date_mode date_mode; + int need_8bit_cte; + struct reflog_walk_info *reflog_info; +}; + extern int non_ascii(int); extern int has_non_ascii(const char *text); struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */ @@ -71,12 +81,10 @@ extern char *reencode_commit_message(const struct commit *commit, extern void get_commit_format(const char *arg, struct rev_info *); extern void format_commit_message(const struct commit *commit, const char *format, struct strbuf *sb, - enum date_mode dmode); -extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*, - struct strbuf *, - int abbrev, const char *subject, - const char *after_subject, enum date_mode, - int need_8bit_cte); + const struct pretty_print_context *context); +extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, + struct strbuf *sb, + const struct pretty_print_context *context); void pp_user_info(const char *what, enum cmit_fmt fmt, struct strbuf *sb, const char *line, enum date_mode dmode, const char *encoding); diff --git a/compat/bswap.h b/compat/bswap.h index 5cc4acbfcc..279e0b48b1 100644 --- a/compat/bswap.h +++ b/compat/bswap.h @@ -28,6 +28,16 @@ static inline uint32_t default_swab32(uint32_t val) } \ __res; }) +#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) + +#include <stdlib.h> + +#define bswap32(x) _byteswap_ulong(x) + +#endif + +#ifdef bswap32 + #undef ntohl #undef htonl #define ntohl(x) bswap32(x) diff --git a/contrib/buildsystems/Generators/Vcproj.pm b/contrib/buildsystems/Generators/Vcproj.pm index be94ba18d2..cfa74adcc2 100644 --- a/contrib/buildsystems/Generators/Vcproj.pm +++ b/contrib/buildsystems/Generators/Vcproj.pm @@ -178,6 +178,7 @@ sub createLibProject { MinimalRebuild="true" RuntimeLibrary="1" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> @@ -244,6 +245,7 @@ sub createLibProject { RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> @@ -401,6 +403,7 @@ sub createAppProject { MinimalRebuild="true" RuntimeLibrary="1" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> @@ -472,6 +475,7 @@ sub createAppProject { RuntimeLibrary="0" EnableFunctionLevelLinking="true" UsePrecompiledHeader="0" + ProgramDataBaseFileName="\$(IntDir)\\\$(TargetName).pdb" WarningLevel="3" DebugInformationFormat="3" /> diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 2a9a88968f..bd66639d48 100755 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -1224,7 +1224,7 @@ _git_log () __git_merge_options=" --no-commit --no-stat --log --no-log --squash --strategy - --commit --stat --no-squash --ff --no-ff + --commit --stat --no-squash --ff --no-ff --ff-only " _git_merge () @@ -13,6 +13,7 @@ #include "utf8.h" #include "userdiff.h" #include "sigchain.h" +#include "submodule.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1557,6 +1558,17 @@ static void builtin_diff(const char *name_a, const char *a_prefix, *b_prefix; const char *textconv_one = NULL, *textconv_two = NULL; + if (DIFF_OPT_TST(o, SUBMODULE_LOG) && + (!one->mode || S_ISGITLINK(one->mode)) && + (!two->mode || S_ISGITLINK(two->mode))) { + const char *del = diff_get_color_opt(o, DIFF_FILE_OLD); + const char *add = diff_get_color_opt(o, DIFF_FILE_NEW); + show_submodule_summary(o->file, one ? one->path : two->path, + one->sha1, two->sha1, + del, add, reset); + return; + } + if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) { textconv_one = get_textconv(one); textconv_two = get_textconv(two); @@ -2757,6 +2769,12 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) DIFF_OPT_CLR(options, ALLOW_TEXTCONV); else if (!strcmp(arg, "--ignore-submodules")) DIFF_OPT_SET(options, IGNORE_SUBMODULES); + else if (!strcmp(arg, "--submodule")) + DIFF_OPT_SET(options, SUBMODULE_LOG); + else if (!prefixcmp(arg, "--submodule=")) { + if (!strcmp(arg + 12, "log")) + DIFF_OPT_SET(options, SUBMODULE_LOG); + } /* misc options */ else if (!strcmp(arg, "-z")) @@ -66,6 +66,9 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, #define DIFF_OPT_DIRSTAT_CUMULATIVE (1 << 19) #define DIFF_OPT_DIRSTAT_BY_FILE (1 << 20) #define DIFF_OPT_ALLOW_TEXTCONV (1 << 21) + +#define DIFF_OPT_SUBMODULE_LOG (1 << 23) + #define DIFF_OPT_TST(opts, flag) ((opts)->flags & DIFF_OPT_##flag) #define DIFF_OPT_SET(opts, flag) ((opts)->flags |= DIFF_OPT_##flag) #define DIFF_OPT_CLR(opts, flag) ((opts)->flags &= ~DIFF_OPT_##flag) diff --git a/git-add--interactive.perl b/git-add--interactive.perl index 69aeaf03ec..8ce1ec92c2 100755 --- a/git-add--interactive.perl +++ b/git-add--interactive.perl @@ -731,14 +731,17 @@ sub parse_diff_header { my $head = { TEXT => [], DISPLAY => [], TYPE => 'header' }; my $mode = { TEXT => [], DISPLAY => [], TYPE => 'mode' }; + my $deletion = { TEXT => [], DISPLAY => [], TYPE => 'deletion' }; for (my $i = 0; $i < @{$src->{TEXT}}; $i++) { - my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? - $mode : $head; + my $dest = + $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ? $mode : + $src->{TEXT}->[$i] =~ /^deleted file/ ? $deletion : + $head; push @{$dest->{TEXT}}, $src->{TEXT}->[$i]; push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i]; } - return ($head, $mode); + return ($head, $mode, $deletion); } sub hunk_splittable { @@ -1206,7 +1209,7 @@ sub patch_update_file { my ($ix, $num); my $path = shift; my ($head, @hunk) = parse_diff($path); - ($head, my $mode) = parse_diff_header($head); + ($head, my $mode, my $deletion) = parse_diff_header($head); for (@{$head->{DISPLAY}}) { print; } @@ -1214,6 +1217,9 @@ sub patch_update_file { if (@{$mode->{TEXT}}) { unshift @hunk, $mode; } + if (@{$deletion->{TEXT}} && !@hunk) { + @hunk = ($deletion); + } $num = scalar @hunk; $ix = 0; @@ -1267,7 +1273,9 @@ sub patch_update_file { print; } print colored $prompt_color, $patch_mode_flavour{VERB}, - ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : ' this hunk'), + ($hunk[$ix]{TYPE} eq 'mode' ? ' mode change' : + $hunk[$ix]{TYPE} eq 'deletion' ? ' deletion' : + ' this hunk'), $patch_mode_flavour{TARGET}, " [y,n,q,a,d,/$other,?]? "; my $line = prompt_single_character; diff --git a/git-bisect.sh b/git-bisect.sh index 6f6f03966f..8b3c5858a9 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -13,8 +13,8 @@ git bisect skip [(<rev>|<range>)...] mark <rev>... untestable revisions. git bisect next find next bisection to test and check it out. -git bisect reset [<branch>] - finish bisection search and go back to branch. +git bisect reset [<commit>] + finish bisection search and go back to commit. git bisect visualize show bisect status in gitk. git bisect replay <logfile> @@ -311,8 +311,8 @@ bisect_reset() { } case "$#" in 0) branch=$(cat "$GIT_DIR/BISECT_START") ;; - 1) git show-ref --verify --quiet -- "refs/heads/$1" || - die "$1 does not seem to be a valid branch" + 1) git rev-parse --quiet --verify "$1^{commit}" > /dev/null || + die "'$1' is not a valid commit" branch="$1" ;; *) usage ;; diff --git a/git-pull.sh b/git-pull.sh index fc78592ae0..37f3d93017 100755 --- a/git-pull.sh +++ b/git-pull.sh @@ -16,7 +16,8 @@ cd_to_toplevel test -z "$(git ls-files -u)" || die "You are in the middle of a conflicted merge." -strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity= +strategy_args= diffstat= no_commit= squash= no_ff= ff_only= +log_arg= verbosity= curr_branch=$(git symbolic-ref -q HEAD) curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||") rebase=$(git config --bool branch.$curr_branch_short.rebase) @@ -45,6 +46,8 @@ do no_ff=--ff ;; --no-ff) no_ff=--no-ff ;; + --ff-only) + ff_only=--ff-only ;; -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\ --strateg=*|--strategy=*|\ -s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy) @@ -215,5 +218,5 @@ merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit test true = "$rebase" && exec git-rebase $diffstat $strategy_args --onto $merge_head \ ${oldremoteref:-$merge_head} -exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \ +exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \ "$merge_name" HEAD $merge_head $verbosity diff --git a/git-stash.sh b/git-stash.sh index 4febbbfa5d..f796c2fe24 100755 --- a/git-stash.sh +++ b/git-stash.sh @@ -205,8 +205,7 @@ have_stash () { list_stash () { have_stash || return 0 - git log --no-color --pretty=oneline -g "$@" $ref_stash -- | - sed -n -e 's/^[.0-9a-f]* refs\///p' + git log --format="%gd: %gs" -g "$@" $ref_stash -- } show_stash () { @@ -383,11 +382,6 @@ test -n "$seen_non_option" || set "save" "$@" case "$1" in list) shift - if test $# = 0 - then - set x -n 10 - shift - fi list_stash "$@" ;; show) diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index e4cbfc35a7..62325ea877 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -5127,7 +5127,8 @@ sub git_blob { chomp $line; $nr++; $line = untabify($line); - printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", + printf "<div class=\"pre\"><a id=\"l%i\" href=\"" . href(-replay => 1) + . "#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, esc_html($line, -nbsp=>1); } } diff --git a/imap-send.c b/imap-send.c index 3847fd151d..f805c6ed81 100644 --- a/imap-send.c +++ b/imap-send.c @@ -273,7 +273,11 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve fprintf(stderr, "SSL requested but SSL support not compiled in\n"); return -1; #else +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L) + const SSL_METHOD *meth; +#else SSL_METHOD *meth; +#endif SSL_CTX *ctx; int ret; diff --git a/log-tree.c b/log-tree.c index f7d54f2f1b..0fdf159f80 100644 --- a/log-tree.c +++ b/log-tree.c @@ -179,8 +179,10 @@ void get_patch_filename(struct commit *commit, int nr, const char *suffix, strbuf_addf(buf, commit ? "%04d-" : "%d", nr); if (commit) { int max_len = start_len + FORMAT_PATCH_NAME_MAX - suffix_len; + struct pretty_print_context ctx = {0}; + ctx.date_mode = DATE_NORMAL; - format_commit_message(commit, "%f", buf, DATE_NORMAL); + format_commit_message(commit, "%f", buf, &ctx); if (max_len < buf->len) strbuf_setlen(buf, max_len); strbuf_addstr(buf, suffix); @@ -277,10 +279,9 @@ void show_log(struct rev_info *opt) struct strbuf msgbuf = STRBUF_INIT; struct log_info *log = opt->loginfo; struct commit *commit = log->commit, *parent = log->parent; - int abbrev = opt->diffopt.abbrev; int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40; - const char *subject = NULL, *extra_headers = opt->extra_headers; - int need_8bit_cte = 0; + const char *extra_headers = opt->extra_headers; + struct pretty_print_context ctx = {0}; opt->loginfo = NULL; if (!opt->verbose_header) { @@ -347,8 +348,8 @@ void show_log(struct rev_info *opt) */ if (opt->commit_format == CMIT_FMT_EMAIL) { - log_write_email_headers(opt, commit, &subject, &extra_headers, - &need_8bit_cte); + log_write_email_headers(opt, commit, &ctx.subject, &extra_headers, + &ctx.need_8bit_cte); } else if (opt->commit_format != CMIT_FMT_USERFORMAT) { fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), stdout); if (opt->commit_format != CMIT_FMT_ONELINE) @@ -405,11 +406,13 @@ void show_log(struct rev_info *opt) /* * And then the pretty-printed message itself */ - if (need_8bit_cte >= 0) - need_8bit_cte = has_non_ascii(opt->add_signoff); - pretty_print_commit(opt->commit_format, commit, &msgbuf, - abbrev, subject, extra_headers, opt->date_mode, - need_8bit_cte); + if (ctx.need_8bit_cte >= 0) + ctx.need_8bit_cte = has_non_ascii(opt->add_signoff); + ctx.date_mode = opt->date_mode; + ctx.abbrev = opt->diffopt.abbrev; + ctx.after_subject = extra_headers; + ctx.reflog_info = opt->reflog_info; + pretty_print_commit(opt->commit_format, commit, &msgbuf, &ctx); if (opt->add_signoff) append_signoff(&msgbuf, opt->add_signoff); @@ -7,6 +7,7 @@ #include "mailmap.h" #include "log-tree.h" #include "color.h" +#include "reflog-walk.h" static char *user_format; @@ -442,7 +443,7 @@ struct chunk { struct format_commit_context { const struct commit *commit; - enum date_mode dmode; + const struct pretty_print_context *pretty_ctx; unsigned commit_header_parsed:1; unsigned commit_message_parsed:1; @@ -701,6 +702,22 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, case 'd': format_decoration(sb, commit); return 1; + case 'g': /* reflog info */ + switch(placeholder[1]) { + case 'd': /* reflog selector */ + case 'D': + if (c->pretty_ctx->reflog_info) + get_reflog_selector(sb, + c->pretty_ctx->reflog_info, + c->pretty_ctx->date_mode, + (placeholder[1] == 'd')); + return 2; + case 's': /* reflog message */ + if (c->pretty_ctx->reflog_info) + get_reflog_message(sb, c->pretty_ctx->reflog_info); + return 2; + } + return 0; /* unknown %g placeholder */ } /* For the rest we have to parse the commit header. */ @@ -711,11 +728,11 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, case 'a': /* author ... */ return format_person_part(sb, placeholder[1], msg + c->author.off, c->author.len, - c->dmode); + c->pretty_ctx->date_mode); case 'c': /* committer ... */ return format_person_part(sb, placeholder[1], msg + c->committer.off, c->committer.len, - c->dmode); + c->pretty_ctx->date_mode); case 'e': /* encoding */ strbuf_add(sb, msg + c->encoding.off, c->encoding.len); return 1; @@ -741,13 +758,13 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder, void format_commit_message(const struct commit *commit, const char *format, struct strbuf *sb, - enum date_mode dmode) + const struct pretty_print_context *pretty_ctx) { struct format_commit_context context; memset(&context, 0, sizeof(context)); context.commit = commit; - context.dmode = dmode; + context.pretty_ctx = pretty_ctx; strbuf_expand(sb, format, format_commit_item, &context); } @@ -900,18 +917,18 @@ char *reencode_commit_message(const struct commit *commit, const char **encoding } void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, - struct strbuf *sb, int abbrev, - const char *subject, const char *after_subject, - enum date_mode dmode, int need_8bit_cte) + struct strbuf *sb, + const struct pretty_print_context *context) { unsigned long beginning_of_body; int indent = 4; const char *msg = commit->buffer; char *reencoded; const char *encoding; + int need_8bit_cte = context->need_8bit_cte; if (fmt == CMIT_FMT_USERFORMAT) { - format_commit_message(commit, user_format, sb, dmode); + format_commit_message(commit, user_format, sb, context); return; } @@ -946,8 +963,9 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, } } - pp_header(fmt, abbrev, dmode, encoding, commit, &msg, sb); - if (fmt != CMIT_FMT_ONELINE && !subject) { + pp_header(fmt, context->abbrev, context->date_mode, encoding, + commit, &msg, sb); + if (fmt != CMIT_FMT_ONELINE && !context->subject) { strbuf_addch(sb, '\n'); } @@ -956,8 +974,8 @@ void pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, /* These formats treat the title line specially. */ if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL) - pp_title_line(fmt, &msg, sb, subject, - after_subject, encoding, need_8bit_cte); + pp_title_line(fmt, &msg, sb, context->subject, + context->after_subject, encoding, need_8bit_cte); beginning_of_body = sb->len; if (fmt != CMIT_FMT_ONELINE) diff --git a/reflog-walk.c b/reflog-walk.c index 5623ea6b48..caba4f743f 100644 --- a/reflog-walk.c +++ b/reflog-walk.c @@ -8,6 +8,7 @@ struct complete_reflogs { char *ref; + const char *short_ref; struct reflog_info { unsigned char osha1[20], nsha1[20]; char *email; @@ -241,36 +242,74 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit) commit->object.flags &= ~(ADDED | SEEN | SHOWN); } -void show_reflog_message(struct reflog_walk_info *info, int oneline, +void get_reflog_selector(struct strbuf *sb, + struct reflog_walk_info *reflog_info, + enum date_mode dmode, + int shorten) +{ + struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; + struct reflog_info *info; + const char *printed_ref; + + if (!commit_reflog) + return; + + if (shorten) { + if (!commit_reflog->reflogs->short_ref) + commit_reflog->reflogs->short_ref + = shorten_unambiguous_ref(commit_reflog->reflogs->ref, 0); + printed_ref = commit_reflog->reflogs->short_ref; + } else { + printed_ref = commit_reflog->reflogs->ref; + } + + strbuf_addf(sb, "%s@{", printed_ref); + if (commit_reflog->flag || dmode) { + info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; + strbuf_addstr(sb, show_date(info->timestamp, info->tz, dmode)); + } else { + strbuf_addf(sb, "%d", commit_reflog->reflogs->nr + - 2 - commit_reflog->recno); + } + + strbuf_addch(sb, '}'); +} + +void get_reflog_message(struct strbuf *sb, + struct reflog_walk_info *reflog_info) +{ + struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; + struct reflog_info *info; + size_t len; + + if (!commit_reflog) + return; + + info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; + len = strlen(info->message); + if (len > 0) + len--; /* strip away trailing newline */ + strbuf_add(sb, info->message, len); +} + +void show_reflog_message(struct reflog_walk_info *reflog_info, int oneline, enum date_mode dmode) { - if (info && info->last_commit_reflog) { - struct commit_reflog *commit_reflog = info->last_commit_reflog; + if (reflog_info && reflog_info->last_commit_reflog) { + struct commit_reflog *commit_reflog = reflog_info->last_commit_reflog; struct reflog_info *info; + struct strbuf selector = STRBUF_INIT; info = &commit_reflog->reflogs->items[commit_reflog->recno+1]; + get_reflog_selector(&selector, reflog_info, dmode, 0); if (oneline) { - printf("%s@{", commit_reflog->reflogs->ref); - if (commit_reflog->flag || dmode) - printf("%s", show_date(info->timestamp, - info->tz, - dmode)); - else - printf("%d", commit_reflog->reflogs->nr - - 2 - commit_reflog->recno); - printf("}: %s", info->message); + printf("%s: %s", selector.buf, info->message); } else { - printf("Reflog: %s@{", commit_reflog->reflogs->ref); - if (commit_reflog->flag || dmode) - printf("%s", show_date(info->timestamp, - info->tz, - dmode)); - else - printf("%d", commit_reflog->reflogs->nr - - 2 - commit_reflog->recno); - printf("} (%s)\nReflog message: %s", - info->email, info->message); + printf("Reflog: %s (%s)\nReflog message: %s", + selector.buf, info->email, info->message); } + + strbuf_release(&selector); } } diff --git a/reflog-walk.h b/reflog-walk.h index 74c90964bd..7bd2cd4c4e 100644 --- a/reflog-walk.h +++ b/reflog-walk.h @@ -3,6 +3,8 @@ #include "cache.h" +struct reflog_walk_info; + extern void init_reflog_walk(struct reflog_walk_info** info); extern int add_reflog_for_walk(struct reflog_walk_info *info, struct commit *commit, const char *name); @@ -10,5 +12,11 @@ extern void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit); extern void show_reflog_message(struct reflog_walk_info *info, int, enum date_mode); +extern void get_reflog_message(struct strbuf *sb, + struct reflog_walk_info *reflog_info); +extern void get_reflog_selector(struct strbuf *sb, + struct reflog_walk_info *reflog_info, + enum date_mode dmode, + int shorten); #endif diff --git a/remote-curl.c b/remote-curl.c index 2faf1c6344..ebdab3603e 100644 --- a/remote-curl.c +++ b/remote-curl.c @@ -82,9 +82,10 @@ int main(int argc, const char **argv) struct strbuf buf = STRBUF_INIT; const char *url; struct walker *walker = NULL; + int nongit; git_extract_argv0_path(argv[0]); - setup_git_directory(); + setup_git_directory_gently(&nongit); if (argc < 2) { fprintf(stderr, "Remote needed\n"); return 1; @@ -103,6 +104,8 @@ int main(int argc, const char **argv) break; if (!prefixcmp(buf.buf, "fetch ")) { char *obj = buf.buf + strlen("fetch "); + if (nongit) + die("Fetch attempted without a local repo"); if (!walker) walker = get_http_walker(url, remote); walker->get_all = 1; diff --git a/revision.c b/revision.c index 9fc4e8d381..a36c0d9bcd 100644 --- a/revision.c +++ b/revision.c @@ -994,7 +994,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg if (!strcmp(arg, "--all") || !strcmp(arg, "--branches") || !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") || !strcmp(arg, "--reflog") || !strcmp(arg, "--not") || - !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk")) + !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") || + !strcmp(arg, "--bisect")) { unkv[(*unkc)++] = arg; return 1; @@ -1218,6 +1219,16 @@ void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx, ctx->argc -= n; } +static int for_each_bad_bisect_ref(each_ref_fn fn, void *cb_data) +{ + return for_each_ref_in("refs/bisect/bad", fn, cb_data); +} + +static int for_each_good_bisect_ref(each_ref_fn fn, void *cb_data) +{ + return for_each_ref_in("refs/bisect/good", fn, cb_data); +} + /* * Parse revision information, filling in the "rev_info" structure, * and removing the used arguments from the argument list. @@ -1259,6 +1270,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch handle_refs(revs, flags, for_each_branch_ref); continue; } + if (!strcmp(arg, "--bisect")) { + handle_refs(revs, flags, for_each_bad_bisect_ref); + handle_refs(revs, flags ^ UNINTERESTING, for_each_good_bisect_ref); + revs->bisect = 1; + continue; + } if (!strcmp(arg, "--tags")) { handle_refs(revs, flags, for_each_tag_ref); continue; diff --git a/revision.h b/revision.h index b6421a6432..921656aaab 100644 --- a/revision.h +++ b/revision.h @@ -63,6 +63,7 @@ struct rev_info { reverse:1, reverse_output_stage:1, cherry_pick:1, + bisect:1, first_parent_only:1; /* Diff flags */ @@ -61,6 +61,19 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) return path; } +int check_filename(const char *prefix, const char *arg) +{ + const char *name; + struct stat st; + + name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; + if (!lstat(name, &st)) + return 1; /* file exists */ + if (errno == ENOENT || errno == ENOTDIR) + return 0; /* file does not exist */ + die_errno("failed to stat '%s'", arg); +} + /* * Verify a filename that we got as an argument for a pathspec * entry. Note that a filename that begins with "-" never verifies @@ -70,18 +83,12 @@ const char *prefix_filename(const char *pfx, int pfx_len, const char *arg) */ void verify_filename(const char *prefix, const char *arg) { - const char *name; - struct stat st; - if (*arg == '-') die("bad flag '%s' used after filename", arg); - name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; - if (!lstat(name, &st)) + if (check_filename(prefix, arg)) return; - if (errno == ENOENT) - die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" - "Use '--' to separate paths from revisions", arg); - die_errno("failed to stat '%s'", arg); + die("ambiguous argument '%s': unknown revision or path not in the working tree.\n" + "Use '--' to separate paths from revisions", arg); } /* @@ -91,19 +98,14 @@ void verify_filename(const char *prefix, const char *arg) */ void verify_non_filename(const char *prefix, const char *arg) { - const char *name; - struct stat st; - if (!is_inside_work_tree() || is_inside_git_dir()) return; if (*arg == '-') return; /* flag */ - name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg; - if (!lstat(name, &st)) - die("ambiguous argument '%s': both revision and filename\n" - "Use '--' to separate filenames from revisions", arg); - if (errno != ENOENT && errno != ENOTDIR) - die_errno("failed to stat '%s'", arg); + if (!check_filename(prefix, arg)) + return; + die("ambiguous argument '%s': both revision and filename\n" + "Use '--' to separate filenames from revisions", arg); } const char **get_pathspec(const char *prefix, const char **pathspec) diff --git a/submodule.c b/submodule.c new file mode 100644 index 0000000000..461faf0e00 --- /dev/null +++ b/submodule.c @@ -0,0 +1,114 @@ +#include "cache.h" +#include "submodule.h" +#include "dir.h" +#include "diff.h" +#include "commit.h" +#include "revision.h" + +int add_submodule_odb(const char *path) +{ + struct strbuf objects_directory = STRBUF_INIT; + struct alternate_object_database *alt_odb; + + strbuf_addf(&objects_directory, "%s/.git/objects/", path); + if (!is_directory(objects_directory.buf)) + return -1; + + /* avoid adding it twice */ + for (alt_odb = alt_odb_list; alt_odb; alt_odb = alt_odb->next) + if (alt_odb->name - alt_odb->base == objects_directory.len && + !strncmp(alt_odb->base, objects_directory.buf, + objects_directory.len)) + return 0; + + alt_odb = xmalloc(objects_directory.len + 42 + sizeof(*alt_odb)); + alt_odb->next = alt_odb_list; + strcpy(alt_odb->base, objects_directory.buf); + alt_odb->name = alt_odb->base + objects_directory.len; + alt_odb->name[2] = '/'; + alt_odb->name[40] = '\0'; + alt_odb->name[41] = '\0'; + alt_odb_list = alt_odb; + prepare_alt_odb(); + return 0; +} + +void show_submodule_summary(FILE *f, const char *path, + unsigned char one[20], unsigned char two[20], + const char *del, const char *add, const char *reset) +{ + struct rev_info rev; + struct commit *commit, *left = left, *right; + struct commit_list *merge_bases, *list; + const char *message = NULL; + struct strbuf sb = STRBUF_INIT; + static const char *format = " %m %s"; + int fast_forward = 0, fast_backward = 0; + + if (is_null_sha1(two)) + message = "(submodule deleted)"; + else if (add_submodule_odb(path)) + message = "(not checked out)"; + else if (is_null_sha1(one)) + message = "(new submodule)"; + else if (!(left = lookup_commit_reference(one)) || + !(right = lookup_commit_reference(two))) + message = "(commits not present)"; + + if (!message) { + init_revisions(&rev, NULL); + setup_revisions(0, NULL, &rev, NULL); + rev.left_right = 1; + rev.first_parent_only = 1; + left->object.flags |= SYMMETRIC_LEFT; + add_pending_object(&rev, &left->object, path); + add_pending_object(&rev, &right->object, path); + merge_bases = get_merge_bases(left, right, 1); + if (merge_bases) { + if (merge_bases->item == left) + fast_forward = 1; + else if (merge_bases->item == right) + fast_backward = 1; + } + for (list = merge_bases; list; list = list->next) { + list->item->object.flags |= UNINTERESTING; + add_pending_object(&rev, &list->item->object, + sha1_to_hex(list->item->object.sha1)); + } + if (prepare_revision_walk(&rev)) + message = "(revision walker failed)"; + } + + strbuf_addf(&sb, "Submodule %s %s..", path, + find_unique_abbrev(one, DEFAULT_ABBREV)); + if (!fast_backward && !fast_forward) + strbuf_addch(&sb, '.'); + strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV)); + if (message) + strbuf_addf(&sb, " %s\n", message); + else + strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : ""); + fwrite(sb.buf, sb.len, 1, f); + + if (!message) { + while ((commit = get_revision(&rev))) { + struct pretty_print_context ctx = {0}; + ctx.date_mode = rev.date_mode; + strbuf_setlen(&sb, 0); + if (commit->object.flags & SYMMETRIC_LEFT) { + if (del) + strbuf_addstr(&sb, del); + } + else if (add) + strbuf_addstr(&sb, add); + format_commit_message(commit, format, &sb, &ctx); + if (reset) + strbuf_addstr(&sb, reset); + strbuf_addch(&sb, '\n'); + fprintf(f, "%s", sb.buf); + } + clear_commit_marks(left, ~0); + clear_commit_marks(right, ~0); + } + strbuf_release(&sb); +} diff --git a/submodule.h b/submodule.h new file mode 100644 index 0000000000..4c0269d679 --- /dev/null +++ b/submodule.h @@ -0,0 +1,8 @@ +#ifndef SUBMODULE_H +#define SUBMODULE_H + +void show_submodule_summary(FILE *f, const char *path, + unsigned char one[20], unsigned char two[20], + const char *del, const char *add, const char *reset); + +#endif diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh new file mode 100755 index 0000000000..eb45afb018 --- /dev/null +++ b/t/t1402-check-ref-format.sh @@ -0,0 +1,61 @@ +#!/bin/sh + +test_description='Test git check-ref-format' + +. ./test-lib.sh + +valid_ref() { + test_expect_success "ref name '$1' is valid" \ + "git check-ref-format '$1'" +} +invalid_ref() { + test_expect_success "ref name '$1' is not valid" \ + "test_must_fail git check-ref-format '$1'" +} + +valid_ref 'heads/foo' +invalid_ref 'foo' +valid_ref 'foo/bar/baz' +valid_ref 'refs///heads/foo' +invalid_ref 'heads/foo/' +invalid_ref './foo' +invalid_ref '.refs/foo' +invalid_ref 'heads/foo..bar' +invalid_ref 'heads/foo?bar' +valid_ref 'foo./bar' +invalid_ref 'heads/foo.lock' +valid_ref 'heads/foo@bar' +invalid_ref 'heads/v@{ation' +invalid_ref 'heads/foo\bar' + +test_expect_success "check-ref-format --branch @{-1}" ' + T=$(git write-tree) && + sha1=$(echo A | git commit-tree $T) && + git update-ref refs/heads/master $sha1 && + git update-ref refs/remotes/origin/master $sha1 + git checkout master && + git checkout origin/master && + git checkout master && + refname=$(git check-ref-format --branch @{-1}) && + test "$refname" = "$sha1" && + refname2=$(git check-ref-format --branch @{-2}) && + test "$refname2" = master' + +valid_ref_normalized() { + test_expect_success "ref name '$1' simplifies to '$2'" " + refname=\$(git check-ref-format --print '$1') && + test \"\$refname\" = '$2'" +} +invalid_ref_normalized() { + test_expect_success "check-ref-format --print rejects '$1'" " + test_must_fail git check-ref-format --print '$1'" +} + +valid_ref_normalized 'heads/foo' 'heads/foo' +valid_ref_normalized 'refs///heads/foo' 'refs/heads/foo' +invalid_ref_normalized 'foo' +invalid_ref_normalized 'heads/foo/../bar' +invalid_ref_normalized 'heads/./foo' +invalid_ref_normalized 'heads\foo' + +test_done diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh index fc1e379321..d5ec333131 100755 --- a/t/t3003-ls-files-exclude.sh +++ b/t/t3003-ls-files-exclude.sh @@ -29,4 +29,12 @@ test_expect_success 'add file to gitignore' ' ' check_all_output +test_expect_success 'ls-files -i lists only tracked-but-ignored files' ' + echo content >other-file && + git add other-file && + echo file >expect && + git ls-files -i --exclude-standard >output && + test_cmp expect output +' + test_done diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 687bd7ab53..d86bc81abf 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -228,4 +228,21 @@ test_expect_success 'add first line works' ' test_cmp expected diff ' +cat >expected <<EOF +diff --git a/empty b/empty +deleted file mode 100644 +index e69de29..0000000 +EOF + +test_expect_success 'deleting an empty file' ' + git reset --hard && + > empty && + git add empty && + git commit -m empty && + rm empty && + echo y | git add -p empty && + git diff --cached >diff && + test_cmp expected diff +' + test_done diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh index 531f5b795c..cab6ce2e97 100755 --- a/t/t4014-format-patch.sh +++ b/t/t4014-format-patch.sh @@ -455,6 +455,27 @@ test_expect_success 'format-patch respects -U' ' ' +cat > expect << EOF + +diff --git a/file b/file +index 40f36c6..2dc5c23 100644 +--- a/file ++++ b/file +@@ -14,3 +14,19 @@ C + D + E + F ++5 +EOF + +test_expect_success 'format-patch -p suppresses stat' ' + + git format-patch -p -2 && + sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output && + test_cmp expect output + +' + test_expect_success 'format-patch from a subdirectory (1)' ' filename=$( rm -rf sub && diff --git a/t/t4041-diff-submodule.sh b/t/t4041-diff-submodule.sh new file mode 100755 index 0000000000..5bb4fed3f5 --- /dev/null +++ b/t/t4041-diff-submodule.sh @@ -0,0 +1,260 @@ +#!/bin/sh +# +# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin +# + +test_description='Support for verbose submodule differences in git diff + +This test tries to verify the sanity of the --submodule option of git diff. +' + +. ./test-lib.sh + +add_file () { + sm=$1 + shift + owd=$(pwd) + cd "$sm" + for name; do + echo "$name" > "$name" && + git add "$name" && + test_tick && + git commit -m "Add $name" + done >/dev/null + git rev-parse --verify HEAD | cut -c1-7 + cd "$owd" +} +commit_file () { + test_tick && + git commit "$@" -m "Commit $*" >/dev/null +} + +test_create_repo sm1 && +add_file . foo >/dev/null + +head1=$(add_file sm1 foo1 foo2) + +test_expect_success 'added submodule' " + git add sm1 && + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 0000000...$head1 (new submodule) +EOF +" + +commit_file sm1 && +head2=$(add_file sm1 foo3) + +test_expect_success 'modified submodule(forward)' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head1..$head2: + > Add foo3 +EOF +" + +test_expect_success 'modified submodule(forward)' " + git diff --submodule=log >actual && + diff actual - <<-EOF +Submodule sm1 $head1..$head2: + > Add foo3 +EOF +" + +test_expect_success 'modified submodule(forward) --submodule' " + git diff --submodule >actual && + diff actual - <<-EOF +Submodule sm1 $head1..$head2: + > Add foo3 +EOF +" + +fullhead1=$(cd sm1; git rev-list --max-count=1 $head1) +fullhead2=$(cd sm1; git rev-list --max-count=1 $head2) +test_expect_success 'modified submodule(forward) --submodule=short' " + git diff --submodule=short >actual && + diff actual - <<-EOF +diff --git a/sm1 b/sm1 +index $head1..$head2 160000 +--- a/sm1 ++++ b/sm1 +@@ -1 +1 @@ +-Subproject commit $fullhead1 ++Subproject commit $fullhead2 +EOF +" + +commit_file sm1 && +cd sm1 && +git reset --hard HEAD~2 >/dev/null && +head3=$(git rev-parse --verify HEAD | cut -c1-7) && +cd .. + +test_expect_success 'modified submodule(backward)' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head2..$head3 (rewind): + < Add foo3 + < Add foo2 +EOF +" + +head4=$(add_file sm1 foo4 foo5) && +head4_full=$(GIT_DIR=sm1/.git git rev-parse --verify HEAD) +test_expect_success 'modified submodule(backward and forward)' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head2...$head4: + > Add foo5 + > Add foo4 + < Add foo3 + < Add foo2 +EOF +" + +commit_file sm1 && +mv sm1 sm1-bak && +echo sm1 >sm1 && +head5=$(git hash-object sm1 | cut -c1-7) && +git add sm1 && +rm -f sm1 && +mv sm1-bak sm1 + +test_expect_success 'typechanged submodule(submodule->blob), --cached' " + git diff --submodule=log --cached >actual && + diff actual - <<-EOF +Submodule sm1 41fbea9...0000000 (submodule deleted) +diff --git a/sm1 b/sm1 +new file mode 100644 +index 0000000..9da5fb8 +--- /dev/null ++++ b/sm1 +@@ -0,0 +1 @@ ++sm1 +EOF +" + +test_expect_success 'typechanged submodule(submodule->blob)' " + git diff --submodule=log >actual && + diff actual - <<-EOF +diff --git a/sm1 b/sm1 +deleted file mode 100644 +index 9da5fb8..0000000 +--- a/sm1 ++++ /dev/null +@@ -1 +0,0 @@ +-sm1 +Submodule sm1 0000000...$head4 (new submodule) +EOF +" + +rm -rf sm1 && +git checkout-index sm1 +test_expect_success 'typechanged submodule(submodule->blob)' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head4...0000000 (submodule deleted) +diff --git a/sm1 b/sm1 +new file mode 100644 +index 0000000..$head5 +--- /dev/null ++++ b/sm1 +@@ -0,0 +1 @@ ++sm1 +EOF +" + +rm -f sm1 && +test_create_repo sm1 && +head6=$(add_file sm1 foo6 foo7) +fullhead6=$(cd sm1; git rev-list --max-count=1 $head6) +test_expect_success 'nonexistent commit' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head4...$head6 (commits not present) +EOF +" + +commit_file +test_expect_success 'typechanged submodule(blob->submodule)' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +diff --git a/sm1 b/sm1 +deleted file mode 100644 +index $head5..0000000 +--- a/sm1 ++++ /dev/null +@@ -1 +0,0 @@ +-sm1 +Submodule sm1 0000000...$head6 (new submodule) +EOF +" + +commit_file sm1 && +rm -rf sm1 +test_expect_success 'deleted submodule' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head6...0000000 (submodule deleted) +EOF +" + +test_create_repo sm2 && +head7=$(add_file sm2 foo8 foo9) && +git add sm2 + +test_expect_success 'multiple submodules' " + git diff-index -p --submodule=log HEAD >actual && + diff actual - <<-EOF +Submodule sm1 $head6...0000000 (submodule deleted) +Submodule sm2 0000000...$head7 (new submodule) +EOF +" + +test_expect_success 'path filter' " + git diff-index -p --submodule=log HEAD sm2 >actual && + diff actual - <<-EOF +Submodule sm2 0000000...$head7 (new submodule) +EOF +" + +commit_file sm2 +test_expect_success 'given commit' " + git diff-index -p --submodule=log HEAD^ >actual && + diff actual - <<-EOF +Submodule sm1 $head6...0000000 (submodule deleted) +Submodule sm2 0000000...$head7 (new submodule) +EOF +" + +test_expect_success 'given commit --submodule' " + git diff-index -p --submodule HEAD^ >actual && + diff actual - <<-EOF +Submodule sm1 $head6...0000000 (submodule deleted) +Submodule sm2 0000000...$head7 (new submodule) +EOF +" + +fullhead7=$(cd sm2; git rev-list --max-count=1 $head7) + +test_expect_success 'given commit --submodule=short' " + git diff-index -p --submodule=short HEAD^ >actual && + diff actual - <<-EOF +diff --git a/sm1 b/sm1 +deleted file mode 160000 +index $head6..0000000 +--- a/sm1 ++++ /dev/null +@@ -1 +0,0 @@ +-Subproject commit $fullhead6 +diff --git a/sm2 b/sm2 +new file mode 160000 +index 0000000..$head7 +--- /dev/null ++++ b/sm2 +@@ -0,0 +1 @@ ++Subproject commit $fullhead7 +EOF +" + +test_done diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh index 59d1f6283b..7f61ab0e52 100755 --- a/t/t6006-rev-list-format.sh +++ b/t/t6006-rev-list-format.sh @@ -162,4 +162,22 @@ test_expect_success 'empty email' ' } ' +test_expect_success '"%h %gD: %gs" is same as git-reflog' ' + git reflog >expect && + git log -g --format="%h %gD: %gs" >actual && + test_cmp expect actual +' + +test_expect_success '"%h %gD: %gs" is same as git-reflog (with date)' ' + git reflog --date=raw >expect && + git log -g --format="%h %gD: %gs" --date=raw >actual && + test_cmp expect actual +' + +test_expect_success '%gd shortens ref name' ' + echo "master@{0}" >expect.gd-short && + git log -g -1 --format=%gd refs/heads/master >actual.gd-short && + test_cmp expect.gd-short actual.gd-short +' + test_done diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index f5a1b615f6..c050f94bc6 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -125,6 +125,20 @@ test_expect_success 'rename tag Q back to A' ' test_expect_success 'pack tag refs' 'git pack-refs' check_describe A-* HEAD +check_describe "A-*[0-9a-f]" --dirty + +test_expect_success 'set-up dirty work tree' ' + echo >>file +' + +check_describe "A-*[0-9a-f]-dirty" --dirty + +check_describe "A-*[0-9a-f].mod" --dirty=.mod + +test_expect_success 'describe --dirty HEAD' ' + test_must_fail git describe --dirty HEAD +' + test_expect_success 'set-up matching pattern tests' ' git tag -a -m test-annotated test-annotated && echo >>file && diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh index 56cd866019..fe94552296 100755 --- a/t/t7502-commit.sh +++ b/t/t7502-commit.sh @@ -258,4 +258,13 @@ test_expect_success 'Hand committing of a redundant merge removes dups' ' ' +test_expect_success 'A single-liner subject with a token plus colon is not a footer' ' + + git reset --hard && + git commit -s -m "hello: kitty" --allow-empty && + git cat-file commit HEAD | sed -e "1,/^$/d" >actual && + test $(wc -l <actual) = 3 + +' + test_done diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh index e5b210bc96..57f6d2bae7 100755 --- a/t/t7600-merge.sh +++ b/t/t7600-merge.sh @@ -243,6 +243,16 @@ test_expect_success 'merge c0 with c1' ' test_debug 'gitk --all' +test_expect_success 'merge c0 with c1 with --ff-only' ' + git reset --hard c0 && + git merge --ff-only c1 && + git merge --ff-only HEAD c0 c1 && + verify_merge file result.1 && + verify_head "$c1" +' + +test_debug 'gitk --all' + test_expect_success 'merge c1 with c2' ' git reset --hard c1 && test_tick && @@ -263,6 +273,14 @@ test_expect_success 'merge c1 with c2 and c3' ' test_debug 'gitk --all' +test_expect_success 'failing merges with --ff-only' ' + git reset --hard c1 && + test_tick && + test_must_fail git merge --ff-only c2 && + test_must_fail git merge --ff-only c3 && + test_must_fail git merge --ff-only c2 c3 +' + test_expect_success 'merge c0 with c1 (no-commit)' ' git reset --hard c0 && git merge --no-commit c1 && @@ -303,6 +321,17 @@ test_expect_success 'merge c0 with c1 (squash)' ' test_debug 'gitk --all' +test_expect_success 'merge c0 with c1 (squash, ff-only)' ' + git reset --hard c0 && + git merge --squash --ff-only c1 && + verify_merge file result.1 && + verify_head $c0 && + verify_no_mergehead && + verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message" +' + +test_debug 'gitk --all' + test_expect_success 'merge c1 with c2 (squash)' ' git reset --hard c1 && git merge --squash c2 && @@ -314,6 +343,13 @@ test_expect_success 'merge c1 with c2 (squash)' ' test_debug 'gitk --all' +test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' ' + git reset --hard c1 && + test_must_fail git merge --squash --ff-only c2 +' + +test_debug 'gitk --all' + test_expect_success 'merge c1 with c2 and c3 (squash)' ' git reset --hard c1 && git merge --squash c2 c3 && @@ -432,6 +468,11 @@ test_expect_success 'combining --squash and --no-ff is refused' ' test_must_fail git merge --no-ff --squash c1 ' +test_expect_success 'combining --ff-only and --no-ff is refused' ' + test_must_fail git merge --ff-only --no-ff c1 && + test_must_fail git merge --no-ff --ff-only c1 +' + test_expect_success 'merge c0 with c1 (ff overrides no-ff)' ' git reset --hard c0 && git config branch.master.mergeoptions "--no-ff" && diff --git a/t/t9150-svk-mergetickets.sh b/t/t9150-svk-mergetickets.sh index 8000c347b4..dd0c2bad24 100644..100755 --- a/t/t9150-svk-mergetickets.sh +++ b/t/t9150-svk-mergetickets.sh @@ -8,7 +8,8 @@ test_description='git-svn svk merge tickets' . ./lib-git-svn.sh test_expect_success 'load svk depot' " - svnadmin load -q '$rawsvnrepo' < '../t9150/svk-merge.dump' && + svnadmin load -q '$rawsvnrepo' \ + < '$TEST_DIRECTORY/t9150/svk-merge.dump' && git svn init --minimize-url -R svkmerge \ -T trunk -b branches '$svnrepo' && git svn fetch --all diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh index 7eb36e599c..9bee516358 100644..100755 --- a/t/t9151-svn-mergeinfo.sh +++ b/t/t9151-svn-mergeinfo.sh @@ -8,7 +8,8 @@ test_description='git-svn svn mergeinfo properties' . ./lib-git-svn.sh test_expect_success 'load svn dump' " - svnadmin load -q '$rawsvnrepo' < '../t9151/svn-mergeinfo.dump' && + svnadmin load -q '$rawsvnrepo' \ + < '$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' && git svn init --minimize-url -R svnmerge \ -T trunk -b branches '$svnrepo' && git svn fetch --all diff --git a/transport.c b/transport.c index 644a30a0b2..298dc46ec5 100644 --- a/transport.c +++ b/transport.c @@ -812,6 +812,9 @@ struct transport *transport_get(struct remote *remote, const char *url) { struct transport *ret = xcalloc(1, sizeof(*ret)); + if (!remote) + die("No remote provided to transport_get()"); + ret->remote = remote; ret->url = url; @@ -849,10 +852,10 @@ struct transport *transport_get(struct remote *remote, const char *url) data->thin = 1; data->conn = NULL; data->uploadpack = "git-upload-pack"; - if (remote && remote->uploadpack) + if (remote->uploadpack) data->uploadpack = remote->uploadpack; data->receivepack = "git-receive-pack"; - if (remote && remote->receivepack) + if (remote->receivepack) data->receivepack = remote->receivepack; } |