/GIT-BUILD-OPTIONS
/GIT-CFLAGS
/GIT-LDFLAGS
-/GIT-GUI-VARS
/GIT-PREFIX
+/GIT-PYTHON-VARS
/GIT-SCRIPT-DEFINES
/GIT-USER-AGENT
/GIT-VERSION-FILE
/git-remote-ftps
/git-remote-fd
/git-remote-ext
-/git-remote-testgit
+/git-remote-testpy
/git-remote-testsvn
/git-repack
/git-replace
/git-whatchanged
/git-write-tree
/git-core-*/?*
-/gitk-git/gitk-wish
/gitweb/GITWEB-BUILD-OPTIONS
/gitweb/gitweb.cgi
/gitweb/static/gitweb.js
--- /dev/null
+Git v1.8.2 Release Notes
+========================
+
+Backward compatibility notes
+----------------------------
+
+In the upcoming major release (tentatively called 1.8.2), we will
+change the behavior of the "git push" command.
+
+When "git push [$there]" does not say what to push, we have used the
+traditional "matching" semantics so far (all your branches were sent
+to the remote as long as there already are branches of the same name
+over there). We will use the "simple" semantics that pushes the
+current branch to the branch with the same name, only when the current
+branch is set to integrate with that remote branch. There is a user
+preference configuration variable "push.default" to change this.
+
+
+Updates since v1.8.1
+--------------------
+
+UI, Workflows & Features
+
+ * Initial ports to QNX and z/OS UNIX System Services have started.
+
+ * Output from the tests is coloured using "green is okay, yellow is
+ questionable, red is bad and blue is informative" scheme.
+
+ * In bare repositories, "git shortlog" and other commands now read
+ mailmap files from the tip of the history, to help running these
+ tools in server settings.
+
+ * Color specifiers, e.g. "%C(blue)Hello%C(reset)", used in the
+ "--format=" option of "git log" and friends can be disabled when
+ the output is not sent to a terminal by prefixing them with
+ "auto,", e.g. "%C(auto,blue)Hello%C(auto,reset)".
+
+ * Scripts can ask Git that wildcard patterns in pathspecs they give do
+ not have any significance, i.e. take them as literal strings.
+
+ * "git fetch --mirror" and fetch that uses other forms of refspec
+ with wildcard used to attempt to update a symbolic ref that match
+ the wildcard on the receiving end, which made little sense (the
+ real ref that is pointed at by the symbolic ref would be updated
+ anyway). Symbolic refs no longer are affected by such a fetch.
+
+ * "git push" now requires "-f" to update a tag, even if it is a
+ fast-forward, as tags are meant to be fixed points.
+
+ * "git submodule" started learning a new mode to integrate with the
+ tip of the remote branch (as opposed to integrating with the commit
+ recorded in the superproject's gitlink).
+
+
+Foreign Interface
+
+ * "git fast-export" has been updated for its use in the context of
+ the remote helper interface.
+
+
+Performance, Internal Implementation, etc.
+
+ * "git fsck" has been taught to be pickier about entries in tree
+ objects that should not be there, e.g. ".", ".git", and "..".
+
+ * Matching paths with common forms of pathspecs that contain wildcard
+ characters has been optimized further.
+
+ * The implementation of "imap-send" has been updated to reuse xml
+ quoting code from http-push codepath.
+
+
+Also contains minor documentation updates and code clean-ups.
+
+
+Fixes since v1.8.1
+------------------
+
+Unless otherwise noted, all the fixes since v1.8.1 in the maintenance
+track are contained in this release (see release notes to them for
+details).
+
+ * An element on GIT_CEILING_DIRECTORIES list that does not name the
+ real path to a directory (i.e. a symbolic link) could have caused
+ the GIT_DIR discovery logic to escape the ceiling.
+ (merge 059b379 mh/ceiling later to maint).
+
+ * t4014, t9502 and t0200 tests had various portability issues that
+ broke on OpenBSD.
+ (merge 27f6342 jc/maint-test-portability later to maint).
+
+ * t9020 and t3600 tests had various portability issues.
+ (merge 5a02966 jc/test-portability later to maint).
+
+ * t9200 runs "cvs init" on a directory that already exists, but a
+ platform can configure this fail for the current user (e.g. you
+ need to be in the cvsadmin group on NetBSD 6.0).
+ (merge 8666df0 jc/test-cvs-no-init-in-existing-dir later to maint).
+
+ * The behaviour visible to the end users was confusing, when they
+ attempt to kill a process spawned in the editor that was in turn
+ launched by Git with SIGINT (or SIGQUIT), as Git would catch that
+ signal and die. We ignore these signals now.
+ (merge 1250857 pf/editor-ignore-sigint later to maint).
+
+ * After failing to create a temporary file using mkstemp(), failing
+ pathname was not reported correctly on some platforms.
+ (merge f7be59b jc/mkstemp-more-careful-error-reporting later to maint).
+
+ * The attribute mechanism didn't allow limiting attributes to be
+ applied to only a single directory itself with "path/" like the
+ exclude mechanism does.
+ (merge 94bc671 ja/directory-attrs later to maint).
+
+ * The way "git svn" asked for password using SSH_ASKPASS and
+ GIT_ASKPASS was not in line with the rest of the system.
+ (merge e9263e4 ss/svn-prompt later to maint).
+
+ * The --graph code fell into infinite loop when asked to do what the
+ code did not expect.
+ (merge 656197a mk/maint-graph-infinity-loop later to maint).
+
+ * http transport was wrong to ask for the username when the
+ authentication is done by certificate identity.
+ (merge 75e9a40 rb/http-cert-cred-no-username-prompt later to maint).
+
+ * "git pack-refs" that ran in parallel to another process that
+ created new refs had a nasty race.
+ (merge b3f1280 jk/repack-ref-racefix later to maint).
+
+ * After "git add -N" and then writing a tree object out of the
+ index, the cache-tree data structure got corrupted.
+ (merge eec3e7e nd/invalidate-i-t-a-cache-tree later to maint).
+
+ * "gitweb", when sorting by age to show repositories with new
+ activities first, used to sort repositories with absolutely
+ nothing in it early, which was not very useful.
+ (merge 28dae18 md/gitweb-sort-by-age later to maint).
+
+ * When a line to be wrapped has a solid run of non space characters
+ whose length exactly is the wrap width, "git shortlog -w" failed
+ to add a newline after such a line.
+ (merge e0db176 sp/shortlog-missing-lf later to maint).
+
+ * Some shells do not behave correctly when IFS is unset; work it
+ around by explicitly setting it to the default value.
+ (merge 393050c jc/maint-fbsd-sh-ifs-workaround later to maint).
can tell Git that you do not need help by setting these to 'false':
+
--
- pushNonFastForward::
+ pushUpdateRejected::
Set this variable to 'false' if you want to disable
- 'pushNonFFCurrent', 'pushNonFFDefault', and
- 'pushNonFFMatching' simultaneously.
+ 'pushNonFFCurrent', 'pushNonFFDefault',
+ 'pushNonFFMatching', and 'pushAlreadyExists'
+ simultaneously.
pushNonFFCurrent::
Advice shown when linkgit:git-push[1] fails due to a
non-fast-forward update to the current branch.
'matching refs' explicitly (i.e. you used ':', or
specified a refspec that isn't your current branch) and
it resulted in a non-fast-forward error.
+ pushAlreadyExists::
+ Shown when linkgit:git-push[1] rejects an update that
+ does not qualify for fast-forwarding (e.g., a tag.)
statusHints::
Show directions on how to proceed from the current
state in the output of linkgit:git-status[1], in
subdirectory, or somewhere outside of the repository itself.
See linkgit:git-shortlog[1] and linkgit:git-blame[1].
+mailmap.blob::
+ Like `mailmap.file`, but consider the value as a reference to a
+ blob in the repository. If both `mailmap.file` and
+ `mailmap.blob` are given, both are parsed, with entries from
+ `mailmap.file` taking precedence. In a bare repository, this
+ defaults to `HEAD:.mailmap`. In a non-bare repository, it
+ defaults to empty.
+
man.viewer::
Specify the programs that may be used to display help in the
'man' format. See linkgit:git-help[1].
URL and other values found in the `.gitmodules` file. See
linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
+submodule.<name>.branch::
+ The remote branch name for a submodule, used by `git submodule
+ update --remote`. Set this option to override the value found in
+ the `.gitmodules` file. See linkgit:git-submodule[1] and
+ linkgit:gitmodules[5] for details.
+
submodule.<name>.fetchRecurseSubmodules::
This option can be used to control recursive fetching of this
submodule. It can be overridden by using the --[no-]recurse-submodules
machineA$ git tag -f lastR2bundle master
----------------
-Then you transfer file.bundle to the target machine B. If you are creating
-the repository on machine B, then you can clone from the bundle as if it
-were a remote repository instead of creating an empty repository and then
-pulling or fetching objects from the bundle:
+Then you transfer file.bundle to the target machine B. Because this
+bundle does not require any existing object to be extracted, you can
+create a new repository on machine B by cloning from it:
----------------
-machineB$ git clone /home/me/tmp/file.bundle R2
+machineB$ git clone -b master /home/me/tmp/file.bundle R2
----------------
This will define a remote called "origin" in the resulting repository that
Using the "--global" option forces this to ~/.gitconfig. Using the
"--system" option forces this to $(prefix)/etc/gitconfig.
+GIT_CONFIG_NOSYSTEM::
+ Whether to skip reading settings from the system-wide
+ $(prefix)/etc/gitconfig file. See linkgit:git[1] for details.
+
See also <<FILES>>.
See ``Date Formats'' below for details about which formats
are supported, and their syntax.
--- done::
- Terminate with error if there is no 'done' command at the
- end of the stream.
-
--force::
Force updating modified existing branches, even if doing
so would cause commits to be lost (as the new commit does
output.
--done::
- Require a `done` command at the end of the stream.
+ Terminate with error if there is no `done` command at the
+ end of the stream.
This option might be useful for detecting errors that
cause the frontend to terminate before it has started to
write a stream.
`git log -p` output would be shown without a diff attached.
The default is `true`.
-mailmap.file::
+mailmap.*::
See linkgit:git-shortlog[1].
notes.displayRef::
updated.
+
The object referenced by <src> is used to update the <dst> reference
-on the remote side, but by default this is only allowed if the
-update can fast-forward <dst>. By having the optional leading `+`,
-you can tell git to update the <dst> ref even when the update is not a
-fast-forward. This does *not* attempt to merge <src> into <dst>. See
+on the remote side. By default this is only allowed if <dst> is not
+a tag (annotated or lightweight), and then only if it can fast-forward
+<dst>. By having the optional leading `+`, you can tell git to update
+the <dst> ref even if it is not allowed by default (e.g., it is not a
+fast-forward.) This does *not* attempt to merge <src> into <dst>. See
EXAMPLES below for details.
+
`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
show remote-helper authors one possible implementation.
The best way to learn more is to read the comments and source code in
-'git-remote-testgit.py'.
+'git-remote-testgit'.
SEE ALSO
--------
[--reference <repository>] [--] <repository> [<path>]
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
-'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
+'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch] [--rebase]
[--reference <repository>] [--merge] [--recursive] [--] [<path>...]
'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
[commit] [--] [<path>...]
-b::
--branch::
Branch of repository to add as submodule.
+ The name of the branch is recorded as `submodule.<path>.branch` in
+ `.gitmodules` for `update --remote`.
-f::
--force::
(the default). This limit only applies to modified submodules. The
size is always limited to 1 for added/deleted/typechanged submodules.
+--remote::
+ This option is only valid for the update command. Instead of using
+ the superproject's recorded SHA-1 to update the submodule, use the
+ status of the submodule's remote tracking branch. The remote used
+ is branch's remote (`branch.<name>.remote`), defaulting to `origin`.
+ The remote branch used defaults to `master`, but the branch name may
+ be overridden by setting the `submodule.<name>.branch` option in
+ either `.gitmodules` or `.git/config` (with `.git/config` taking
+ precedence).
++
+This works for any of the supported update procedures (`--checkout`,
+`--rebase`, etc.). The only change is the source of the target SHA-1.
+For example, `submodule update --remote --merge` will merge upstream
+submodule changes into the submodules, while `submodule update
+--merge` will merge superproject gitlink changes into the submodules.
++
+In order to ensure a current tracking branch state, `update --remote`
+fetches the submodule's remote repository before calculating the
+SHA-1. If you don't want to fetch, you should use `submodule update
+--remote --no-fetch`.
+
-N::
--no-fetch::
This option is only valid for the update command.
Do not use replacement refs to replace git objects. See
linkgit:git-replace[1] for more information.
+--literal-pathspecs::
+ Treat pathspecs literally, rather than as glob patterns. This is
+ equivalent to setting the `GIT_LITERAL_PATHSPECS` environment
+ variable to `1`.
+
GIT COMMANDS
------------
and read the password from its STDOUT. See also the 'core.askpass'
option in linkgit:git-config[1].
+'GIT_CONFIG_NOSYSTEM'::
+ Whether to skip reading settings from the system-wide
+ `$(prefix)/etc/gitconfig` file. This environment variable can
+ be used along with `$HOME` and `$XDG_CONFIG_HOME` to create a
+ predictable environment for a picky script, or you can set it
+ temporarily to avoid using a buggy `/etc/gitconfig` file while
+ waiting for someone with sufficient permissions to fix it.
+
'GIT_FLUSH'::
If this environment variable is set to "1", then commands such
as 'git blame' (in incremental mode), 'git rev-list', 'git log',
as a file path and will try to write the trace messages
into it.
+GIT_LITERAL_PATHSPECS::
+ Setting this variable to `1` will cause git to treat all
+ pathspecs literally, rather than as glob patterns. For example,
+ running `GIT_LITERAL_PATHSPECS=1 git log -- '*.c'` will search
+ for commits that touch the path `*.c`, not any paths that the
+ glob `*.c` matches. You might want this if you are feeding
+ literal paths to git (e.g., paths previously given to you by
+ `git ls-tree`, `--raw` diff output, etc).
+
+
Discussion[[Discussion]]
------------------------
This config option is overridden if 'git submodule update' is given
the '--merge', '--rebase' or '--checkout' options.
+submodule.<name>.branch::
+ A remote branch name for tracking updates in the upstream submodule.
+ If the option is not specified, it defaults to 'master'. See the
+ `--remote` documentation in linkgit:git-submodule[1] for details.
+
submodule.<name>.fetchRecurseSubmodules::
This option can be used to control recursive fetching of this
submodule. If this option is also present in the submodules entry in
If the file `.mailmap` exists at the toplevel of the repository, or at
-the location pointed to by the mailmap.file configuration option, it
+the location pointed to by the mailmap.file or mailmap.blob
+configuration options, it
is used to map author and committer names and email addresses to
canonical real names and email addresses.
- '%Cgreen': switch color to green
- '%Cblue': switch color to blue
- '%Creset': reset color
-- '%C(...)': color specification, as described in color.branch.* config option
+- '%C(...)': color specification, as described in color.branch.* config option;
+ adding `auto,` at the beginning will emit color only when colors are
+ enabled for log output (by `color.diff`, `color.ui`, or `--color`, and
+ respecting the `auto` settings of the former if we are going to a
+ terminal)
- '%m': left, right or boundary mark
- '%n': newline
- '%%': a raw '%'
+++ /dev/null
-gittutorial(7)
-==============
-
-NOME
-----
-gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova)
-
-SINOPSE
---------
-git *
-
-DESCRIÇÃO
------------
-
-Este tutorial explica como importar um novo projeto para o git,
-adicionar mudanças a ele, e compartilhar mudanças com outros
-desenvolvedores.
-
-Se, ao invés disso, você está interessado primariamente em usar git para
-obter um projeto, por exemplo, para testar a última versão, você pode
-preferir começar com os primeiros dois capÃtulos de
-link:user-manual.html[O Manual do Usuário Git].
-
-Primeiro, note que você pode obter documentação para um comando como
-`git log --graph` com:
-
-------------------------------------------------
-$ man git-log
-------------------------------------------------
-
-ou:
-
-------------------------------------------------
-$ git help log
-------------------------------------------------
-
-Com a última forma, você pode usar o visualizador de manual de sua
-escolha; veja linkgit:git-help[1] para maior informação.
-
-É uma boa idéia informar ao git seu nome e endereço público de email
-antes de fazer qualquer operação. A maneira mais fácil de fazê-lo é:
-
-------------------------------------------------
-$ git config --global user.name "Seu Nome Vem Aqui"
-$ git config --global user.email voce@seudominio.exemplo.com
-------------------------------------------------
-
-
-Importando um novo projeto
------------------------
-
-Assuma que você tem um tarball project.tar.gz com seu trabalho inicial.
-Você pode colocá-lo sob controle de revisão git da seguinte forma:
-
-------------------------------------------------
-$ tar xzf project.tar.gz
-$ cd project
-$ git init
-------------------------------------------------
-
-Git irá responder
-
-------------------------------------------------
-Initialized empty Git repository in .git/
-------------------------------------------------
-
-Agora que você iniciou seu diretório de trabalho, você deve ter notado que um
-novo diretório foi criado com o nome de ".git".
-
-A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os
-arquivos sob o diretório atual (note o '.'), com 'git-add':
-
-------------------------------------------------
-$ git add .
-------------------------------------------------
-
-Este instantâneo está agora armazenado em uma área temporária que o git
-chama de "index" ou Ãndice. Você pode armazenar permanentemente o
-conteúdo do Ãndice no repositório com 'git-commit':
-
-------------------------------------------------
-$ git commit
-------------------------------------------------
-
-Isto vai te pedir por uma mensagem de commit. Você agora gravou sua
-primeira versão de seu projeto no git.
-
-Fazendo mudanças
---------------
-
-Modifique alguns arquivos, e, então, adicione seu conteúdo atualizado ao
-Ãndice:
-
-------------------------------------------------
-$ git add file1 file2 file3
-------------------------------------------------
-
-Você está agora pronto para fazer o commit. Você pode ver o que está
-para ser gravado usando 'git-diff' com a opção --cached:
-
-------------------------------------------------
-$ git diff --cached
-------------------------------------------------
-
-(Sem --cached, o comando 'git-diff' irá te mostrar quaisquer mudanças
-que você tenha feito mas ainda não adicionou ao Ãndice.) Você também
-pode obter um breve sumário da situação com 'git-status':
-
-------------------------------------------------
-$ git status
-# On branch master
-# Changes to be committed:
-# (use "git reset HEAD <file>..." to unstage)
-#
-# modified: file1
-# modified: file2
-# modified: file3
-#
-------------------------------------------------
-
-Se você precisar fazer qualquer outro ajuste, faça-o agora, e, então,
-adicione qualquer conteúdo modificado ao Ãndice. Finalmente, grave suas
-mudanças com:
-
-------------------------------------------------
-$ git commit
-------------------------------------------------
-
-Ao executar esse comando, ele irá te pedir uma mensagem descrevendo a mudança,
-e, então, irá gravar a nova versão do projeto.
-
-Alternativamente, ao invés de executar 'git-add' antes, você pode usar
-
-------------------------------------------------
-$ git commit -a
-------------------------------------------------
-
-o que irá automaticamente notar quaisquer arquivos modificados (mas não
-novos), adicioná-los ao Ãndices, e gravar, tudo em um único passo.
-
-Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa
-idéia começar a mensagem com uma simples e curta (menos de 50
-caracteres) linha sumarizando a mudança, seguida de uma linha em branco
-e, então, uma descrição mais detalhada. Ferramentas que transformam
-commits em email, por exemplo, usam a primeira linha no campo de
-cabeçalho "Subject:" e o resto no corpo.
-
-Git rastreia conteúdo, não arquivos
-----------------------------
-
-Muitos sistemas de controle de revisão provêem um comando `add` que diz
-ao sistema para começar a rastrear mudanças em um novo arquivo. O
-comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é
-usado tanto para arquivos novos e arquivos recentemente modificados, e
-em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena
-o conteúdo no Ãndice, pronto para inclusão do próximo commit.
-
-Visualizando a história do projeto
------------------------
-
-Em qualquer ponto você pode visualizar a história das suas mudanças
-usando
-
-------------------------------------------------
-$ git log
-------------------------------------------------
-
-Se você também quiser ver a diferença completa a cada passo, use
-
-------------------------------------------------
-$ git log -p
-------------------------------------------------
-
-Geralmente, uma visão geral da mudança é útil para ter a sensação de
-cada passo
-
-------------------------------------------------
-$ git log --stat --summary
-------------------------------------------------
-
-Gerenciando "branches"/ramos
------------------
-
-Um simples repositório git pode manter múltiplos ramos de
-desenvolvimento. Para criar um novo ramo chamado "experimental", use
-
-------------------------------------------------
-$ git branch experimental
-------------------------------------------------
-
-Se você executar agora
-
-------------------------------------------------
-$ git branch
-------------------------------------------------
-
-você vai obter uma lista de todos os ramos existentes:
-
-------------------------------------------------
- experimental
-* master
-------------------------------------------------
-
-O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o
-ramo padrão que foi criado pra você automaticamente. O asterisco marca
-o ramo em que você está atualmente; digite
-
-------------------------------------------------
-$ git checkout experimental
-------------------------------------------------
-
-para mudar para o ramo experimental. Agora edite um arquivo, grave a
-mudança, e mude de volta para o ramo master:
-
-------------------------------------------------
-(edita arquivo)
-$ git commit -a
-$ git checkout master
-------------------------------------------------
-
-Verifique que a mudança que você fez não está mais visÃvel, já que ela
-foi feita no ramo experimental e você está de volta ao ramo master.
-
-Você pode fazer uma mudança diferente no ramo master:
-
-------------------------------------------------
-(edit file)
-$ git commit -a
-------------------------------------------------
-
-neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em
-cada um. Para unificar as mudanças feitas no experimental para o
-master, execute
-
-------------------------------------------------
-$ git merge experimental
-------------------------------------------------
-
-Se as mudanças não conflitarem, estará pronto. Se existirem conflitos,
-marcadores serão deixados nos arquivos problemáticos exibindo o
-conflito;
-
-------------------------------------------------
-$ git diff
-------------------------------------------------
-
-vai exibir isto. Após você editar os arquivos para resolver os
-conflitos,
-
-------------------------------------------------
-$ git commit -a
-------------------------------------------------
-
-irá gravar o resultado da unificação. Finalmente,
-
-------------------------------------------------
-$ gitk
-------------------------------------------------
-
-vai mostrar uma bela representação gráfica da história resultante.
-
-Neste ponto você pode remover seu ramo experimental com
-
-------------------------------------------------
-$ git branch -d experimental
-------------------------------------------------
-
-Este comando garante que as mudanças no ramo experimental já estão no
-ramo atual.
-
-Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode
-sempre remover o ramo com
-
--------------------------------------
-$ git branch -D ideia-louca
--------------------------------------
-
-Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar
-alguma coisa.
-
-Usando git para colaboração
----------------------------
-
-Suponha que Alice começou um novo projeto com um repositório git em
-/home/alice/project, e que Bob, que tem um diretório home na mesma
-máquina, quer contribuir.
-
-Bob começa com:
-
-------------------------------------------------
-bob$ git clone /home/alice/project myrepo
-------------------------------------------------
-
-Isso cria um novo diretório "myrepo" contendo um clone do repositório de
-Alice. O clone está no mesmo pé que o projeto original, possuindo sua
-própria cópia da história do projeto original.
-
-Bob então faz algumas mudanças e as grava:
-
-------------------------------------------------
-(editar arquivos)
-bob$ git commit -a
-(repetir conforme necessário)
-------------------------------------------------
-
-Quanto está pronto, ele diz a Alice para puxar as mudanças do
-repositório em /home/bob/myrepo. Ela o faz com:
-
-------------------------------------------------
-alice$ cd /home/alice/project
-alice$ git pull /home/bob/myrepo master
-------------------------------------------------
-
-Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice.
-Se Alice fez suas próprias mudanças no intervalo, ela, então, pode
-precisar corrigir manualmente quaisquer conflitos. (Note que o argumento
-"master" no comando acima é, de fato, desnecessário, já que é o padrão.)
-
-O comando "pull" executa, então, duas operações: ele obtém mudanças de
-um ramo remoto, e, então, as unifica no ramo atual.
-
-Note que, em geral, Alice gostaria que suas mudanças locais fossem
-gravadas antes de iniciar este "pull". Se o trabalho de Bob conflita
-com o que Alice fez desde que suas histórias se ramificaram, Alice irá
-usar seu diretório de trabalho e o Ãndice para resolver conflitos, e
-mudanças locais existentes irão interferir com o processo de resolução
-de conflitos (git ainda irá realizar a obtenção mas irá se recusar a
-unificar --- Alice terá que se livrar de suas mudanças locais de alguma
-forma e puxar de novo quando isso acontecer).
-
-Alice pode espiar o que Bob fez sem unificar primeiro, usando o comando
-"fetch"; isto permite Alice inspecionar o que Bob fez, usando um sÃmbolo
-especial "FETCH_HEAD", com o fim de determinar se ele tem alguma coisa
-que vale puxar, assim:
-
-------------------------------------------------
-alice$ git fetch /home/bob/myrepo master
-alice$ git log -p HEAD..FETCH_HEAD
-------------------------------------------------
-
-Esta operação é segura mesmo se Alice tem mudanças locais não gravadas.
-A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é
-alcançável de FETCH_HEAD mas exclua tudo o que é alcançável de HEAD.
-Alice já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob
-tem em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando.
-
-Se Alice quer visualizar o que Bob fez desde que suas histórias se
-ramificaram, ela pode disparar o seguinte comando:
-
-------------------------------------------------
-$ gitk HEAD..FETCH_HEAD
-------------------------------------------------
-
-Isto usa a mesma notação de intervalo que vimos antes com 'git log'.
-
-Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela
-pode usar a forma com três pontos ao invés da forma com dois pontos:
-
-------------------------------------------------
-$ gitk HEAD...FETCH_HEAD
-------------------------------------------------
-
-Isto significa "mostre tudo que é alcançável de qualquer um deles, mas
-exclua tudo que é alcançável a partir de ambos".
-
-Por favor, note que essas notações de intervalo podem ser usadas tanto
-com gitk quanto com "git log".
-
-Após inspecionar o que Bob fez, se não há nada urgente, Alice pode
-decidir continuar trabalhando sem puxar de Bob. Se a história de Bob
-tem alguma coisa que Alice precisa imediatamente, Alice pode optar por
-separar seu trabalho em progresso primeiro, fazer um "pull", e, então,
-finalmente, retomar seu trabalho em progresso em cima da história
-resultante.
-
-Quando você está trabalhando em um pequeno grupo unido, não é incomum
-interagir com o mesmo repositório várias e várias vezes. Definindo um
-repositório remoto antes de tudo, você pode fazê-lo mais facilmente:
-
-------------------------------------------------
-alice$ git remote add bob /home/bob/myrepo
-------------------------------------------------
-
-Com isso, Alice pode executar a primeira parte da operação "pull" usando
-o comando 'git-fetch' sem unificar suas mudanças com seu próprio ramo,
-usando:
-
--------------------------------------
-alice$ git fetch bob
--------------------------------------
-
-Diferente da forma longa, quando Alice obteve de Bob usando um
-repositório remoto antes definido com 'git-remote', o que foi obtido é
-armazenado em um ramo remoto, neste caso `bob/master`. Então, após isso:
-
--------------------------------------
-alice$ git log -p master..bob/master
--------------------------------------
-
-mostra uma lista de todas as mudanças que Bob fez desde que ramificou do
-ramo master de Alice.
-
-Após examinar essas mudanças, Alice pode unificá-las em seu ramo master:
-
--------------------------------------
-alice$ git merge bob/master
--------------------------------------
-
-Esse `merge` pode também ser feito puxando de seu próprio ramo remoto,
-assim:
-
--------------------------------------
-alice$ git pull . remotes/bob/master
--------------------------------------
-
-Note que 'git pull' sempre unifica ao ramo atual, independente do que
-mais foi passado na linha de comando.
-
-Depois, Bob pode atualizar seu repositório com as últimas mudanças de
-Alice, usando
-
--------------------------------------
-bob$ git pull
--------------------------------------
-
-Note que ele não precisa dar o caminho do repositório de Alice; quando
-Bob clonou seu repositório, o git armazenou a localização de seu
-repositório na configuração do mesmo, e essa localização é usada
-para puxar:
-
--------------------------------------
-bob$ git config --get remote.origin.url
-/home/alice/project
--------------------------------------
-
-(A configuração completa criada por 'git-clone' é visÃvel usando `git
-config -l`, e a página de manual linkgit:git-config[1] explica o
-significado de cada opção.)
-
-Git também mantém uma cópia limpa do ramo master de Alice sob o nome
-"origin/master":
-
--------------------------------------
-bob$ git branch -r
- origin/master
--------------------------------------
-
-Se Bob decidir depois em trabalhar em um host diferente, ele ainda pode
-executar clones e puxar usando o protocolo ssh:
-
--------------------------------------
-bob$ git clone alice.org:/home/alice/project myrepo
--------------------------------------
-
-Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou
-http; veja linkgit:git-pull[1] para detalhes.
-
-Git pode também ser usado em um modo parecido com CVS, com um
-repositório central para o qual vários usuários empurram modificações;
-veja linkgit:git-push[1] e linkgit:gitcvs-migration[7].
-
-Explorando história
------------------
-
-A história no git é representada como uma série de commits
-interrelacionados. Nós já vimos que o comando 'git-log' pode listar
-esses commits. Note que a primeira linha de cada entrada no log também
-dá o nome para o commit:
-
--------------------------------------
-$ git log
-commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
-Author: Junio C Hamano <junkio@cox.net>
-Date: Tue May 16 17:18:22 2006 -0700
-
- merge-base: Clarify the comments on post processing.
--------------------------------------
-
-Nós podemos dar este nome ao 'git-show' para ver os detalhes sobre este
-commit.
-
--------------------------------------
-$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
--------------------------------------
-
-Mas há outras formas de se referir aos commits. Você pode usar qualquer
-parte inicial do nome que seja longo o bastante para identificar
-unicamente o commit:
-
--------------------------------------
-$ git show c82a22c39c # os primeiros caracteres do nome são o bastante
- # usualmente
-$ git show HEAD # a ponta do ramo atual
-$ git show experimental # a ponta do ramo "experimental"
--------------------------------------
-
-Todo commit normalmente tem um commit "pai" que aponta para o estado
-anterior do projeto:
-
--------------------------------------
-$ git show HEAD^ # para ver o pai de HEAD
-$ git show HEAD^^ # para ver o avô de HEAD
-$ git show HEAD~4 # para ver o trisavô de HEAD
--------------------------------------
-
-Note que commits de unificação podem ter mais de um pai:
-
--------------------------------------
-$ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^)
-$ git show HEAD^2 # mostra o segundo pai de HEAD
--------------------------------------
-
-Você também pode dar aos commits nomes à sua escolha; após executar
-
--------------------------------------
-$ git tag v2.5 1b2e1d63ff
--------------------------------------
-
-você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende
-compartilhar esse nome com outras pessoas (por exemplo, para identificar
-uma versão de lançamento), você deveria criar um objeto "tag", e talvez
-assiná-lo; veja linkgit:git-tag[1] para detalhes.
-
-Qualquer comando git que precise conhecer um commit pode receber
-quaisquer desses nomes. Por exemplo:
-
--------------------------------------
-$ git diff v2.5 HEAD # compara o HEAD atual com v2.5
-$ git branch stable v2.5 # inicia um novo ramo chamado "stable" baseado
- # em v2.5
-$ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de
- # trabalho a seu estado em HEAD^
--------------------------------------
-
-Seja cuidadoso com o último comando: além de perder quaisquer mudanças
-em seu diretório de trabalho, ele também remove todos os commits
-posteriores desse ramo. Se esse ramo é o único ramo contendo esses
-commits, eles serão perdidos. Também, não use 'git-reset' num ramo
-publicamente visÃvel de onde outros desenvolvedores puxam, já que vai
-forçar unificações desnecessárias para que outros desenvolvedores limpem
-a história. Se você precisa desfazer mudanças que você empurrou, use
-'git-revert' no lugar.
-
-O comando 'git-grep' pode buscar strings em qualquer versão de seu
-projeto, então
-
--------------------------------------
-$ git grep "hello" v2.5
--------------------------------------
-
-procura por todas as ocorrências de "hello" em v2.5.
-
-Se você deixar de fora o nome do commit, 'git-grep' irá procurar
-quaisquer dos arquivos que ele gerencia no diretório corrente. Então
-
--------------------------------------
-$ git grep "hello"
--------------------------------------
-
-é uma forma rápida de buscar somente os arquivos que são rastreados pelo
-git.
-
-Muitos comandos git também recebem um conjunto de commits, o que pode
-ser especificado de várias formas. Aqui estão alguns exemplos com 'git-log':
-
--------------------------------------
-$ git log v2.5..v2.6 # commits entre v2.5 e v2.6
-$ git log v2.5.. # commits desde v2.5
-$ git log --since="2 weeks ago" # commits das últimas 2 semanas
-$ git log v2.5.. Makefile # commits desde v2.5 que modificam
- # Makefile
--------------------------------------
-
-Você também pode dar ao 'git-log' um "intervalo" de commits onde o
-primeiro não é necessariamente um ancestral do segundo; por exemplo, se
-as pontas dos ramos "stable" e "master" divergiram de um commit
-comum algum tempo atrás, então
-
--------------------------------------
-$ git log stable..master
--------------------------------------
-
-irá listar os commits feitos no ramo "master" mas não no ramo
-"stable", enquanto
-
--------------------------------------
-$ git log master..stable
--------------------------------------
-
-irá listar a lista de commits feitos no ramo "stable" mas não no ramo
-"master".
-
-O comando 'git-log' tem uma fraqueza: ele precisa mostrar os commits em
-uma lista. Quando a história tem linhas de desenvolvimento que
-divergiram e então foram unificadas novamente, a ordem em que 'git-log'
-apresenta essas mudanças é irrelevante.
-
-A maioria dos projetos com múltiplos contribuidores (como o kernel
-Linux, ou o próprio git) tem unificações frequentes, e 'gitk' faz um
-trabalho melhor de visualizar sua história. Por exemplo,
-
--------------------------------------
-$ gitk --since="2 weeks ago" drivers/
--------------------------------------
-
-permite a você navegar em quaisquer commits desde as últimas duas semanas
-de commits que modificaram arquivos sob o diretório "drivers". (Nota:
-você pode ajustar as fontes do gitk segurando a tecla control enquanto
-pressiona "-" ou "+".)
-
-Finalmente, a maioria dos comandos que recebem nomes de arquivo permitirão
-também, opcionalmente, preceder qualquer nome de arquivo por um
-commit, para especificar uma versão particular do arquivo:
-
--------------------------------------
-$ git diff v2.5:Makefile HEAD:Makefile.in
--------------------------------------
-
-Você pode usar 'git-show' para ver tal arquivo:
-
--------------------------------------
-$ git show v2.5:Makefile
--------------------------------------
-
-Próximos passos
-----------
-
-Este tutorial deve ser o bastante para operar controle de revisão
-distribuÃdo básico para seus projetos. No entanto, para entender
-plenamente a profundidade e o poder do git você precisa entender duas
-idéias simples nas quais ele se baseia:
-
- * A base de objetos é um sistema bem elegante usado para armazenar a
- história de seu projeto--arquivos, diretórios, e commits.
-
- * O arquivo de Ãndice é um cache do estado de uma árvore de diretório,
- usado para criar commits, restaurar diretórios de trabalho, e
- armazenar as várias árvores envolvidas em uma unificação.
-
-A parte dois deste tutorial explica a base de objetos, o arquivo de
-Ãndice, e algumas outras coisinhas que você vai precisar pra usar o
-máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7].
-
-Se você não quiser continuar com o tutorial agora nesse momento, algumas
-outras digressões que podem ser interessantes neste ponto são:
-
- * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem
- séries de commits em patches para email, e vice-versa, úteis para
- projetos como o kernel Linux que dependem fortemente de patches
- enviados por email.
-
- * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma
- forma de rastrear um bug é procurando pela história para encontrar o
- commit culpado. Git bisect pode ajudar a executar uma busca binária
- por esse commit. Ele é inteligente o bastante para executar uma
- busca próxima da ótima mesmo no caso de uma história complexa
- não-linear com muitos ramos unificados.
-
- * link:everyday.html[GIT diariamente com 20 e tantos comandos]
-
- * linkgit:gitcvs-migration[7]: Git para usuários de CVS.
-
-VEJA TAMBÉM
---------
-linkgit:gittutorial-2[7],
-linkgit:gitcvs-migration[7],
-linkgit:gitcore-tutorial[7],
-linkgit:gitglossary[7],
-linkgit:git-help[1],
-link:everyday.html[git diariamente],
-link:user-manual.html[O Manual do Usuário git]
-
-GIT
----
-Parte da suite linkgit:git[1].
They can all be called with a NULL graph argument, in which case no graph
output will be printed.
-* `graph_show_commit()` calls `graph_next_line()` until it returns non-zero.
- This prints all graph lines up to, and including, the line containing this
- commit. Output is printed to stdout. The last line printed does not contain
- a terminating newline. This should not be called if the commit line has
- already been printed, or it will loop forever.
+* `graph_show_commit()` calls `graph_next_line()` and
+ `graph_is_commit_finished()` until one of them return non-zero. This prints
+ all graph lines up to, and including, the line containing this commit.
+ Output is printed to stdout. The last line printed does not contain a
+ terminating newline.
* `graph_show_oneline()` calls `graph_next_line()` and prints the result to
stdout. The line printed does not contain a terminating newline.
call free() on the util members of any items that have to be
deleted. Preserve the order of the items that are retained.
-`string_list_longest_prefix`::
-
- Return the longest string within a string_list that is a
- prefix (in the sense of prefixcmp()) of the specified string,
- or NULL if no such prefix exists. This function does not
- require the string_list to be sorted (it does a linear
- search).
-
`print_string_list`::
Dump a string_list to stdout, useful mainly for debugging purposes. It
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.1
+DEF_VER=v1.8.1.GIT
LF='
'
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
# d_type in struct dirent (Cygwin 1.5, fixed in Cygwin 1.7).
#
+# Define HAVE_STRINGS_H if you have strings.h and need it for strcasecmp.
+#
# Define NO_STRCASESTR if you don't have strcasestr.
#
# Define NO_MEMMEM if you don't have memmem.
#
+# Define NO_GETPAGESIZE if you don't have getpagesize.
+#
# Define NO_STRLCPY if you don't have strlcpy.
#
# Define NO_STRTOUMAX if you don't have both strtoimax and strtoumax in the
# Define NO_POLL if you do not have or don't want to use poll().
# This also implies NO_SYS_POLL_H.
#
+# Define NEEDS_SYS_PARAM_H if you need to include sys/param.h to compile,
+# *PLEASE* REPORT to git@vger.kernel.org if your platform needs this;
+# we want to know more about the issue.
+#
# Define NO_PTHREADS if you do not have or do not want to use Pthreads.
#
# Define NO_PREAD if you have a problem with pread() system call (e.g.
SCRIPT_PERL += git-send-email.perl
SCRIPT_PERL += git-svn.perl
-SCRIPT_PYTHON += git-remote-testgit.py
+SCRIPT_PYTHON += git-remote-testpy.py
SCRIPT_PYTHON += git-p4.py
SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
# Added manually, see above.
NEEDS_SSL_WITH_CURL = YesPlease
HAVE_LIBCHARSET_H = YesPlease
+ HAVE_STRINGS_H = YesPlease
NEEDS_LIBICONV = YesPlease
NEEDS_LIBINTL_BEFORE_LIBICONV = YesPlease
NO_SYS_SELECT_H = UnfortunatelyYes
NO_CURL = YesPlease
endif
endif
+ifeq ($(uname_S),QNX)
+ COMPAT_CFLAGS += -DSA_RESTART=0
+ HAVE_STRINGS_H = YesPlease
+ NEEDS_SOCKET = YesPlease
+ NO_FNMATCH_CASEFOLD = YesPlease
+ NO_GETPAGESIZE = YesPlease
+ NO_ICONV = YesPlease
+ NO_MEMMEM = YesPlease
+ NO_MKDTEMP = YesPlease
+ NO_MKSTEMPS = YesPlease
+ NO_NSEC = YesPlease
+ NO_PTHREADS = YesPlease
+ NO_R_TO_GCC_LINKER = YesPlease
+ NO_STRCASESTR = YesPlease
+ NO_STRLCPY = YesPlease
+endif
-include config.mak.autogen
-include config.mak
ifdef NO_D_INO_IN_DIRENT
BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
endif
+ifdef NO_GECOS_IN_PWENT
+ BASIC_CFLAGS += -DNO_GECOS_IN_PWENT
+endif
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
endif
ifdef NO_SYS_POLL_H
BASIC_CFLAGS += -DNO_SYS_POLL_H
endif
+ifdef NEEDS_SYS_PARAM_H
+ BASIC_CFLAGS += -DNEEDS_SYS_PARAM_H
+endif
ifdef NO_INTTYPES_H
BASIC_CFLAGS += -DNO_INTTYPES_H
endif
COMPAT_CFLAGS += -DNO_MEMMEM
COMPAT_OBJS += compat/memmem.o
endif
+ifdef NO_GETPAGESIZE
+ COMPAT_CFLAGS += -DNO_GETPAGESIZE
+endif
ifdef INTERNAL_QSORT
COMPAT_CFLAGS += -DINTERNAL_QSORT
COMPAT_OBJS += compat/qsort.o
EXTLIBS += $(CHARSET_LIB)
endif
+ifdef HAVE_STRINGS_H
+ BASIC_CFLAGS += -DHAVE_STRINGS_H
+endif
+
ifdef HAVE_DEV_TTY
BASIC_CFLAGS += -DHAVE_DEV_TTY
endif
GIT-SCRIPT-DEFINES: FORCE
@FLAGS='$(SCRIPT_DEFINES)'; \
if test x"$$FLAGS" != x"`cat $@ 2>/dev/null`" ; then \
- echo 1>&2 " * new script parameters"; \
+ echo >&2 " * new script parameters"; \
echo "$$FLAGS" >$@; \
fi
endif # NO_PERL
ifndef NO_PYTHON
-$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX
+$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
$(QUIET_GEN)$(RM) $@ $@+ && \
INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
GIT-PREFIX: FORCE
@FLAGS='$(TRACK_PREFIX)'; \
if test x"$$FLAGS" != x"`cat GIT-PREFIX 2>/dev/null`" ; then \
- echo 1>&2 " * new prefix flags"; \
+ echo >&2 " * new prefix flags"; \
echo "$$FLAGS" >GIT-PREFIX; \
fi
GIT-CFLAGS: FORCE
@FLAGS='$(TRACK_CFLAGS)'; \
if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \
- echo 1>&2 " * new build flags"; \
+ echo >&2 " * new build flags"; \
echo "$$FLAGS" >GIT-CFLAGS; \
fi
GIT-LDFLAGS: FORCE
@FLAGS='$(TRACK_LDFLAGS)'; \
if test x"$$FLAGS" != x"`cat GIT-LDFLAGS 2>/dev/null`" ; then \
- echo 1>&2 " * new link flags"; \
+ echo >&2 " * new link flags"; \
echo "$$FLAGS" >GIT-LDFLAGS; \
fi
@echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
endif
-### Detect Tck/Tk interpreter path changes
-ifndef NO_TCLTK
-TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
+### Detect Python interpreter path changes
+ifndef NO_PYTHON
+TRACK_PYTHON = $(subst ','\'',-DPYTHON_PATH='$(PYTHON_PATH_SQ)')
-GIT-GUI-VARS: FORCE
- @VARS='$(TRACK_VARS)'; \
+GIT-PYTHON-VARS: FORCE
+ @VARS='$(TRACK_PYTHON)'; \
if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
- echo 1>&2 " * new Tcl/Tk interpreter location"; \
+ echo >&2 " * new Python interpreter location"; \
echo "$$VARS" >$@; \
fi
endif
$(MAKE) -C gitk-git clean
$(MAKE) -C git-gui clean
endif
- $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-GUI-VARS GIT-BUILD-OPTIONS
- $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES
+ $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-LDFLAGS GIT-BUILD-OPTIONS
+ $(RM) GIT-USER-AGENT GIT-PREFIX GIT-SCRIPT-DEFINES GIT-PYTHON-VARS
.PHONY: all install profile-clean clean strip
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-Documentation/RelNotes/1.8.1.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.2.txt
\ No newline at end of file
#define MAXDEPTH 5
/*
- * Use this to get the real path, i.e. resolve links. If you want an
- * absolute path but don't mind links, use absolute_path.
+ * Return the real path (i.e., absolute path, with symlinks resolved
+ * and extra slashes removed) equivalent to the specified path. (If
+ * you want an absolute path but don't mind links, use
+ * absolute_path().) The return value is a pointer to a static
+ * buffer.
+ *
+ * The input and all intermediate paths must be shorter than MAX_PATH.
+ * The directory part of path (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist. If die_on_error is set, then die with an
+ * informative error message if there is a problem. Otherwise, return
+ * NULL on errors (without generating any output).
*
* If path is our buffer, then return path, as it's already what the
* user wants.
*/
-const char *real_path(const char *path)
+static const char *real_path_internal(const char *path, int die_on_error)
{
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
+ char *retval = NULL;
+
+ /*
+ * If we have to temporarily chdir(), store the original CWD
+ * here so that we can chdir() back to it at the end of the
+ * function:
+ */
char cwd[1024] = "";
+
int buf_index = 1;
int depth = MAXDEPTH;
if (path == buf || path == next_buf)
return path;
- if (!*path)
- die("The empty string is not a valid path");
+ if (!*path) {
+ if (die_on_error)
+ die("The empty string is not a valid path");
+ else
+ goto error_out;
+ }
- if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
- die ("Too long path: %.*s", 60, path);
+ if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) {
+ if (die_on_error)
+ die("Too long path: %.*s", 60, path);
+ else
+ goto error_out;
+ }
while (depth--) {
if (!is_directory(buf)) {
}
if (*buf) {
- if (!*cwd && !getcwd(cwd, sizeof(cwd)))
- die_errno ("Could not get current working directory");
+ if (!*cwd && !getcwd(cwd, sizeof(cwd))) {
+ if (die_on_error)
+ die_errno("Could not get current working directory");
+ else
+ goto error_out;
+ }
- if (chdir(buf))
- die_errno ("Could not switch to '%s'", buf);
+ if (chdir(buf)) {
+ if (die_on_error)
+ die_errno("Could not switch to '%s'", buf);
+ else
+ goto error_out;
+ }
+ }
+ if (!getcwd(buf, PATH_MAX)) {
+ if (die_on_error)
+ die_errno("Could not get current working directory");
+ else
+ goto error_out;
}
- if (!getcwd(buf, PATH_MAX))
- die_errno ("Could not get current working directory");
if (last_elem) {
size_t len = strlen(buf);
- if (len + strlen(last_elem) + 2 > PATH_MAX)
- die ("Too long path name: '%s/%s'",
- buf, last_elem);
+ if (len + strlen(last_elem) + 2 > PATH_MAX) {
+ if (die_on_error)
+ die("Too long path name: '%s/%s'",
+ buf, last_elem);
+ else
+ goto error_out;
+ }
if (len && !is_dir_sep(buf[len-1]))
buf[len++] = '/';
strcpy(buf + len, last_elem);
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
ssize_t len = readlink(buf, next_buf, PATH_MAX);
- if (len < 0)
- die_errno ("Invalid symlink '%s'", buf);
- if (PATH_MAX <= len)
- die("symbolic link too long: %s", buf);
+ if (len < 0) {
+ if (die_on_error)
+ die_errno("Invalid symlink '%s'", buf);
+ else
+ goto error_out;
+ }
+ if (PATH_MAX <= len) {
+ if (die_on_error)
+ die("symbolic link too long: %s", buf);
+ else
+ goto error_out;
+ }
next_buf[len] = '\0';
buf = next_buf;
buf_index = 1 - buf_index;
break;
}
+ retval = buf;
+error_out:
+ free(last_elem);
if (*cwd && chdir(cwd))
die_errno ("Could not change back to '%s'", cwd);
- return buf;
+ return retval;
+}
+
+const char *real_path(const char *path)
+{
+ return real_path_internal(path, 1);
+}
+
+const char *real_path_if_valid(const char *path)
+{
+ return real_path_internal(path, 0);
}
static const char *get_pwd_cwd(void)
#include "cache.h"
-int advice_push_nonfastforward = 1;
+int advice_push_update_rejected = 1;
int advice_push_non_ff_current = 1;
int advice_push_non_ff_default = 1;
int advice_push_non_ff_matching = 1;
+int advice_push_already_exists = 1;
int advice_status_hints = 1;
int advice_commit_before_merge = 1;
int advice_resolve_conflict = 1;
const char *name;
int *preference;
} advice_config[] = {
- { "pushnonfastforward", &advice_push_nonfastforward },
+ { "pushupdaterejected", &advice_push_update_rejected },
{ "pushnonffcurrent", &advice_push_non_ff_current },
{ "pushnonffdefault", &advice_push_non_ff_default },
{ "pushnonffmatching", &advice_push_non_ff_matching },
+ { "pushalreadyexists", &advice_push_already_exists },
{ "statushints", &advice_status_hints },
{ "commitbeforemerge", &advice_commit_before_merge },
{ "resolveconflict", &advice_resolve_conflict },
{ "implicitidentity", &advice_implicit_identity },
{ "detachedhead", &advice_detached_head },
+
+ /* make this an alias for backward compatibility */
+ { "pushnonfastforward", &advice_push_update_rejected }
};
void advise(const char *advice, ...)
#include "git-compat-util.h"
-extern int advice_push_nonfastforward;
+extern int advice_push_update_rejected;
extern int advice_push_non_ff_current;
extern int advice_push_non_ff_default;
extern int advice_push_non_ff_matching;
+extern int advice_push_already_exists;
extern int advice_status_hints;
extern int advice_commit_before_merge;
extern int advice_resolve_conflict;
strbuf_add(&path, args->base, args->baselen);
strbuf_add(&path, base, baselen);
strbuf_addstr(&path, filename);
+ if (S_ISDIR(mode) || S_ISGITLINK(mode))
+ strbuf_addch(&path, '/');
path_without_prefix = path.buf + args->baselen;
setup_archive_check(check);
}
if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
- strbuf_addch(&path, '/');
if (args->verbose)
fprintf(stderr, "%.*s\n", (int)path.len, path.buf);
err = write_entry(args, sha1, path.buf, path.len, mode);
attr_stack = elem;
}
+static const char *find_basename(const char *path)
+{
+ const char *cp, *last_slash = NULL;
+
+ for (cp = path; *cp; cp++) {
+ if (*cp == '/' && cp[1])
+ last_slash = cp;
+ }
+ return last_slash ? last_slash + 1 : path;
+}
+
static void prepare_attr_stack(const char *path)
{
struct attr_stack *elem, *info;
int dirlen, len;
const char *cp;
- cp = strrchr(path, '/');
- if (!cp)
- dirlen = 0;
- else
- dirlen = cp - path;
+ dirlen = find_basename(path) - path;
/*
* At the bottom of the attribute stack is the built-in
const char *pattern = pat->pattern;
int prefix = pat->nowildcardlen;
+ if ((pat->flags & EXC_FLAG_MUSTBEDIR) &&
+ ((!pathlen) || (pathname[pathlen-1] != '/')))
+ return 0;
+
if (pat->flags & EXC_FLAG_NODIR) {
return match_basename(basename,
pathlen - (basename - pathname),
for (i = 0; i < attr_nr; i++)
check_all_attr[i].value = ATTR__UNKNOWN;
- basename = strrchr(path, '/');
- basename = basename ? basename + 1 : path;
-
+ basename = find_basename(path);
pathlen = strlen(path);
rem = attr_nr;
for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
char *buf,
size_t len, size_t postlen)
{
- int i, ctx;
+ int i, ctx, reduced;
char *new, *old, *fixed;
struct image fixed_preimage;
* free "oldlines".
*/
prepare_image(&fixed_preimage, buf, len, 1);
- assert(fixed_preimage.nr == preimage->nr);
- for (i = 0; i < preimage->nr; i++)
+ assert(postlen
+ ? fixed_preimage.nr == preimage->nr
+ : fixed_preimage.nr <= preimage->nr);
+ for (i = 0; i < fixed_preimage.nr; i++)
fixed_preimage.line[i].flag = preimage->line[i].flag;
free(preimage->line_allocated);
*preimage = fixed_preimage;
else
new = old;
fixed = preimage->buf;
- for (i = ctx = 0; i < postimage->nr; i++) {
+
+ for (i = reduced = ctx = 0; i < postimage->nr; i++) {
size_t len = postimage->line[i].len;
if (!(postimage->line[i].flag & LINE_COMMON)) {
/* an added line -- no counterparts in preimage */
fixed += preimage->line[ctx].len;
ctx++;
}
- if (preimage->nr <= ctx)
- die(_("oops"));
+
+ /*
+ * preimage is expected to run out, if the caller
+ * fixed addition of trailing blank lines.
+ */
+ if (preimage->nr <= ctx) {
+ reduced++;
+ continue;
+ }
/* and copy it in, while fixing the line length */
len = preimage->line[ctx].len;
/* Fix the length of the whole thing */
postimage->len = new - postimage->buf;
+ postimage->nr -= reduced;
}
static int match_fragment(struct image *img,
(int)message_size, (int)message_size, message ? message : "");
}
-static void get_tags_and_duplicates(struct object_array *pending,
+static void get_tags_and_duplicates(struct rev_cmdline_info *info,
struct string_list *extra_refs)
{
struct tag *tag;
int i;
- for (i = 0; i < pending->nr; i++) {
- struct object_array_entry *e = pending->objects + i;
+ for (i = 0; i < info->nr; i++) {
+ struct rev_cmdline_entry *e = info->rev + i;
unsigned char sha1[20];
- struct commit *commit = commit;
+ struct commit *commit;
char *full_name;
+ if (e->flags & UNINTERESTING)
+ continue;
+
if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
continue;
typename(e->item->type));
continue;
}
- if (commit->util)
- /* more than one name for the same object */
+
+ /*
+ * This ref will not be updated through a commit, lets make
+ * sure it gets properly updated eventually.
+ */
+ if (commit->util || commit->object.flags & SHOWN)
string_list_append(extra_refs, full_name)->util = commit;
- else
+ if (!commit->util)
commit->util = full_name;
}
}
if (object->flags & SHOWN)
error("Object %s already has a mark", sha1_to_hex(sha1));
+ if (object->type != OBJ_COMMIT)
+ /* only commits */
+ continue;
+
mark_object(object, mark);
if (last_idnum < mark)
last_idnum = mark;
if (import_filename && revs.prune_data.nr)
full_tree = 1;
- get_tags_and_duplicates(&revs.pending, &extra_refs);
+ get_tags_and_duplicates(&revs.cmdline, &extra_refs);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
matchbuf[0] = prefix;
matchbuf[1] = NULL;
init_pathspec(&pathspec, matchbuf);
- pathspec.items[0].use_wildcard = 0;
+ pathspec.items[0].nowildcard_len = pathspec.items[0].len;
} else
init_pathspec(&pathspec, NULL);
if (read_tree(tree, 1, &pathspec))
init_pathspec(&pathspec, get_pathspec(prefix, argv + 1));
for (i = 0; i < pathspec.nr; i++)
- pathspec.items[i].use_wildcard = 0;
+ pathspec.items[i].nowildcard_len = pathspec.items[i].len;
pathspec.has_wildcard = 0;
tree = parse_tree_indirect(sha1);
if (!tree)
"(e.g. 'git pull') before pushing again.\n"
"See the 'Note about fast-forwards' in 'git push --help' for details.");
+static const char message_advice_ref_already_exists[] =
+ N_("Updates were rejected because the destination reference already exists\n"
+ "in the remote.");
+
static void advise_pull_before_push(void)
{
- if (!advice_push_non_ff_current || !advice_push_nonfastforward)
+ if (!advice_push_non_ff_current || !advice_push_update_rejected)
return;
advise(_(message_advice_pull_before_push));
}
static void advise_use_upstream(void)
{
- if (!advice_push_non_ff_default || !advice_push_nonfastforward)
+ if (!advice_push_non_ff_default || !advice_push_update_rejected)
return;
advise(_(message_advice_use_upstream));
}
static void advise_checkout_pull_push(void)
{
- if (!advice_push_non_ff_matching || !advice_push_nonfastforward)
+ if (!advice_push_non_ff_matching || !advice_push_update_rejected)
return;
advise(_(message_advice_checkout_pull_push));
}
+static void advise_ref_already_exists(void)
+{
+ if (!advice_push_already_exists || !advice_push_update_rejected)
+ return;
+ advise(_(message_advice_ref_already_exists));
+}
+
static int push_with_options(struct transport *transport, int flags)
{
int err;
- int nonfastforward;
+ unsigned int reject_reasons;
transport_set_verbosity(transport, verbosity, progress);
if (verbosity > 0)
fprintf(stderr, _("Pushing to %s\n"), transport->url);
err = transport_push(transport, refspec_nr, refspec, flags,
- &nonfastforward);
+ &reject_reasons);
if (err != 0)
error(_("failed to push some refs to '%s'"), transport->url);
if (!err)
return 0;
- switch (nonfastforward) {
- default:
- break;
- case NON_FF_HEAD:
+ if (reject_reasons & REJECT_NON_FF_HEAD) {
advise_pull_before_push();
- break;
- case NON_FF_OTHER:
+ } else if (reject_reasons & REJECT_NON_FF_OTHER) {
if (default_matching_used)
advise_use_upstream();
else
advise_checkout_pull_push();
- break;
+ } else if (reject_reasons & REJECT_ALREADY_EXISTS) {
+ advise_ref_already_exists();
}
return 1;
msg = "non-fast forward";
break;
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ res = "error";
+ msg = "already exists";
+ break;
+
case REF_STATUS_REJECT_NODELETE:
case REF_STATUS_REMOTE_REJECT:
res = "error";
int send_all = 0;
const char *receivepack = "git-receive-pack";
int flags;
- int nonfastforward = 0;
+ unsigned int reject_reasons;
int progress = -1;
argv++;
ret |= finish_connect(conn);
if (!helper_status)
- transport_print_push_status(dest, remote_refs, args.verbose, 0, &nonfastforward);
+ transport_print_push_status(dest, remote_refs, args.verbose, 0, &reject_reasons);
if (!args.dry_run && remote) {
struct ref *ref;
static void add_wrapped_shortlog_msg(struct strbuf *sb, const char *s,
const struct shortlog *log)
{
- int col = strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
- if (col != log->wrap)
- strbuf_addch(sb, '\n');
+ strbuf_add_wrapped_text(sb, s, log->in1, log->in2, log->wrap);
+ strbuf_addch(sb, '\n');
}
void shortlog_output(struct shortlog *log)
fprintf(stderr, "...\n");
break;
}
- if (ce_stage(ce))
- fprintf(stderr, "%s: unmerged (%s)\n",
- ce->name, sha1_to_hex(ce->sha1));
- else
- fprintf(stderr, "%s: not added yet\n",
- ce->name);
+ fprintf(stderr, "%s: unmerged (%s)\n",
+ ce->name, sha1_to_hex(ce->sha1));
}
}
if (funny)
int entries,
const char *base,
int baselen,
+ int *skip_count,
int flags)
{
struct strbuf buffer;
int missing_ok = flags & WRITE_TREE_MISSING_OK;
int dryrun = flags & WRITE_TREE_DRY_RUN;
+ int to_invalidate = 0;
int i;
+ *skip_count = 0;
+
if (0 <= it->entry_count && has_sha1_file(it->sha1))
return it->entry_count;
/*
* Find the subtrees and update them.
*/
- for (i = 0; i < entries; i++) {
+ i = 0;
+ while (i < entries) {
struct cache_entry *ce = cache[i];
struct cache_tree_sub *sub;
const char *path, *slash;
- int pathlen, sublen, subcnt;
+ int pathlen, sublen, subcnt, subskip;
path = ce->name;
pathlen = ce_namelen(ce);
break; /* at the end of this level */
slash = strchr(path + baselen, '/');
- if (!slash)
+ if (!slash) {
+ i++;
continue;
+ }
/*
* a/bbb/c (base = a/, slash = /c)
* ==>
cache + i, entries - i,
path,
baselen + sublen + 1,
+ &subskip,
flags);
if (subcnt < 0)
return subcnt;
- i += subcnt - 1;
+ i += subcnt;
+ sub->count = subcnt; /* to be used in the next loop */
+ *skip_count += subskip;
sub->used = 1;
}
*/
strbuf_init(&buffer, 8192);
- for (i = 0; i < entries; i++) {
+ i = 0;
+ while (i < entries) {
struct cache_entry *ce = cache[i];
struct cache_tree_sub *sub;
const char *path, *slash;
if (!sub)
die("cache-tree.c: '%.*s' in '%s' not found",
entlen, path + baselen, path);
- i += sub->cache_tree->entry_count - 1;
+ i += sub->count;
sha1 = sub->cache_tree->sha1;
mode = S_IFDIR;
+ if (sub->cache_tree->entry_count < 0)
+ to_invalidate = 1;
}
else {
sha1 = ce->sha1;
mode = ce->ce_mode;
entlen = pathlen - baselen;
+ i++;
}
if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
strbuf_release(&buffer);
mode, sha1_to_hex(sha1), entlen+baselen, path);
}
- if (ce->ce_flags & (CE_REMOVE | CE_INTENT_TO_ADD))
- continue; /* entry being removed or placeholder */
+ /*
+ * CE_REMOVE entries are removed before the index is
+ * written to disk. Skip them to remain consistent
+ * with the future on-disk index.
+ */
+ if (ce->ce_flags & CE_REMOVE) {
+ *skip_count = *skip_count + 1;
+ continue;
+ }
+
+ /*
+ * CE_INTENT_TO_ADD entries exist on on-disk index but
+ * they are not part of generated trees. Invalidate up
+ * to root to force cache-tree users to read elsewhere.
+ */
+ if (ce->ce_flags & CE_INTENT_TO_ADD) {
+ to_invalidate = 1;
+ continue;
+ }
strbuf_grow(&buffer, entlen + 100);
strbuf_addf(&buffer, "%o %.*s%c", mode, entlen, path + baselen, '\0');
}
strbuf_release(&buffer);
- it->entry_count = i;
+ it->entry_count = to_invalidate ? -1 : i - *skip_count;
#if DEBUG
fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
it->entry_count, it->subtree_nr,
int entries,
int flags)
{
- int i;
+ int i, skip;
i = verify_cache(cache, entries, flags);
if (i)
return i;
- i = update_one(it, cache, entries, "", 0, flags);
+ i = update_one(it, cache, entries, "", 0, &skip, flags);
if (i < 0)
return i;
return 0;
struct cache_tree;
struct cache_tree_sub {
struct cache_tree *cache_tree;
+ int count; /* internally used by update_one() */
int namelen;
int used;
char name[FLEX_ARRAY];
#define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
#define GIT_NOTES_REWRITE_REF_ENVIRONMENT "GIT_NOTES_REWRITE_REF"
#define GIT_NOTES_REWRITE_MODE_ENVIRONMENT "GIT_NOTES_REWRITE_MODE"
+#define GIT_LITERAL_PATHSPECS_ENVIRONMENT "GIT_LITERAL_PATHSPECS"
/*
* Repository-local GIT_* environment variables
extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
+#define PATHSPEC_ONESTAR 1 /* the pathspec pattern sastisfies GFNM_ONESTAR */
+
struct pathspec {
const char **raw; /* get_pathspec() result, not freed by free_pathspec() */
int nr;
struct pathspec_item {
const char *match;
int len;
- unsigned int use_wildcard:1;
+ int nowildcard_len;
+ int flags;
} *items;
};
extern void free_pathspec(struct pathspec *);
extern int ce_path_match(const struct cache_entry *ce, const struct pathspec *pathspec);
+extern int limit_pathspec_to_literal(void);
+
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
}
int is_directory(const char *);
const char *real_path(const char *path);
+const char *real_path_if_valid(const char *path);
const char *absolute_path(const char *path);
const char *relative_path(const char *abs, const char *base);
int normalize_path_copy(char *dst, const char *src);
-int longest_ancestor_length(const char *path, const char *prefix_list);
+int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
int daemon_avoid_alias(const char *path);
int offset_1st_component(const char *path);
unsigned char old_sha1[20];
unsigned char new_sha1[20];
char *symref;
- unsigned int force:1,
+ unsigned int
+ force:1,
+ requires_force:1,
merge:1,
nonfastforward:1,
+ not_forwardable:1,
+ update:1,
deletion:1;
enum {
REF_STATUS_NONE = 0,
REF_STATUS_OK,
REF_STATUS_REJECT_NONFASTFORWARD,
+ REF_STATUS_REJECT_ALREADY_EXISTS,
REF_STATUS_REJECT_NODELETE,
REF_STATUS_UPTODATE,
REF_STATUS_REMOTE_REJECT,
extern int git_env_bool(const char *, int);
extern int git_config_system(void);
extern int config_error_nonbool(const char *);
+#ifdef __GNUC__
+#define config_error_nonbool(s) (config_error_nonbool(s), -1)
+#endif
extern const char *get_log_output_encoding(void);
extern const char *get_commit_output_encoding(void);
extern const char *git_commit_encoding;
extern const char *git_log_output_encoding;
extern const char *git_mailmap_file;
+extern const char *git_mailmap_blob;
/* IO helper functions */
extern void maybe_flush_or_die(FILE *, const char *);
char *notes_message;
struct reflog_walk_info *reflog_info;
const char *output_encoding;
+ int color;
};
struct userformat_want {
path = buf.buf;
}
- if (!access_or_warn(path, R_OK)) {
+ if (!access_or_die(path, R_OK)) {
if (++inc->depth > MAX_INCLUDE_DEPTH)
die(include_depth_advice, MAX_INCLUDE_DEPTH, path,
cf && cf->name ? cf->name : "the command line");
{
if (!strcmp(var, "mailmap.file"))
return git_config_string(&git_mailmap_file, var, value);
+ if (!strcmp(var, "mailmap.blob"))
+ return git_config_string(&git_mailmap_blob, var, value);
/* Add other config variables here and to Documentation/config.txt. */
return 0;
home_config_paths(&user_config, &xdg_config, "config");
- if (git_config_system() && !access_or_warn(git_etc_gitconfig(), R_OK)) {
+ if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK)) {
ret += git_config_from_file(fn, git_etc_gitconfig(),
data);
found += 1;
}
- if (xdg_config && !access_or_warn(xdg_config, R_OK)) {
+ if (xdg_config && !access_or_die(xdg_config, R_OK)) {
ret += git_config_from_file(fn, xdg_config, data);
found += 1;
}
- if (user_config && !access_or_warn(user_config, R_OK)) {
+ if (user_config && !access_or_die(user_config, R_OK)) {
ret += git_config_from_file(fn, user_config, data);
found += 1;
}
- if (repo_config && !access_or_warn(repo_config, R_OK)) {
+ if (repo_config && !access_or_die(repo_config, R_OK)) {
ret += git_config_from_file(fn, repo_config, data);
found += 1;
}
* Call this to report error for your variable that should not
* get a boolean value (i.e. "[my] var" means "true").
*/
+#undef config_error_nonbool
int config_error_nonbool(const char *var)
{
return error("Missing value for '%s'", var);
[#include <dirent.h>])
GIT_CONF_SUBST([NO_D_TYPE_IN_DIRENT])
#
+# Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
+# in the C library.
+AC_CHECK_MEMBER(struct passwd.pw_gecos,
+[NO_GECOS_IN_PWENT=],
+[NO_GECOS_IN_PWENT=YesPlease],
+[#include <pwd.h>])
+GIT_CONF_SUBST([NO_GECOS_IN_PWENT])
+#
# Define NO_SOCKADDR_STORAGE if your platform does not have struct
# sockaddr_storage.
AC_CHECK_TYPE(struct sockaddr_storage,
[HAVE_LIBCHARSET_H=YesPlease],
[HAVE_LIBCHARSET_H=])
GIT_CONF_SUBST([HAVE_LIBCHARSET_H])
+#
+# Define HAVE_STRINGS_H if you have strings.h
+AC_CHECK_HEADER([strings.h],
+[HAVE_STRINGS_H=YesPlease],
+[HAVE_STRINGS_H=])
+GIT_CONF_SUBST([HAVE_STRINGS_H])
# Define CHARSET_LIB if libiconv does not export the locale_charset symbol
# and libcharset does
CHARSET_LIB=
# -D_REENTRANT' or some such.
elif test -z "$PTHREAD_CFLAGS"; then
threads_found=no
- for opt in -mt -pthread -lpthread; do
+ # Attempt to compile and link some code using pthreads to determine
+ # required linker flags. The order is somewhat important here: We
+ # first try it without any extra flags, to catch systems where
+ # pthreads are part of the C library, then go on testing various other
+ # flags. We do so to avoid false positives. For example, on Mac OS X
+ # pthreads are part of the C library; moreover, the compiler allows us
+ # to add "-mt" to the CFLAGS (although it will do nothing except
+ # trigger a warning about an unused flag). Hence if we checked for
+ # "-mt" before "" we would end up picking it. But unfortunately this
+ # would then trigger compiler warnings on every single file we compile.
+ for opt in "" -mt -pthread -lpthread; do
old_CFLAGS="$CFLAGS"
CFLAGS="$opt $CFLAGS"
AC_MSG_CHECKING([for POSIX Threads with '$opt'])
{
__git_has_doubledash && return
+ case "$prev" in
+ -c|-C)
+ __gitcomp_nl "$(__git_refs)" "" "${cur}"
+ return
+ ;;
+ esac
+
case "$cur" in
--cleanup=*)
__gitcomp "default strip verbatim whitespace
return fnmatch(pattern, string, flags | (ignore_case ? FNM_CASEFOLD : 0));
}
+inline int git_fnmatch(const char *pattern, const char *string,
+ int flags, int prefix)
+{
+ int fnm_flags = 0;
+ if (flags & GFNM_PATHNAME)
+ fnm_flags |= FNM_PATHNAME;
+ if (prefix > 0) {
+ if (strncmp(pattern, string, prefix))
+ return FNM_NOMATCH;
+ pattern += prefix;
+ string += prefix;
+ }
+ if (flags & GFNM_ONESTAR) {
+ int pattern_len = strlen(++pattern);
+ int string_len = strlen(string);
+ return string_len < pattern_len ||
+ strcmp(pattern,
+ string + string_len - pattern_len);
+ }
+ return fnmatch(pattern, string, fnm_flags);
+}
+
static size_t common_prefix_len(const char **pathspec)
{
const char *n, *first;
size_t max = 0;
+ int literal = limit_pathspec_to_literal();
if (!pathspec)
return max;
size_t i, len = 0;
for (i = 0; first == n || i < max; i++) {
char c = n[i];
- if (!c || c != first[i] || is_glob_special(c))
+ if (!c || c != first[i] || (!literal && is_glob_special(c)))
break;
if (c == '/')
len = i + 1;
static int match_one(const char *match, const char *name, int namelen)
{
int matchlen;
+ int literal = limit_pathspec_to_literal();
/* If the match was just the prefix, we matched */
if (!*match)
for (;;) {
unsigned char c1 = tolower(*match);
unsigned char c2 = tolower(*name);
- if (c1 == '\0' || is_glob_special(c1))
+ if (c1 == '\0' || (!literal && is_glob_special(c1)))
break;
if (c1 != c2)
return 0;
for (;;) {
unsigned char c1 = *match;
unsigned char c2 = *name;
- if (c1 == '\0' || is_glob_special(c1))
+ if (c1 == '\0' || (!literal && is_glob_special(c1)))
break;
if (c1 != c2)
return 0;
}
}
-
/*
* If we don't match the matchstring exactly,
* we need to match by fnmatch
*/
matchlen = strlen(match);
- if (strncmp_icase(match, name, matchlen))
+ if (strncmp_icase(match, name, matchlen)) {
+ if (literal)
+ return 0;
return !fnmatch_icase(match, name, 0) ? MATCHED_FNMATCH : 0;
+ }
if (namelen == matchlen)
return MATCHED_EXACTLY;
return MATCHED_RECURSIVELY;
}
- if (item->use_wildcard && !fnmatch(match, name, 0))
+ if (item->nowildcard_len < item->len &&
+ !git_fnmatch(match, name,
+ item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
+ item->nowildcard_len - prefix))
return MATCHED_FNMATCH;
return 0;
item->match = path;
item->len = strlen(path);
- item->use_wildcard = !no_wildcard(path);
- if (item->use_wildcard)
- pathspec->has_wildcard = 1;
+ item->flags = 0;
+ if (limit_pathspec_to_literal()) {
+ item->nowildcard_len = item->len;
+ } else {
+ item->nowildcard_len = simple_length(path);
+ if (item->nowildcard_len < item->len) {
+ pathspec->has_wildcard = 1;
+ if (path[item->nowildcard_len] == '*' &&
+ no_wildcard(path + item->nowildcard_len + 1))
+ item->flags |= PATHSPEC_ONESTAR;
+ }
+ }
}
qsort(pathspec->items, pathspec->nr,
free(pathspec->items);
pathspec->items = NULL;
}
+
+int limit_pathspec_to_literal(void)
+{
+ static int flag = -1;
+ if (flag < 0)
+ flag = git_env_bool(GIT_LITERAL_PATHSPECS_ENVIRONMENT, 0);
+ return flag;
+}
extern int strncmp_icase(const char *a, const char *b, size_t count);
extern int fnmatch_icase(const char *pattern, const char *string, int flags);
+/*
+ * The prefix part of pattern must not contains wildcards.
+ */
+#define GFNM_PATHNAME 1 /* similar to FNM_PATHNAME */
+#define GFNM_ONESTAR 2 /* there is only _one_ wildcard, a star */
+
+extern int git_fnmatch(const char *pattern, const char *string,
+ int flags, int prefix);
+
#endif
#include "cache.h"
#include "strbuf.h"
#include "run-command.h"
+#include "sigchain.h"
#ifndef DEFAULT_EDITOR
#define DEFAULT_EDITOR "vi"
if (strcmp(editor, ":")) {
const char *args[] = { editor, path, NULL };
+ struct child_process p;
+ int ret, sig;
- if (run_command_v_opt_cd_env(args, RUN_USING_SHELL, NULL, env))
+ memset(&p, 0, sizeof(p));
+ p.argv = args;
+ p.env = env;
+ p.use_shell = 1;
+ if (start_command(&p) < 0)
+ return error("unable to start editor '%s'", editor);
+
+ sigchain_push(SIGINT, SIG_IGN);
+ sigchain_push(SIGQUIT, SIG_IGN);
+ ret = finish_command(&p);
+ sig = ret + 128;
+ sigchain_pop(SIGINT);
+ sigchain_pop(SIGQUIT);
+ if (sig == SIGINT || sig == SIGQUIT)
+ raise(sig);
+ if (ret)
return error("There was a problem with the editor '%s'.",
editor);
}
return git_default_config(var, value, cb);
}
-static struct lock_file lock;
-
static void fetch_pack_setup(void)
{
static int did_setup;
ref_cpy = do_fetch_pack(args, fd, ref, sought, pack_lockfile);
if (args->depth > 0) {
+ static struct lock_file lock;
struct cache_time mtime;
struct strbuf sb = STRBUF_INIT;
char *shallow = git_path("shallow");
int has_null_sha1 = 0;
int has_full_path = 0;
int has_empty_name = 0;
+ int has_dot = 0;
+ int has_dotdot = 0;
+ int has_dotgit = 0;
int has_zero_pad = 0;
int has_bad_modes = 0;
int has_dup_entries = 0;
has_full_path = 1;
if (!*name)
has_empty_name = 1;
+ if (!strcmp(name, "."))
+ has_dot = 1;
+ if (!strcmp(name, ".."))
+ has_dotdot = 1;
+ if (!strcmp(name, ".git"))
+ has_dotgit = 1;
has_zero_pad |= *(char *)desc.buffer == '0';
update_tree_entry(&desc);
retval += error_func(&item->object, FSCK_WARN, "contains full pathnames");
if (has_empty_name)
retval += error_func(&item->object, FSCK_WARN, "contains empty pathname");
+ if (has_dot)
+ retval += error_func(&item->object, FSCK_WARN, "contains '.'");
+ if (has_dotdot)
+ retval += error_func(&item->object, FSCK_WARN, "contains '..'");
+ if (has_dotgit)
+ retval += error_func(&item->object, FSCK_WARN, "contains '.git'");
if (has_zero_pad)
retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes");
if (has_bad_modes)
# endif
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && \
!defined(_M_UNIX) && !defined(__sgi) && !defined(__DragonFly__) && \
- !defined(__TANDEM)
+ !defined(__TANDEM) && !defined(__QNX__)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
-#ifdef __TANDEM /* or HAVE_STRINGS_H or !NO_STRINGS_H? */
+#ifdef HAVE_STRINGS_H
#include <strings.h> /* for strcasecmp() */
#endif
#include <errno.h>
#include <limits.h>
+#ifdef NEEDS_SYS_PARAM_H
#include <sys/param.h>
+#endif
#include <sys/types.h>
#include <dirent.h>
#include <sys/time.h>
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
+/*
+ * Let callers be aware of the constant return value; this can help
+ * gcc with -Wuninitialized analysis. We have to restrict this trick to
+ * gcc, though, because of the variadic macro and the magic ## comma pasting
+ * behavior. But since we're only trying to help gcc, anyway, it's OK; other
+ * compilers will fall back to using the function as usual.
+ */
+#ifdef __GNUC__
+#define error(fmt, ...) (error((fmt), ##__VA_ARGS__), -1)
+#endif
+
extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
extern void set_error_routine(void (*routine)(const char *err, va_list params));
const void *needle, size_t needlelen);
#endif
+#ifdef NO_GETPAGESIZE
+#define getpagesize() sysconf(_SC_PAGESIZE)
+#endif
+
#ifdef FREAD_READS_DIRECTORIES
#ifdef fopen
#undef fopen
*/
int remove_or_warn(unsigned int mode, const char *path);
-/* Call access(2), but warn for any error besides ENOENT. */
+/*
+ * Call access(2), but warn for any error except "missing file"
+ * (ENOENT or ENOTDIR).
+ */
int access_or_warn(const char *path, int mode);
+int access_or_die(const char *path, int mode);
/* Warn on an inaccessible file that ought to be accessible */
void warn_on_inaccessible(const char *path);
--- /dev/null
+#!/usr/bin/env bash
+# Copyright (c) 2012 Felipe Contreras
+
+alias=$1
+url=$2
+
+dir="$GIT_DIR/testgit/$alias"
+prefix="refs/testgit/$alias"
+
+default_refspec="refs/heads/*:${prefix}/heads/*"
+
+refspec="${GIT_REMOTE_TESTGIT_REFSPEC-$default_refspec}"
+
+test -z "$refspec" && prefix="refs"
+
+export GIT_DIR="$url/.git"
+
+mkdir -p "$dir"
+
+if test -z "$GIT_REMOTE_TESTGIT_NO_MARKS"
+then
+ gitmarks="$dir/git.marks"
+ testgitmarks="$dir/testgit.marks"
+ test -e "$gitmarks" || >"$gitmarks"
+ test -e "$testgitmarks" || >"$testgitmarks"
+ testgitmarks_args=( "--"{import,export}"-marks=$testgitmarks" )
+fi
+
+while read line
+do
+ case $line in
+ capabilities)
+ echo 'import'
+ echo 'export'
+ test -n "$refspec" && echo "refspec $refspec"
+ if test -n "$gitmarks"
+ then
+ echo "*import-marks $gitmarks"
+ echo "*export-marks $gitmarks"
+ fi
+ echo
+ ;;
+ list)
+ git for-each-ref --format='? %(refname)' 'refs/heads/'
+ head=$(git symbolic-ref HEAD)
+ echo "@$head HEAD"
+ echo
+ ;;
+ import*)
+ # read all import lines
+ while true
+ do
+ ref="${line#* }"
+ refs="$refs $ref"
+ read line
+ test "${line%% *}" != "import" && break
+ done
+
+ if test -n "$gitmarks"
+ then
+ echo "feature import-marks=$gitmarks"
+ echo "feature export-marks=$gitmarks"
+ fi
+ echo "feature done"
+ git fast-export "${testgitmarks_args[@]}" $refs |
+ sed -e "s#refs/heads/#${prefix}/heads/#g"
+ echo "done"
+ ;;
+ export)
+ before=$(git for-each-ref --format='%(refname) %(objectname)')
+
+ git fast-import "${testgitmarks_args[@]}" --quiet
+
+ after=$(git for-each-ref --format='%(refname) %(objectname)')
+
+ # figure out which refs were updated
+ join -e 0 -o '0 1.2 2.2' -a 2 <(echo "$before") <(echo "$after") |
+ while read ref a b
+ do
+ test $a == $b && continue
+ echo "ok $ref"
+ done
+
+ echo
+ ;;
+ '')
+ exit
+ ;;
+ esac
+done
+++ /dev/null
-#!/usr/bin/env python
-
-# This command is a simple remote-helper, that is used both as a
-# testcase for the remote-helper functionality, and as an example to
-# show remote-helper authors one possible implementation.
-#
-# This is a Git <-> Git importer/exporter, that simply uses git
-# fast-import and git fast-export to consume and produce fast-import
-# streams.
-#
-# To understand better the way things work, one can activate debug
-# traces by setting (to any value) the environment variables
-# GIT_TRANSPORT_HELPER_DEBUG and GIT_DEBUG_TESTGIT, to see messages
-# from the transport-helper side, or from this example remote-helper.
-
-# hashlib is only available in python >= 2.5
-try:
- import hashlib
- _digest = hashlib.sha1
-except ImportError:
- import sha
- _digest = sha.new
-import sys
-import os
-import time
-sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
-
-from git_remote_helpers.util import die, debug, warn
-from git_remote_helpers.git.repo import GitRepo
-from git_remote_helpers.git.exporter import GitExporter
-from git_remote_helpers.git.importer import GitImporter
-from git_remote_helpers.git.non_local import NonLocalGit
-
-def get_repo(alias, url):
- """Returns a git repository object initialized for usage.
- """
-
- repo = GitRepo(url)
- repo.get_revs()
- repo.get_head()
-
- hasher = _digest()
- hasher.update(repo.path)
- repo.hash = hasher.hexdigest()
-
- repo.get_base_path = lambda base: os.path.join(
- base, 'info', 'fast-import', repo.hash)
-
- prefix = 'refs/testgit/%s/' % alias
- debug("prefix: '%s'", prefix)
-
- repo.gitdir = os.environ["GIT_DIR"]
- repo.alias = alias
- repo.prefix = prefix
-
- repo.exporter = GitExporter(repo)
- repo.importer = GitImporter(repo)
- repo.non_local = NonLocalGit(repo)
-
- return repo
-
-
-def local_repo(repo, path):
- """Returns a git repository object initalized for usage.
- """
-
- local = GitRepo(path)
-
- local.non_local = None
- local.gitdir = repo.gitdir
- local.alias = repo.alias
- local.prefix = repo.prefix
- local.hash = repo.hash
- local.get_base_path = repo.get_base_path
- local.exporter = GitExporter(local)
- local.importer = GitImporter(local)
-
- return local
-
-
-def do_capabilities(repo, args):
- """Prints the supported capabilities.
- """
-
- print "import"
- print "export"
- print "refspec refs/heads/*:%s*" % repo.prefix
-
- dirname = repo.get_base_path(repo.gitdir)
-
- if not os.path.exists(dirname):
- os.makedirs(dirname)
-
- path = os.path.join(dirname, 'testgit.marks')
-
- print "*export-marks %s" % path
- if os.path.exists(path):
- print "*import-marks %s" % path
-
- print # end capabilities
-
-
-def do_list(repo, args):
- """Lists all known references.
-
- Bug: This will always set the remote head to master for non-local
- repositories, since we have no way of determining what the remote
- head is at clone time.
- """
-
- for ref in repo.revs:
- debug("? refs/heads/%s", ref)
- print "? refs/heads/%s" % ref
-
- if repo.head:
- debug("@refs/heads/%s HEAD" % repo.head)
- print "@refs/heads/%s HEAD" % repo.head
- else:
- debug("@refs/heads/master HEAD")
- print "@refs/heads/master HEAD"
-
- print # end list
-
-
-def update_local_repo(repo):
- """Updates (or clones) a local repo.
- """
-
- if repo.local:
- return repo
-
- path = repo.non_local.clone(repo.gitdir)
- repo.non_local.update(repo.gitdir)
- repo = local_repo(repo, path)
- return repo
-
-
-def do_import(repo, args):
- """Exports a fast-import stream from testgit for git to import.
- """
-
- if len(args) != 1:
- die("Import needs exactly one ref")
-
- if not repo.gitdir:
- die("Need gitdir to import")
-
- ref = args[0]
- refs = [ref]
-
- while True:
- line = sys.stdin.readline()
- if line == '\n':
- break
- if not line.startswith('import '):
- die("Expected import line.")
-
- # strip of leading 'import '
- ref = line[7:].strip()
- refs.append(ref)
-
- repo = update_local_repo(repo)
- repo.exporter.export_repo(repo.gitdir, refs)
-
- print "done"
-
-
-def do_export(repo, args):
- """Imports a fast-import stream from git to testgit.
- """
-
- if not repo.gitdir:
- die("Need gitdir to export")
-
- update_local_repo(repo)
- changed = repo.importer.do_import(repo.gitdir)
-
- if not repo.local:
- repo.non_local.push(repo.gitdir)
-
- for ref in changed:
- print "ok %s" % ref
- print
-
-
-COMMANDS = {
- 'capabilities': do_capabilities,
- 'list': do_list,
- 'import': do_import,
- 'export': do_export,
-}
-
-
-def sanitize(value):
- """Cleans up the url.
- """
-
- if value.startswith('testgit::'):
- value = value[9:]
-
- return value
-
-
-def read_one_line(repo):
- """Reads and processes one command.
- """
-
- sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY")
- if sleepy:
- debug("Sleeping %d sec before readline" % int(sleepy))
- time.sleep(int(sleepy))
-
- line = sys.stdin.readline()
-
- cmdline = line
-
- if not cmdline:
- warn("Unexpected EOF")
- return False
-
- cmdline = cmdline.strip().split()
- if not cmdline:
- # Blank line means we're about to quit
- return False
-
- cmd = cmdline.pop(0)
- debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
-
- if cmd not in COMMANDS:
- die("Unknown command, %s", cmd)
-
- func = COMMANDS[cmd]
- func(repo, cmdline)
- sys.stdout.flush()
-
- return True
-
-
-def main(args):
- """Starts a new remote helper for the specified repository.
- """
-
- if len(args) != 3:
- die("Expecting exactly three arguments.")
- sys.exit(1)
-
- if os.getenv("GIT_DEBUG_TESTGIT"):
- import git_remote_helpers.util
- git_remote_helpers.util.DEBUG = True
-
- alias = sanitize(args[1])
- url = sanitize(args[2])
-
- if not alias.isalnum():
- warn("non-alnum alias '%s'", alias)
- alias = "tmp"
-
- args[1] = alias
- args[2] = url
-
- repo = get_repo(alias, url)
-
- debug("Got arguments %s", args[1:])
-
- more = True
-
- sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
- while (more):
- more = read_one_line(repo)
-
-if __name__ == '__main__':
- sys.exit(main(sys.argv))
--- /dev/null
+#!/usr/bin/env python
+
+# This command is a simple remote-helper, that is used both as a
+# testcase for the remote-helper functionality, and as an example to
+# show remote-helper authors one possible implementation.
+#
+# This is a Git <-> Git importer/exporter, that simply uses git
+# fast-import and git fast-export to consume and produce fast-import
+# streams.
+#
+# To understand better the way things work, one can activate debug
+# traces by setting (to any value) the environment variables
+# GIT_TRANSPORT_HELPER_DEBUG and GIT_DEBUG_TESTGIT, to see messages
+# from the transport-helper side, or from this example remote-helper.
+
+# hashlib is only available in python >= 2.5
+try:
+ import hashlib
+ _digest = hashlib.sha1
+except ImportError:
+ import sha
+ _digest = sha.new
+import sys
+import os
+import time
+sys.path.insert(0, os.getenv("GITPYTHONLIB","."))
+
+from git_remote_helpers.util import die, debug, warn
+from git_remote_helpers.git.repo import GitRepo
+from git_remote_helpers.git.exporter import GitExporter
+from git_remote_helpers.git.importer import GitImporter
+from git_remote_helpers.git.non_local import NonLocalGit
+
+def get_repo(alias, url):
+ """Returns a git repository object initialized for usage.
+ """
+
+ repo = GitRepo(url)
+ repo.get_revs()
+ repo.get_head()
+
+ hasher = _digest()
+ hasher.update(repo.path)
+ repo.hash = hasher.hexdigest()
+
+ repo.get_base_path = lambda base: os.path.join(
+ base, 'info', 'fast-import', repo.hash)
+
+ prefix = 'refs/testgit/%s/' % alias
+ debug("prefix: '%s'", prefix)
+
+ repo.gitdir = os.environ["GIT_DIR"]
+ repo.alias = alias
+ repo.prefix = prefix
+
+ repo.exporter = GitExporter(repo)
+ repo.importer = GitImporter(repo)
+ repo.non_local = NonLocalGit(repo)
+
+ return repo
+
+
+def local_repo(repo, path):
+ """Returns a git repository object initalized for usage.
+ """
+
+ local = GitRepo(path)
+
+ local.non_local = None
+ local.gitdir = repo.gitdir
+ local.alias = repo.alias
+ local.prefix = repo.prefix
+ local.hash = repo.hash
+ local.get_base_path = repo.get_base_path
+ local.exporter = GitExporter(local)
+ local.importer = GitImporter(local)
+
+ return local
+
+
+def do_capabilities(repo, args):
+ """Prints the supported capabilities.
+ """
+
+ print "import"
+ print "export"
+ print "refspec refs/heads/*:%s*" % repo.prefix
+
+ dirname = repo.get_base_path(repo.gitdir)
+
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+
+ path = os.path.join(dirname, 'git.marks')
+
+ print "*export-marks %s" % path
+ if os.path.exists(path):
+ print "*import-marks %s" % path
+
+ print # end capabilities
+
+
+def do_list(repo, args):
+ """Lists all known references.
+
+ Bug: This will always set the remote head to master for non-local
+ repositories, since we have no way of determining what the remote
+ head is at clone time.
+ """
+
+ for ref in repo.revs:
+ debug("? refs/heads/%s", ref)
+ print "? refs/heads/%s" % ref
+
+ if repo.head:
+ debug("@refs/heads/%s HEAD" % repo.head)
+ print "@refs/heads/%s HEAD" % repo.head
+ else:
+ debug("@refs/heads/master HEAD")
+ print "@refs/heads/master HEAD"
+
+ print # end list
+
+
+def update_local_repo(repo):
+ """Updates (or clones) a local repo.
+ """
+
+ if repo.local:
+ return repo
+
+ path = repo.non_local.clone(repo.gitdir)
+ repo.non_local.update(repo.gitdir)
+ repo = local_repo(repo, path)
+ return repo
+
+
+def do_import(repo, args):
+ """Exports a fast-import stream from testgit for git to import.
+ """
+
+ if len(args) != 1:
+ die("Import needs exactly one ref")
+
+ if not repo.gitdir:
+ die("Need gitdir to import")
+
+ ref = args[0]
+ refs = [ref]
+
+ while True:
+ line = sys.stdin.readline()
+ if line == '\n':
+ break
+ if not line.startswith('import '):
+ die("Expected import line.")
+
+ # strip of leading 'import '
+ ref = line[7:].strip()
+ refs.append(ref)
+
+ repo = update_local_repo(repo)
+ repo.exporter.export_repo(repo.gitdir, refs)
+
+ print "done"
+
+
+def do_export(repo, args):
+ """Imports a fast-import stream from git to testgit.
+ """
+
+ if not repo.gitdir:
+ die("Need gitdir to export")
+
+ update_local_repo(repo)
+ changed = repo.importer.do_import(repo.gitdir)
+
+ if not repo.local:
+ repo.non_local.push(repo.gitdir)
+
+ for ref in changed:
+ print "ok %s" % ref
+ print
+
+
+COMMANDS = {
+ 'capabilities': do_capabilities,
+ 'list': do_list,
+ 'import': do_import,
+ 'export': do_export,
+}
+
+
+def sanitize(value):
+ """Cleans up the url.
+ """
+
+ if value.startswith('testgit::'):
+ value = value[9:]
+
+ return value
+
+
+def read_one_line(repo):
+ """Reads and processes one command.
+ """
+
+ sleepy = os.environ.get("GIT_REMOTE_TESTGIT_SLEEPY")
+ if sleepy:
+ debug("Sleeping %d sec before readline" % int(sleepy))
+ time.sleep(int(sleepy))
+
+ line = sys.stdin.readline()
+
+ cmdline = line
+
+ if not cmdline:
+ warn("Unexpected EOF")
+ return False
+
+ cmdline = cmdline.strip().split()
+ if not cmdline:
+ # Blank line means we're about to quit
+ return False
+
+ cmd = cmdline.pop(0)
+ debug("Got command '%s' with args '%s'", cmd, ' '.join(cmdline))
+
+ if cmd not in COMMANDS:
+ die("Unknown command, %s", cmd)
+
+ func = COMMANDS[cmd]
+ func(repo, cmdline)
+ sys.stdout.flush()
+
+ return True
+
+
+def main(args):
+ """Starts a new remote helper for the specified repository.
+ """
+
+ if len(args) != 3:
+ die("Expecting exactly three arguments.")
+ sys.exit(1)
+
+ if os.getenv("GIT_DEBUG_TESTGIT"):
+ import git_remote_helpers.util
+ git_remote_helpers.util.DEBUG = True
+
+ alias = sanitize(args[1])
+ url = sanitize(args[2])
+
+ if not alias.isalnum():
+ warn("non-alnum alias '%s'", alias)
+ alias = "tmp"
+
+ args[1] = alias
+ args[2] = url
+
+ repo = get_repo(alias, url)
+
+ debug("Got arguments %s", args[1:])
+
+ more = True
+
+ sys.stdin = os.fdopen(sys.stdin.fileno(), 'r', 0)
+ while (more):
+ more = read_one_line(repo)
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
USAGE="[--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
- or: $dashless [--quiet] update [--init] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+ or: $dashless [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: $dashless [--quiet] foreach [--recursive] <command>
or: $dashless [--quiet] sync [--recursive] [--] [<path>...]"
recursive=
init=
files=
+remote=
nofetch=
update=
prefix=
fi
}
+#
+# Print a submodule configuration setting
+#
+# $1 = submodule name
+# $2 = option name
+# $3 = default value
+#
+# Checks in the usual git-config places first (for overrides),
+# otherwise it falls back on .gitmodules. This allows you to
+# distribute project-wide defaults in .gitmodules, while still
+# customizing individual repositories if necessary. If the option is
+# not in .gitmodules either, print a default value.
+#
+get_submodule_config () {
+ name="$1"
+ option="$2"
+ default="$3"
+ value=$(git config submodule."$name"."$option")
+ if test -z "$value"
+ then
+ value=$(git config -f .gitmodules submodule."$name"."$option")
+ fi
+ printf '%s' "${value:-$default}"
+}
+
+
#
# Map submodule path to submodule name
#
git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
git config -f .gitmodules submodule."$sm_name".url "$repo" &&
+ if test -n "$branch"
+ then
+ git config -f .gitmodules submodule."$sm_name".branch "$branch"
+ fi &&
git add --force .gitmodules ||
die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
}
-i|--init)
init=1
;;
+ --remote)
+ remote=1
+ ;;
-N|--no-fetch)
nofetch=1
;;
fi
name=$(module_name "$sm_path") || exit
url=$(git config submodule."$name".url)
+ branch=$(get_submodule_config "$name" branch master)
if ! test -z "$update"
then
update_module=$update
die "$(eval_gettext "Unable to find current revision in submodule path '\$sm_path'")"
fi
+ if test -n "$remote"
+ then
+ if test -z "$nofetch"
+ then
+ # Fetch remote before determining tracking $sha1
+ (clear_local_git_env; cd "$sm_path" && git-fetch) ||
+ die "$(eval_gettext "Unable to fetch in submodule path '\$sm_path'")"
+ fi
+ remote_name=$(clear_local_git_env; cd "$sm_path" && get_default_remote)
+ sha1=$(clear_local_git_env; cd "$sm_path" &&
+ git rev-parse --verify "${remote_name}/${branch}") ||
+ die "$(eval_gettext "Unable to find current ${remote_name}/${branch} revision in submodule path '\$sm_path'")"
+ fi
+
if test "$subsha1" != "$sha1" -o -n "$force"
then
subforce=$force
git_config_push_parameter((*argv)[1]);
(*argv)++;
(*argc)--;
+ } else if (!strcmp(cmd, "--literal-pathspecs")) {
+ setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "1", 1);
+ if (envchanged)
+ *envchanged = 1;
+ } else if (!strcmp(cmd, "--no-literal-pathspecs")) {
+ setenv(GIT_LITERAL_PATHSPECS_ENVIRONMENT, "0", 1);
+ if (envchanged)
+ *envchanged = 1;
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(git_usage_string);
gitdir = self.repo.gitpath
else:
gitdir = os.path.abspath(os.path.join(dirname, '.git'))
- path = os.path.abspath(os.path.join(dirname, 'git.marks'))
+ path = os.path.abspath(os.path.join(dirname, 'testgit.marks'))
if not os.path.exists(dirname):
os.makedirs(dirname)
sub sort_projects_list {
my ($projlist, $order) = @_;
- my @projects;
- my %order_info = (
- project => { key => 'path', type => 'str' },
- descr => { key => 'descr_long', type => 'str' },
- owner => { key => 'owner', type => 'str' },
- age => { key => 'age', type => 'num' }
- );
- my $oi = $order_info{$order};
- return @$projlist unless defined $oi;
- if ($oi->{'type'} eq 'str') {
- @projects = sort {$a->{$oi->{'key'}} cmp $b->{$oi->{'key'}}} @$projlist;
- } else {
- @projects = sort {$a->{$oi->{'key'}} <=> $b->{$oi->{'key'}}} @$projlist;
+ sub order_str {
+ my $key = shift;
+ return sub { $a->{$key} cmp $b->{$key} };
}
- return @projects;
+ sub order_num_then_undef {
+ my $key = shift;
+ return sub {
+ defined $a->{$key} ?
+ (defined $b->{$key} ? $a->{$key} <=> $b->{$key} : -1) :
+ (defined $b->{$key} ? 1 : 0)
+ };
+ }
+
+ my %orderings = (
+ project => order_str('path'),
+ descr => order_str('descr_long'),
+ owner => order_str('owner'),
+ age => order_num_then_undef('age'),
+ );
+
+ my $ordering = $orderings{$order};
+ return defined $ordering ? sort $ordering @$projlist : @$projlist;
}
# returns a hash of categories, containing the list of project
if (!graph)
return;
- while (!shown_commit_line) {
+ while (!shown_commit_line && !graph_is_commit_finished(graph)) {
shown_commit_line = graph_next_line(graph, &msgbuf);
fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
if (!shown_commit_line)
static char *xml_entities(const char *s)
{
struct strbuf buf = STRBUF_INIT;
- while (*s) {
- size_t len = strcspn(s, "\"<>&");
- strbuf_add(&buf, s, len);
- s += len;
- switch (*s) {
- case '"':
- strbuf_addstr(&buf, """);
- break;
- case '<':
- strbuf_addstr(&buf, "<");
- break;
- case '>':
- strbuf_addstr(&buf, ">");
- break;
- case '&':
- strbuf_addstr(&buf, "&");
- break;
- case 0:
- return strbuf_detach(&buf, NULL);
- }
- s++;
- }
+ strbuf_addstr_xml_quoted(&buf, s);
return strbuf_detach(&buf, NULL);
}
return 0;
if (!cert_auth.password) {
cert_auth.protocol = xstrdup("cert");
+ cert_auth.username = xstrdup("");
cert_auth.path = xstrdup(ssl_cert);
credential_fill(&cert_auth);
}
};
struct msg_data {
- char *data;
- int len;
+ struct strbuf data;
unsigned char flags;
};
return d;
}
-static void lf_to_crlf(struct msg_data *msg)
+static void lf_to_crlf(struct strbuf *msg)
{
+ size_t new_len;
char *new;
int i, j, lfnum = 0;
- if (msg->data[0] == '\n')
+ if (msg->buf[0] == '\n')
lfnum++;
for (i = 1; i < msg->len; i++) {
- if (msg->data[i - 1] != '\r' && msg->data[i] == '\n')
+ if (msg->buf[i - 1] != '\r' && msg->buf[i] == '\n')
lfnum++;
}
- new = xmalloc(msg->len + lfnum);
- if (msg->data[0] == '\n') {
+ new_len = msg->len + lfnum;
+ new = xmalloc(new_len + 1);
+ if (msg->buf[0] == '\n') {
new[0] = '\r';
new[1] = '\n';
i = 1;
j = 2;
} else {
- new[0] = msg->data[0];
+ new[0] = msg->buf[0];
i = 1;
j = 1;
}
for ( ; i < msg->len; i++) {
- if (msg->data[i] != '\n') {
- new[j++] = msg->data[i];
+ if (msg->buf[i] != '\n') {
+ new[j++] = msg->buf[i];
continue;
}
- if (msg->data[i - 1] != '\r')
+ if (msg->buf[i - 1] != '\r')
new[j++] = '\r';
/* otherwise it already had CR before */
new[j++] = '\n';
}
- msg->len += lfnum;
- free(msg->data);
- msg->data = new;
+ strbuf_attach(msg, new, new_len, new_len + 1);
}
-static int imap_store_msg(struct store *gctx, struct msg_data *data)
+/*
+ * Store msg to IMAP. Also detach and free the data from msg->data,
+ * leaving msg->data empty.
+ */
+static int imap_store_msg(struct store *gctx, struct msg_data *msg)
{
struct imap_store *ctx = (struct imap_store *)gctx;
struct imap *imap = ctx->imap;
int ret, d;
char flagstr[128];
- lf_to_crlf(data);
+ lf_to_crlf(&msg->data);
memset(&cb, 0, sizeof(cb));
- cb.dlen = data->len;
- cb.data = xmalloc(cb.dlen);
- memcpy(cb.data, data->data, data->len);
+ cb.dlen = msg->data.len;
+ cb.data = strbuf_detach(&msg->data, NULL);
d = 0;
- if (data->flags) {
- d = imap_make_flags(data->flags, flagstr);
+ if (msg->flags) {
+ d = imap_make_flags(msg->flags, flagstr);
flagstr[d++] = ' ';
}
flagstr[d] = 0;
return DRV_OK;
}
-static void encode_html_chars(struct strbuf *p)
-{
- int i;
- for (i = 0; i < p->len; i++) {
- if (p->buf[i] == '&')
- strbuf_splice(p, i, 1, "&", 5);
- if (p->buf[i] == '<')
- strbuf_splice(p, i, 1, "<", 4);
- if (p->buf[i] == '>')
- strbuf_splice(p, i, 1, ">", 4);
- if (p->buf[i] == '"')
- strbuf_splice(p, i, 1, """, 6);
- }
-}
-static void wrap_in_html(struct msg_data *msg)
+static void wrap_in_html(struct strbuf *msg)
{
struct strbuf buf = STRBUF_INIT;
- struct strbuf **lines;
- struct strbuf **p;
static char *content_type = "Content-Type: text/html;\n";
static char *pre_open = "<pre>\n";
static char *pre_close = "</pre>\n";
- int added_header = 0;
-
- strbuf_attach(&buf, msg->data, msg->len, msg->len);
- lines = strbuf_split(&buf, '\n');
- strbuf_release(&buf);
- for (p = lines; *p; p++) {
- if (! added_header) {
- if ((*p)->len == 1 && *((*p)->buf) == '\n') {
- strbuf_addstr(&buf, content_type);
- strbuf_addbuf(&buf, *p);
- strbuf_addstr(&buf, pre_open);
- added_header = 1;
- continue;
- }
- }
- else
- encode_html_chars(*p);
- strbuf_addbuf(&buf, *p);
- }
+ const char *body = strstr(msg->buf, "\n\n");
+
+ if (!body)
+ return; /* Headers but no body; no wrapping needed */
+
+ body += 2;
+
+ strbuf_add(&buf, msg->buf, body - msg->buf - 1);
+ strbuf_addstr(&buf, content_type);
+ strbuf_addch(&buf, '\n');
+ strbuf_addstr(&buf, pre_open);
+ strbuf_addstr_xml_quoted(&buf, body);
strbuf_addstr(&buf, pre_close);
- strbuf_list_free(lines);
- msg->len = buf.len;
- msg->data = strbuf_detach(&buf, NULL);
+
+ strbuf_release(msg);
+ *msg = buf;
}
#define CHUNKSIZE 0x1000
-static int read_message(FILE *f, struct msg_data *msg)
+static int read_message(FILE *f, struct strbuf *all_msgs)
{
- struct strbuf buf = STRBUF_INIT;
-
- memset(msg, 0, sizeof(*msg));
-
do {
- if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
+ if (strbuf_fread(all_msgs, CHUNKSIZE, f) <= 0)
break;
} while (!feof(f));
- msg->len = buf.len;
- msg->data = strbuf_detach(&buf, NULL);
- return msg->len;
+ return ferror(f) ? -1 : 0;
}
-static int count_messages(struct msg_data *msg)
+static int count_messages(struct strbuf *all_msgs)
{
int count = 0;
- char *p = msg->data;
+ char *p = all_msgs->buf;
while (1) {
if (!prefixcmp(p, "From ")) {
return count;
}
-static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
+/*
+ * Copy the next message from all_msgs, starting at offset *ofs, to
+ * msg. Update *ofs to the start of the following message. Return
+ * true iff a message was successfully copied.
+ */
+static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
{
char *p, *data;
+ size_t len;
- memset(msg, 0, sizeof *msg);
if (*ofs >= all_msgs->len)
return 0;
- data = &all_msgs->data[*ofs];
- msg->len = all_msgs->len - *ofs;
+ data = &all_msgs->buf[*ofs];
+ len = all_msgs->len - *ofs;
- if (msg->len < 5 || prefixcmp(data, "From "))
+ if (len < 5 || prefixcmp(data, "From "))
return 0;
p = strchr(data, '\n');
if (p) {
- p = &p[1];
- msg->len -= p-data;
- *ofs += p-data;
+ p++;
+ len -= p - data;
+ *ofs += p - data;
data = p;
}
p = strstr(data, "\nFrom ");
if (p)
- msg->len = &p[1] - data;
+ len = &p[1] - data;
- msg->data = xmemdupz(data, msg->len);
- *ofs += msg->len;
+ strbuf_add(msg, data, len);
+ *ofs += len;
return 1;
}
int main(int argc, char **argv)
{
- struct msg_data all_msgs, msg;
+ struct strbuf all_msgs = STRBUF_INIT;
+ struct msg_data msg = {STRBUF_INIT, 0};
struct store *ctx = NULL;
int ofs = 0;
int r;
}
/* read the messages */
- if (!read_message(stdin, &all_msgs)) {
+ if (read_message(stdin, &all_msgs)) {
+ fprintf(stderr, "error reading input\n");
+ return 1;
+ }
+
+ if (all_msgs.len == 0) {
fprintf(stderr, "nothing to send\n");
return 1;
}
ctx->name = imap_folder;
while (1) {
unsigned percent = n * 100 / total;
+
fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
- if (!split_msg(&all_msgs, &msg, &ofs))
+ if (!split_msg(&all_msgs, &msg.data, &ofs))
break;
if (server.use_html)
- wrap_in_html(&msg);
+ wrap_in_html(&msg.data);
r = imap_store_msg(ctx, &msg);
if (r != DRV_OK)
break;
ctx.preserve_subject = opt->preserve_subject;
ctx.reflog_info = opt->reflog_info;
ctx.fmt = opt->commit_format;
+ ctx.color = opt->diffopt.use_color;
pretty_print_commit(&ctx, commit, &msgbuf);
if (opt->add_signoff)
#endif
const char *git_mailmap_file;
+const char *git_mailmap_blob;
struct mailmap_info {
char *name;
return (*right == '\0' ? NULL : right);
}
-static int read_single_mailmap(struct string_list *map, const char *filename, char **repo_abbrev)
+static void read_mailmap_line(struct string_list *map, char *buffer,
+ char **repo_abbrev)
+{
+ char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
+ if (buffer[0] == '#') {
+ static const char abbrev[] = "# repo-abbrev:";
+ int abblen = sizeof(abbrev) - 1;
+ int len = strlen(buffer);
+
+ if (!repo_abbrev)
+ return;
+
+ if (len && buffer[len - 1] == '\n')
+ buffer[--len] = 0;
+ if (!strncmp(buffer, abbrev, abblen)) {
+ char *cp;
+
+ if (repo_abbrev)
+ free(*repo_abbrev);
+ *repo_abbrev = xmalloc(len);
+
+ for (cp = buffer + abblen; isspace(*cp); cp++)
+ ; /* nothing */
+ strcpy(*repo_abbrev, cp);
+ }
+ return;
+ }
+ if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
+ parse_name_and_email(name2, &name2, &email2, 1);
+
+ if (email1)
+ add_mapping(map, name1, email1, name2, email2);
+}
+
+static int read_mailmap_file(struct string_list *map, const char *filename,
+ char **repo_abbrev)
{
char buffer[1024];
- FILE *f = (filename == NULL ? NULL : fopen(filename, "r"));
+ FILE *f;
- if (f == NULL)
- return 1;
- while (fgets(buffer, sizeof(buffer), f) != NULL) {
- char *name1 = NULL, *email1 = NULL, *name2 = NULL, *email2 = NULL;
- if (buffer[0] == '#') {
- static const char abbrev[] = "# repo-abbrev:";
- int abblen = sizeof(abbrev) - 1;
- int len = strlen(buffer);
-
- if (!repo_abbrev)
- continue;
-
- if (len && buffer[len - 1] == '\n')
- buffer[--len] = 0;
- if (!strncmp(buffer, abbrev, abblen)) {
- char *cp;
-
- if (repo_abbrev)
- free(*repo_abbrev);
- *repo_abbrev = xmalloc(len);
-
- for (cp = buffer + abblen; isspace(*cp); cp++)
- ; /* nothing */
- strcpy(*repo_abbrev, cp);
- }
- continue;
- }
- if ((name2 = parse_name_and_email(buffer, &name1, &email1, 0)) != NULL)
- parse_name_and_email(name2, &name2, &email2, 1);
+ if (!filename)
+ return 0;
- if (email1)
- add_mapping(map, name1, email1, name2, email2);
+ f = fopen(filename, "r");
+ if (!f) {
+ if (errno == ENOENT)
+ return 0;
+ return error("unable to open mailmap at %s: %s",
+ filename, strerror(errno));
}
+
+ while (fgets(buffer, sizeof(buffer), f) != NULL)
+ read_mailmap_line(map, buffer, repo_abbrev);
fclose(f);
return 0;
}
+static void read_mailmap_buf(struct string_list *map,
+ const char *buf, unsigned long len,
+ char **repo_abbrev)
+{
+ while (len) {
+ const char *end = strchrnul(buf, '\n');
+ unsigned long linelen = end - buf + 1;
+ char *line = xmemdupz(buf, linelen);
+
+ read_mailmap_line(map, line, repo_abbrev);
+
+ free(line);
+ buf += linelen;
+ len -= linelen;
+ }
+}
+
+static int read_mailmap_blob(struct string_list *map,
+ const char *name,
+ char **repo_abbrev)
+{
+ unsigned char sha1[20];
+ char *buf;
+ unsigned long size;
+ enum object_type type;
+
+ if (!name)
+ return 0;
+ if (get_sha1(name, sha1) < 0)
+ return 0;
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return error("unable to read mailmap object at %s", name);
+ if (type != OBJ_BLOB)
+ return error("mailmap is not a blob: %s", name);
+
+ read_mailmap_buf(map, buf, size, repo_abbrev);
+
+ free(buf);
+ return 0;
+}
+
int read_mailmap(struct string_list *map, char **repo_abbrev)
{
+ int err = 0;
+
map->strdup_strings = 1;
- /* each failure returns 1, so >1 means both calls failed */
- return read_single_mailmap(map, ".mailmap", repo_abbrev) +
- read_single_mailmap(map, git_mailmap_file, repo_abbrev) > 1;
+
+ if (!git_mailmap_blob && is_bare_repository())
+ git_mailmap_blob = "HEAD:.mailmap";
+
+ err |= read_mailmap_file(map, ".mailmap", repo_abbrev);
+ err |= read_mailmap_blob(map, git_mailmap_blob, repo_abbrev);
+ err |= read_mailmap_file(map, git_mailmap_file, repo_abbrev);
+ return err;
}
void clear_mailmap(struct string_list *map)
diff_cmd () {
+ empty_file=
+
# p4merge does not like /dev/null
- rm_local=
- rm_remote=
if test "/dev/null" = "$LOCAL"
then
- LOCAL="./p4merge-dev-null.LOCAL.$$"
- >"$LOCAL"
- rm_local=true
+ LOCAL="$(create_empty_file)"
fi
if test "/dev/null" = "$REMOTE"
then
- REMOTE="./p4merge-dev-null.REMOTE.$$"
- >"$REMOTE"
- rm_remote=true
+ REMOTE="$(create_empty_file)"
fi
"$merge_tool_path" "$LOCAL" "$REMOTE"
- if test -n "$rm_local"
- then
- rm -f "$LOCAL"
- fi
- if test -n "$rm_remote"
+ if test -n "$empty_file"
then
- rm -f "$REMOTE"
+ rm -f "$empty_file"
fi
}
"$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" "$MERGED"
check_unchanged
}
+
+create_empty_file () {
+ empty_file="${TMPDIR:-/tmp}/git-difftool-p4merge-empty-file.$$"
+ >"$empty_file"
+
+ printf "$empty_file"
+}
return error("BUG: switch '%c' %s", opt->short_name, reason);
}
-int opterror(const struct option *opt, const char *reason, int flags)
-{
- if (flags & OPT_SHORT)
- return error("switch `%c' %s", opt->short_name, reason);
- if (flags & OPT_UNSET)
- return error("option `no-%s' %s", opt->long_name, reason);
- return error("option `%s' %s", opt->long_name, reason);
-}
-
static int get_arg(struct parse_opt_ctx_t *p, const struct option *opt,
int flags, const char **arg)
{
return usage_with_options_internal(ctx, usagestr, opts, 0, err);
}
+#undef opterror
+int opterror(const struct option *opt, const char *reason, int flags)
+{
+ if (flags & OPT_SHORT)
+ return error("switch `%c' %s", opt->short_name, reason);
+ if (flags & OPT_UNSET)
+ return error("option `no-%s' %s", opt->long_name, reason);
+ return error("option `%s' %s", opt->long_name, reason);
+}
extern int optbug(const struct option *opt, const char *reason);
extern int opterror(const struct option *opt, const char *reason, int flags);
+#ifdef __GNUC__
+#define opterror(o,r,f) (opterror((o),(r),(f)), -1)
+#endif
+
/*----- incremental advanced APIs -----*/
enum {
*/
#include "cache.h"
#include "strbuf.h"
+#include "string-list.h"
static char bad_path[] = "/bad-path/";
/*
* path = Canonical absolute path
- * prefix_list = Colon-separated list of absolute paths
+ * prefixes = string_list containing normalized, absolute paths without
+ * trailing slashes (except for the root directory, which is denoted by "/").
*
- * Determines, for each path in prefix_list, whether the "prefix" really
+ * Determines, for each path in prefixes, whether the "prefix"
* is an ancestor directory of path. Returns the length of the longest
* ancestor directory, excluding any trailing slashes, or -1 if no prefix
- * is an ancestor. (Note that this means 0 is returned if prefix_list is
- * "/".) "/foo" is not considered an ancestor of "/foobar". Directories
+ * is an ancestor. (Note that this means 0 is returned if prefixes is
+ * ["/"].) "/foo" is not considered an ancestor of "/foobar". Directories
* are not considered to be their own ancestors. path must be in a
* canonical form: empty components, or "." or ".." components are not
- * allowed. prefix_list may be null, which is like "".
+ * allowed.
*/
-int longest_ancestor_length(const char *path, const char *prefix_list)
+int longest_ancestor_length(const char *path, struct string_list *prefixes)
{
- char buf[PATH_MAX+1];
- const char *ceil, *colon;
- int len, max_len = -1;
+ int i, max_len = -1;
- if (prefix_list == NULL || !strcmp(path, "/"))
+ if (!strcmp(path, "/"))
return -1;
- for (colon = ceil = prefix_list; *colon; ceil = colon+1) {
- for (colon = ceil; *colon && *colon != PATH_SEP; colon++);
- len = colon - ceil;
- if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil))
- continue;
- strlcpy(buf, ceil, len+1);
- if (normalize_path_copy(buf, buf) < 0)
- continue;
- len = strlen(buf);
- if (len > 0 && buf[len-1] == '/')
- buf[--len] = '\0';
+ for (i = 0; i < prefixes->nr; i++) {
+ const char *ceil = prefixes->items[i].string;
+ int len = strlen(ceil);
- if (!strncmp(path, buf, len) &&
- path[len] == '/' &&
- len > max_len) {
+ if (len == 1 && ceil[0] == '/')
+ len = 0; /* root matches anything, with length 0 */
+ else if (!strncmp(path, ceil, len) && path[len] == '/')
+ ; /* match of length len */
+ else
+ continue; /* no match */
+
+ if (len > max_len)
max_len = len;
- }
}
return max_len;
command_output_pipe command_input_pipe command_close_pipe
command_bidi_pipe command_close_bidi_pipe
version exec_path html_path hash_object git_cmd_try
- remote_refs
+ remote_refs prompt
temp_acquire temp_release temp_reset temp_path);
sub html_path { command_oneline('--html-path') }
+=item prompt ( PROMPT , ISPASSWORD )
+
+Query user C<PROMPT> and return answer from user.
+
+Honours GIT_ASKPASS and SSH_ASKPASS environment variables for querying
+the user. If no *_ASKPASS variable is set or an error occoured,
+the terminal is tried as a fallback.
+If C<ISPASSWORD> is set and true, the terminal disables echo.
+
+=cut
+
+sub prompt {
+ my ($prompt, $isPassword) = @_;
+ my $ret;
+ if (exists $ENV{'GIT_ASKPASS'}) {
+ $ret = _prompt($ENV{'GIT_ASKPASS'}, $prompt);
+ }
+ if (!defined $ret && exists $ENV{'SSH_ASKPASS'}) {
+ $ret = _prompt($ENV{'SSH_ASKPASS'}, $prompt);
+ }
+ if (!defined $ret) {
+ print STDERR $prompt;
+ STDERR->flush;
+ if (defined $isPassword && $isPassword) {
+ require Term::ReadKey;
+ Term::ReadKey::ReadMode('noecho');
+ $ret = '';
+ while (defined(my $key = Term::ReadKey::ReadKey(0))) {
+ last if $key =~ /[\012\015]/; # \n\r
+ $ret .= $key;
+ }
+ Term::ReadKey::ReadMode('restore');
+ print STDERR "\n";
+ STDERR->flush;
+ } else {
+ chomp($ret = <STDIN>);
+ }
+ }
+ return $ret;
+}
+
+sub _prompt {
+ my ($askpass, $prompt) = @_;
+ return unless length $askpass;
+ $prompt =~ s/\n/ /g;
+ my $ret;
+ open my $fh, "-|", $askpass, $prompt or return;
+ $ret = <$fh>;
+ $ret =~ s/[\015\012]//g; # strip \r\n, chomp does not work on all systems (i.e. windows) as expected
+ close ($fh);
+ return $ret;
+}
=item repo_path ()
issuer_dname fingerprint);
my $choice;
prompt:
- print STDERR $may_save ?
+ my $options = $may_save ?
"(R)eject, accept (t)emporarily or accept (p)ermanently? " :
"(R)eject or accept (t)emporarily? ";
STDERR->flush;
- $choice = lc(substr(<STDIN> || 'R', 0, 1));
- if ($choice =~ /^t$/i) {
+ $choice = lc(substr(Git::prompt("Certificate problem.\n" . $options) || 'R', 0, 1));
+ if ($choice eq 't') {
$cred->may_save(undef);
- } elsif ($choice =~ /^r$/i) {
+ } elsif ($choice eq 'r') {
return -1;
- } elsif ($may_save && $choice =~ /^p$/i) {
+ } elsif ($may_save && $choice eq 'p') {
$cred->may_save($may_save);
} else {
goto prompt;
if (defined $_username) {
$username = $_username;
} else {
- print STDERR "Username: ";
- STDERR->flush;
- chomp($username = <STDIN>);
+ $username = Git::prompt("Username: ");
}
$cred->username($username);
$cred->may_save($may_save);
sub _read_password {
my ($prompt, $realm) = @_;
- my $password = '';
- if (exists $ENV{GIT_ASKPASS}) {
- open(PH, "-|", $ENV{GIT_ASKPASS}, $prompt);
- $password = <PH>;
- $password =~ s/[\012\015]//; # \n\r
- close(PH);
- } else {
- print STDERR $prompt;
- STDERR->flush;
- require Term::ReadKey;
- Term::ReadKey::ReadMode('noecho');
- while (defined(my $key = Term::ReadKey::ReadKey(0))) {
- last if $key =~ /[\012\015]/; # \n\r
- $password .= $key;
- }
- Term::ReadKey::ReadMode('restore');
- print STDERR "\n";
- STDERR->flush;
- }
+ my $password = Git::prompt($prompt, 1);
$password;
}
char *encoding;
char *out;
- if (!*output_encoding)
+ if (!output_encoding || !*output_encoding)
return NULL;
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
switch (placeholder[0]) {
case 'C':
if (placeholder[1] == '(') {
- const char *end = strchr(placeholder + 2, ')');
+ const char *begin = placeholder + 2;
+ const char *end = strchr(begin, ')');
char color[COLOR_MAXLEN];
+
if (!end)
return 0;
- color_parse_mem(placeholder + 2,
- end - (placeholder + 2),
+ if (!memcmp(begin, "auto,", 5)) {
+ if (!want_color(c->pretty_ctx->color))
+ return end - placeholder + 1;
+ begin += 5;
+ }
+ color_parse_mem(begin,
+ end - begin,
"--pretty format", color);
strbuf_addstr(sb, color);
return end - placeholder + 1;
const struct pretty_print_context *pretty_ctx)
{
struct format_commit_context context;
- static const char utf8[] = "UTF-8";
const char *output_enc = pretty_ctx->output_encoding;
memset(&context, 0, sizeof(context));
context.commit = commit;
context.pretty_ctx = pretty_ctx;
context.wrap_start = sb->len;
- context.message = commit->buffer;
- if (output_enc) {
- char *enc = get_header(commit, "encoding");
- if (strcmp(enc ? enc : utf8, output_enc)) {
- context.message = logmsg_reencode(commit, output_enc);
- if (!context.message)
- context.message = commit->buffer;
- }
- free(enc);
- }
+ context.message = logmsg_reencode(commit, output_enc);
+ if (!context.message)
+ context.message = commit->buffer;
strbuf_expand(sb, format, format_commit_item, &context);
rewrap_message_tail(sb, &context, 0, 0, 0);
static int repack_without_ref(const char *refname)
{
struct repack_without_ref_sb data;
- struct ref_dir *packed = get_packed_refs(get_ref_cache(NULL));
+ struct ref_cache *refs = get_ref_cache(NULL);
+ struct ref_dir *packed = get_packed_refs(refs);
if (find_ref(packed, refname) == NULL)
return 0;
data.refname = refname;
unable_to_lock_error(git_path("packed-refs"), errno);
return error("cannot delete '%s' from packed refs", refname);
}
+ clear_packed_ref_cache(refs);
+ packed = get_packed_refs(refs);
do_for_each_ref_in_dir(packed, 0, "", repack_without_ref_fn, 0, 0, &data);
return commit_lock_file(&packlock);
}
return 0;
}
+static inline int is_forwardable(struct ref* ref)
+{
+ struct object *o;
+
+ if (!prefixcmp(ref->name, "refs/tags/"))
+ return 0;
+
+ /* old object must be a commit */
+ o = parse_object(ref->old_sha1);
+ if (!o || o->type != OBJ_COMMIT)
+ return 0;
+
+ /* new object must be commit-ish */
+ o = deref_tag(parse_object(ref->new_sha1), NULL, 0);
+ if (!o || o->type != OBJ_COMMIT)
+ return 0;
+
+ return 1;
+}
+
void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
int force_update)
{
struct ref *ref;
for (ref = remote_refs; ref; ref = ref->next) {
+ int force_ref_update = ref->force || force_update;
+
if (ref->peer_ref)
hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
else if (!send_mirror)
continue;
}
- /* This part determines what can overwrite what.
- * The rules are:
+ /*
+ * The below logic determines whether an individual
+ * refspec A:B can be pushed. The push will succeed
+ * if any of the following are true:
*
- * (0) you can always use --force or +A:B notation to
- * selectively force individual ref pairs.
+ * (1) the remote reference B does not exist
*
- * (1) if the old thing does not exist, it is OK.
+ * (2) the remote reference B is being removed (i.e.,
+ * pushing :B where no source is specified)
*
- * (2) if you do not have the old thing, you are not allowed
- * to overwrite it; you would not know what you are losing
- * otherwise.
+ * (3) the update meets all fast-forwarding criteria:
*
- * (3) if both new and old are commit-ish, and new is a
- * descendant of old, it is OK.
+ * (a) the destination is not under refs/tags/
+ * (b) the old is a commit
+ * (c) the new is a descendant of the old
*
- * (4) regardless of all of the above, removing :B is
- * always allowed.
+ * NOTE: We must actually have the old object in
+ * order to overwrite it in the remote reference,
+ * and the new object must be commit-ish. These are
+ * implied by (b) and (c) respectively.
+ *
+ * (4) it is forced using the +A:B notation, or by
+ * passing the --force argument
*/
- ref->nonfastforward =
- !ref->deletion &&
- !is_null_sha1(ref->old_sha1) &&
- (!has_sha1_file(ref->old_sha1)
- || !ref_newer(ref->new_sha1, ref->old_sha1));
+ ref->not_forwardable = !is_forwardable(ref);
- if (ref->nonfastforward && !ref->force && !force_update) {
- ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
- continue;
+ ref->update =
+ !ref->deletion &&
+ !is_null_sha1(ref->old_sha1);
+
+ if (ref->update) {
+ ref->nonfastforward =
+ !has_sha1_file(ref->old_sha1)
+ || !ref_newer(ref->new_sha1, ref->old_sha1);
+
+ if (ref->not_forwardable) {
+ ref->requires_force = 1;
+ if (!force_ref_update) {
+ ref->status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ continue;
+ }
+ } else if (ref->nonfastforward) {
+ ref->requires_force = 1;
+ if (!force_ref_update) {
+ ref->status = REF_STATUS_REJECT_NONFASTFORWARD;
+ continue;
+ }
+ }
}
}
}
return refname_match(branch->merge[i]->src, refname, ref_fetch_rules);
}
+static int ignore_symref_update(const char *refname)
+{
+ unsigned char sha1[20];
+ int flag;
+
+ if (!resolve_ref_unsafe(refname, sha1, 0, &flag))
+ return 0; /* non-existing refs are OK */
+ return (flag & REF_ISSYMREF);
+}
+
static struct ref *get_expanded_map(const struct ref *remote_refs,
const struct refspec *refspec)
{
if (strchr(ref->name, '^'))
continue; /* a dereference item */
if (match_name_with_pattern(refspec->src, ref->name,
- refspec->dst, &expn_name)) {
+ refspec->dst, &expn_name) &&
+ !ignore_symref_update(expn_name)) {
struct ref *cpy = copy_ref(ref);
cpy->peer_ref = alloc_ref(expn_name);
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
-static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
+static int wait_or_whine(pid_t pid, const char *argv0)
{
int status, code = -1;
pid_t waiting;
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
code = WTERMSIG(status);
- error("%s died of signal %d", argv0, code);
+ if (code != SIGINT && code != SIGQUIT)
+ error("%s died of signal %d", argv0, code);
/*
* This return value is chosen so that code & 0xff
* mimics the exit code that a POSIX shell would report for
* At this point we know that fork() succeeded, but execvp()
* failed. Errors have been reported to our stderr.
*/
- wait_or_whine(cmd->pid, cmd->argv[0],
- cmd->silent_exec_failure);
+ wait_or_whine(cmd->pid, cmd->argv[0]);
failed_errno = errno;
cmd->pid = -1;
}
int finish_command(struct child_process *cmd)
{
- return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
+ return wait_or_whine(cmd->pid, cmd->argv[0]);
}
int run_command(struct child_process *cmd)
int finish_async(struct async *async)
{
#ifdef NO_PTHREADS
- return wait_or_whine(async->pid, "child process", 0);
+ return wait_or_whine(async->pid, "child process");
#else
void *ret = (void *)(intptr_t)(-1);
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
#include "cache.h"
#include "dir.h"
+#include "string-list.h"
static int inside_git_dir = -1;
static int inside_work_tree = -1;
return buf.st_dev;
}
+/*
+ * A "string_list_each_func_t" function that canonicalizes an entry
+ * from GIT_CEILING_DIRECTORIES using real_path_if_valid(), or
+ * discards it if unusable.
+ */
+static int canonicalize_ceiling_entry(struct string_list_item *item,
+ void *unused)
+{
+ char *ceil = item->string;
+ const char *real_path;
+
+ if (!*ceil || !is_absolute_path(ceil))
+ return 0;
+ real_path = real_path_if_valid(ceil);
+ if (!real_path)
+ return 0;
+ free(item->string);
+ item->string = xstrdup(real_path);
+ return 1;
+}
+
/*
* We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called.
static const char *setup_git_directory_gently_1(int *nongit_ok)
{
const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
+ struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
static char cwd[PATH_MAX+1];
const char *gitdirenv, *ret;
char *gitfile;
- int len, offset, offset_parent, ceil_offset;
+ int len, offset, offset_parent, ceil_offset = -1;
dev_t current_device = 0;
int one_filesystem = 1;
if (gitdirenv)
return setup_explicit_git_dir(gitdirenv, cwd, len, nongit_ok);
- ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
+ if (env_ceiling_dirs) {
+ string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
+ filter_string_list(&ceiling_dirs, 0,
+ canonicalize_ceiling_entry, NULL);
+ ceil_offset = longest_ancestor_length(cwd, &ceiling_dirs);
+ string_list_clear(&ceiling_dirs, 0);
+ }
+
if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
ceil_offset = 1;
strbuf_complete_line(out);
}
+void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
+{
+ while (*s) {
+ size_t len = strcspn(s, "\"<>&");
+ strbuf_add(buf, s, len);
+ s += len;
+ switch (*s) {
+ case '"':
+ strbuf_addstr(buf, """);
+ break;
+ case '<':
+ strbuf_addstr(buf, "<");
+ break;
+ case '>':
+ strbuf_addstr(buf, ">");
+ break;
+ case '&':
+ strbuf_addstr(buf, "&");
+ break;
+ case 0:
+ return;
+ }
+ s++;
+ }
+}
+
static int is_rfc3986_reserved(char ch)
{
switch (ch) {
extern void strbuf_add_lines(struct strbuf *sb, const char *prefix, const char *buf, size_t size);
+/*
+ * Append s to sb, with the characters '<', '>', '&' and '"' converted
+ * into XML entities.
+ */
+extern void strbuf_addstr_xml_quoted(struct strbuf *sb, const char *s);
+
static inline void strbuf_complete_line(struct strbuf *sb)
{
if (sb->len && sb->buf[sb->len - 1] != '\n')
filter_string_list(list, free_util, item_is_not_empty, NULL);
}
-char *string_list_longest_prefix(const struct string_list *prefixes,
- const char *string)
-{
- int i, max_len = -1;
- char *retval = NULL;
-
- for (i = 0; i < prefixes->nr; i++) {
- char *prefix = prefixes->items[i].string;
- if (!prefixcmp(string, prefix)) {
- int len = strlen(prefix);
- if (len > max_len) {
- retval = prefix;
- max_len = len;
- }
- }
- }
-
- return retval;
-}
-
void string_list_clear(struct string_list *list, int free_util)
{
if (list->items) {
*/
void string_list_remove_empty_items(struct string_list *list, int free_util);
-/*
- * Return the longest string in prefixes that is a prefix (in the
- * sense of prefixcmp()) of string, or NULL if no such prefix exists.
- * This function does not require the string_list to be sorted (it
- * does a linear search).
- */
-char *string_list_longest_prefix(const struct string_list *prefixes, const char *string);
-
-
/* Use these functions only on sorted lists: */
int string_list_has_string(const struct string_list *list, const char *string);
int string_list_find_insert_index(const struct string_list *list, const char *string,
false
'
-test_expect_success 'pretend we have fixed a known breakage (run in sub test-lib)' "
- mkdir passing-todo &&
- (cd passing-todo &&
- cat >passing-todo.sh <<-EOF &&
- #!$SHELL_PATH
-
- test_description='A passing TODO test
+run_sub_test_lib_test () {
+ name="$1" descr="$2" # stdin is the body of the test code
+ mkdir "$name" &&
+ (
+ cd "$name" &&
+ cat >"$name.sh" <<-EOF &&
+ #!$SHELL_PATH
+
+ test_description='$descr (run in sub test-lib)
+
+ This is run in a sub test-lib so that we do not get incorrect
+ passing metrics
+ '
+
+ # Point to the t/test-lib.sh, which isn't in ../ as usual
+ . "\$TEST_DIRECTORY"/test-lib.sh
+ EOF
+ cat >>"$name.sh" &&
+ chmod +x "$name.sh" &&
+ export TEST_DIRECTORY &&
+ ./"$name.sh" >out 2>err
+ )
+}
- This is run in a sub test-lib so that we do not get incorrect
- passing metrics
- '
+check_sub_test_lib_test () {
+ name="$1" # stdin is the expected output from the test
+ (
+ cd "$name" &&
+ ! test -s err &&
+ sed -e 's/^> //' -e 's/Z$//' >expect &&
+ test_cmp expect out
+ )
+}
+
+test_expect_success 'pretend we have a fully passing test suite' "
+ run_sub_test_lib_test full-pass '3 passing tests' <<-\\EOF &&
+ for i in 1 2 3
+ do
+ test_expect_success \"passing test #\$i\" 'true'
+ done
+ test_done
+ EOF
+ check_sub_test_lib_test full-pass <<-\\EOF
+ > ok 1 - passing test #1
+ > ok 2 - passing test #2
+ > ok 3 - passing test #3
+ > # passed all 3 test(s)
+ > 1..3
+ EOF
+"
- # Point to the t/test-lib.sh, which isn't in ../ as usual
- TEST_DIRECTORY=\"$TEST_DIRECTORY\"
- . \"\$TEST_DIRECTORY\"/test-lib.sh
+test_expect_success 'pretend we have a partially passing test suite' "
+ test_must_fail run_sub_test_lib_test \
+ partial-pass '2/3 tests passing' <<-\\EOF &&
+ test_expect_success 'passing test #1' 'true'
+ test_expect_success 'failing test #2' 'false'
+ test_expect_success 'passing test #3' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test partial-pass <<-\\EOF
+ > ok 1 - passing test #1
+ > not ok 2 - failing test #2
+ # false
+ > ok 3 - passing test #3
+ > # failed 1 among 3 test(s)
+ > 1..3
+ EOF
+"
- test_expect_failure 'pretend we have fixed a known breakage' '
- :
- '
+test_expect_success 'pretend we have a known breakage' "
+ run_sub_test_lib_test failing-todo 'A failing TODO test' <<-\\EOF &&
+ test_expect_success 'passing test' 'true'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_done
+ EOF
+ check_sub_test_lib_test failing-todo <<-\\EOF
+ > ok 1 - passing test
+ > not ok 2 - pretend we have a known breakage # TODO known breakage
+ > # still have 1 known breakage(s)
+ > # passed all remaining 1 test(s)
+ > 1..2
+ EOF
+"
+test_expect_success 'pretend we have fixed a known breakage' "
+ run_sub_test_lib_test passing-todo 'A passing TODO test' <<-\\EOF &&
+ test_expect_failure 'pretend we have fixed a known breakage' 'true'
test_done
EOF
- chmod +x passing-todo.sh &&
- ./passing-todo.sh >out 2>err &&
- ! test -s err &&
- sed -e 's/^> //' >expect <<-\\EOF &&
- > ok 1 - pretend we have fixed a known breakage # TODO known breakage
- > # fixed 1 known breakage(s)
- > # passed all 1 test(s)
+ check_sub_test_lib_test passing-todo <<-\\EOF
+ > ok 1 - pretend we have fixed a known breakage # TODO known breakage vanished
+ > # 1 known breakage(s) vanished; please update test(s)
> 1..1
EOF
- test_cmp expect out)
"
+
+test_expect_success 'pretend we have fixed one of two known breakages (run in sub test-lib)' "
+ run_sub_test_lib_test partially-passing-todos \
+ '2 TODO tests, one passing' <<-\\EOF &&
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_expect_success 'pretend we have a passing test' 'true'
+ test_expect_failure 'pretend we have fixed another known breakage' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test partially-passing-todos <<-\\EOF
+ > not ok 1 - pretend we have a known breakage # TODO known breakage
+ > ok 2 - pretend we have a passing test
+ > ok 3 - pretend we have fixed another known breakage # TODO known breakage vanished
+ > # 1 known breakage(s) vanished; please update test(s)
+ > # still have 1 known breakage(s)
+ > # passed all remaining 1 test(s)
+ > 1..3
+ EOF
+"
+
+test_expect_success 'pretend we have a pass, fail, and known breakage' "
+ test_must_fail run_sub_test_lib_test \
+ mixed-results1 'mixed results #1' <<-\\EOF &&
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'failing test' 'false'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_done
+ EOF
+ check_sub_test_lib_test mixed-results1 <<-\\EOF
+ > ok 1 - passing test
+ > not ok 2 - failing test
+ > # false
+ > not ok 3 - pretend we have a known breakage # TODO known breakage
+ > # still have 1 known breakage(s)
+ > # failed 1 among remaining 2 test(s)
+ > 1..3
+ EOF
+"
+
+test_expect_success 'pretend we have a mix of all possible results' "
+ test_must_fail run_sub_test_lib_test \
+ mixed-results2 'mixed results #2' <<-\\EOF &&
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'passing test' 'true'
+ test_expect_success 'failing test' 'false'
+ test_expect_success 'failing test' 'false'
+ test_expect_success 'failing test' 'false'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_expect_failure 'pretend we have a known breakage' 'false'
+ test_expect_failure 'pretend we have fixed a known breakage' 'true'
+ test_done
+ EOF
+ check_sub_test_lib_test mixed-results2 <<-\\EOF
+ > ok 1 - passing test
+ > ok 2 - passing test
+ > ok 3 - passing test
+ > ok 4 - passing test
+ > not ok 5 - failing test
+ > # false
+ > not ok 6 - failing test
+ > # false
+ > not ok 7 - failing test
+ > # false
+ > not ok 8 - pretend we have a known breakage # TODO known breakage
+ > not ok 9 - pretend we have a known breakage # TODO known breakage
+ > ok 10 - pretend we have fixed a known breakage # TODO known breakage vanished
+ > # 1 known breakage(s) vanished; please update test(s)
+ > # still have 2 known breakage(s)
+ > # failed 3 among remaining 7 test(s)
+ > 1..10
+ EOF
+"
+
test_set_prereq HAVEIT
haveit=no
test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
fi
test_expect_success 'tests clean up even on failures' "
- mkdir failing-cleanup &&
- (
- cd failing-cleanup &&
-
- cat >failing-cleanup.sh <<-EOF &&
- #!$SHELL_PATH
-
- test_description='Failing tests with cleanup commands'
-
- # Point to the t/test-lib.sh, which isn't in ../ as usual
- TEST_DIRECTORY=\"$TEST_DIRECTORY\"
- . \"\$TEST_DIRECTORY\"/test-lib.sh
-
+ test_must_fail run_sub_test_lib_test \
+ failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF &&
test_expect_success 'tests clean up even after a failure' '
touch clean-after-failure &&
test_when_finished rm clean-after-failure &&
test_when_finished \"(exit 2)\"
'
test_done
-
EOF
-
- chmod +x failing-cleanup.sh &&
- test_must_fail ./failing-cleanup.sh >out 2>err &&
- ! test -s err &&
- ! test -f \"trash directory.failing-cleanup/clean-after-failure\" &&
- sed -e 's/Z$//' -e 's/^> //' >expect <<-\\EOF &&
- > not ok - 1 tests clean up even after a failure
+ check_sub_test_lib_test failing-cleanup <<-\\EOF
+ > not ok 1 - tests clean up even after a failure
> # Z
> # touch clean-after-failure &&
> # test_when_finished rm clean-after-failure &&
> # (exit 1)
> # Z
- > not ok - 2 failure to clean up causes the test to fail
+ > not ok 2 - failure to clean up causes the test to fail
> # Z
> # test_when_finished \"(exit 2)\"
> # Z
> # failed 2 among 2 test(s)
> 1..2
EOF
- test_cmp expect out
- )
"
################################################################
norm_path /d1/.../d2 /d1/.../d2 POSIX
norm_path /d1/..././../d2 /d1/d2 POSIX
-ancestor / "" -1
ancestor / / -1
-ancestor /foo "" -1
-ancestor /foo : -1
-ancestor /foo ::. -1
-ancestor /foo ::..:: -1
ancestor /foo / 0
ancestor /foo /fo -1
ancestor /foo /foo -1
-ancestor /foo /foo/ -1
ancestor /foo /bar -1
-ancestor /foo /bar/ -1
ancestor /foo /foo/bar -1
-ancestor /foo /foo:/bar/ -1
-ancestor /foo /foo/:/bar/ -1
-ancestor /foo /foo::/bar/ -1
-ancestor /foo /:/foo:/bar/ 0
-ancestor /foo /foo:/:/bar/ 0
-ancestor /foo /:/bar/:/foo 0
-ancestor /foo/bar "" -1
+ancestor /foo /foo:/bar -1
+ancestor /foo /:/foo:/bar 0
+ancestor /foo /foo:/:/bar 0
+ancestor /foo /:/bar:/foo 0
ancestor /foo/bar / 0
ancestor /foo/bar /fo -1
-ancestor /foo/bar foo -1
ancestor /foo/bar /foo 4
-ancestor /foo/bar /foo/ 4
ancestor /foo/bar /foo/ba -1
ancestor /foo/bar /:/fo 0
ancestor /foo/bar /foo:/foo/ba 4
ancestor /foo/bar /bar -1
-ancestor /foo/bar /bar/ -1
-ancestor /foo/bar /fo: -1
-ancestor /foo/bar :/fo -1
-ancestor /foo/bar /foo:/bar/ 4
-ancestor /foo/bar /:/foo:/bar/ 4
-ancestor /foo/bar /foo:/:/bar/ 4
-ancestor /foo/bar /:/bar/:/fo 0
-ancestor /foo/bar /:/bar/ 0
-ancestor /foo/bar .:/foo/. 4
-ancestor /foo/bar .:/foo/.:.: 4
-ancestor /foo/bar /foo/./:.:/bar 4
-ancestor /foo/bar .:/bar -1
+ancestor /foo/bar /fo -1
+ancestor /foo/bar /foo:/bar 4
+ancestor /foo/bar /:/foo:/bar 4
+ancestor /foo/bar /foo:/:/bar 4
+ancestor /foo/bar /:/bar:/fo 0
+ancestor /foo/bar /:/bar 0
+ancestor /foo/bar /foo 4
+ancestor /foo/bar /foo:/bar 4
+ancestor /foo/bar /bar -1
test_expect_success 'strip_path_suffix' '
test c:/msysgit = $(test-path-utils strip_path_suffix \
"
}
-test_longest_prefix () {
- test "$(test-string-list longest_prefix "$1" "$2")" = "$3"
-}
-
-test_no_longest_prefix () {
- test_must_fail test-string-list longest_prefix "$1" "$2"
-}
-
test_split "foo:bar:baz" ":" "-1" <<EOF
3
[0]: "foo"
test a:b:c = "$(test-string-list remove_duplicates a:a:a:b:b:b:c:c:c)"
'
-test_expect_success "test longest_prefix" '
- test_no_longest_prefix - '' &&
- test_no_longest_prefix - x &&
- test_longest_prefix "" x "" &&
- test_longest_prefix x x x &&
- test_longest_prefix "" foo "" &&
- test_longest_prefix : foo "" &&
- test_longest_prefix f foo f &&
- test_longest_prefix foo foobar foo &&
- test_longest_prefix foo foo foo &&
- test_no_longest_prefix bar foo &&
- test_no_longest_prefix bar:bar foo &&
- test_no_longest_prefix foobar foo &&
- test_longest_prefix foo:bar foo foo &&
- test_longest_prefix foo:bar bar bar &&
- test_longest_prefix foo::bar foo foo &&
- test_longest_prefix foo:foobar foo foo &&
- test_longest_prefix foobar:foo foo foo &&
- test_longest_prefix foo: bar "" &&
- test_longest_prefix :foo bar ""
-'
-
test_done
test_expect_success 'alias expansion' '
(
- git config alias.ss status &&
+ git config alias.test-status-alias status &&
cd dir &&
git status &&
- git ss
+ git test-status-alias
)
'
test_expect_success NOT_MINGW '!alias expansion' '
pwd >expect &&
(
- git config alias.test !pwd &&
+ git config alias.test-alias-directory !pwd &&
cd dir &&
- git test >../actual
+ git test-alias-directory >../actual
) &&
test_cmp expect actual
'
test_expect_success 'GIT_PREFIX for !alias' '
printf "dir/" >expect &&
(
- git config alias.test "!sh -c \"printf \$GIT_PREFIX\"" &&
+ git config alias.test-alias-directory "!sh -c \"printf \$GIT_PREFIX\"" &&
cd dir &&
- git test >../actual
+ git test-alias-directory >../actual
) &&
test_cmp expect actual
'
)
'
+test_expect_success 'fsck notices "." and ".." in trees' '
+ (
+ git init dots &&
+ cd dots &&
+ blob=$(echo foo | git hash-object -w --stdin) &&
+ tab=$(printf "\\t") &&
+ git mktree <<-EOF &&
+ 100644 blob $blob$tab.
+ 100644 blob $blob$tab..
+ EOF
+ git fsck 2>out &&
+ cat out &&
+ grep "warning.*\\." out
+ )
+'
+
+test_expect_success 'fsck notices ".git" in trees' '
+ (
+ git init dotgit &&
+ cd dotgit &&
+ blob=$(echo foo | git hash-object -w --stdin) &&
+ tab=$(printf "\\t") &&
+ git mktree <<-EOF &&
+ 100644 blob $blob$tab.git
+ EOF
+ git fsck 2>out &&
+ cat out &&
+ grep "warning.*\\.git" out
+ )
+'
+
test_done
git commit -a -m all
'
+test_expect_success 'cache-tree invalidates i-t-a paths' '
+ git reset --hard &&
+ mkdir dir &&
+ : >dir/foo &&
+ git add dir/foo &&
+ git commit -m foo &&
+
+ : >dir/bar &&
+ git add -N dir/bar &&
+ git diff --cached --name-only >actual &&
+ echo dir/bar >expect &&
+ test_cmp expect actual &&
+
+ git write-tree >/dev/null &&
+
+ git diff --cached --name-only >actual &&
+ echo dir/bar >expect &&
+ test_cmp expect actual
+'
+
test_done
test_cmp expect out
'
+test_expect_success 'shortlog should add newline when input line matches wraplen' '
+ cat >expect <<\EOF &&
+A U Thor (2):
+ bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb
+ aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa
+
+EOF
+ git shortlog -w >out <<\EOF &&
+commit 0000000000000000000000000000000000000001
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:14:13 2005 -0700
+
+ aaaaaaaaaaaaaaaaaaaaaa: aaaaaa aaaaaaaaaa aaaa aaaaaaaa aa aaaa aa aaa
+
+commit 0000000000000000000000000000000000000002
+Author: A U Thor <author@example.com>
+Date: Thu Apr 7 15:14:13 2005 -0700
+
+ bbbbbbbbbbbbbbbbbb: bbbbbbbb bbb bbbb bbbbbbb bb bbbb bbb bbbbb bbbbbb
+
+EOF
+ test_cmp expect out
+'
+
iconvfromutf8toiso88591() {
printf "%s" "$*" | iconv -f UTF-8 -t ISO8859-1
}
test_cmp expect actual
'
+test_expect_success 'log --raw --graph -m with merge' '
+ git log --raw --graph --oneline -m master | head -n 500 >actual &&
+ grep "initial" actual
+'
+
+test_expect_success 'diff-tree --graph' '
+ git diff-tree --graph master^ | head -n 500 >actual &&
+ grep "one" actual
+'
+
cat > expect <<\EOF
* commit master
|\ Merge: A B
test_cmp expect actual
'
+test_expect_success 'setup mailmap blob tests' '
+ git checkout -b map &&
+ test_when_finished "git checkout master" &&
+ cat >just-bugs <<-\EOF &&
+ Blob Guy <bugs@company.xx>
+ EOF
+ cat >both <<-\EOF &&
+ Blob Guy <author@example.com>
+ Blob Guy <bugs@company.xx>
+ EOF
+ git add just-bugs both &&
+ git commit -m "my mailmaps" &&
+ echo "Repo Guy <author@example.com>" >.mailmap &&
+ echo "Internal Guy <author@example.com>" >internal.map
+'
+
+test_expect_success 'mailmap.blob set' '
+ cat >expect <<-\EOF &&
+ Blob Guy (1):
+ second
+
+ Repo Guy (1):
+ initial
+
+ EOF
+ git -c mailmap.blob=map:just-bugs shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob overrides .mailmap' '
+ cat >expect <<-\EOF &&
+ Blob Guy (2):
+ initial
+ second
+
+ EOF
+ git -c mailmap.blob=map:both shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.file overrides mailmap.blob' '
+ cat >expect <<-\EOF &&
+ Blob Guy (1):
+ second
+
+ Internal Guy (1):
+ initial
+
+ EOF
+ git \
+ -c mailmap.blob=map:both \
+ -c mailmap.file=internal.map \
+ shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob can be missing' '
+ cat >expect <<-\EOF &&
+ Repo Guy (1):
+ initial
+
+ nick1 (1):
+ second
+
+ EOF
+ git -c mailmap.blob=map:nonexistent shortlog HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'mailmap.blob defaults to off in non-bare repo' '
+ git init non-bare &&
+ (
+ cd non-bare &&
+ test_commit one .mailmap "Fake Name <author@example.com>" &&
+ echo " 1 Fake Name" >expect &&
+ git shortlog -ns HEAD >actual &&
+ test_cmp expect actual &&
+ rm .mailmap &&
+ echo " 1 A U Thor" >expect &&
+ git shortlog -ns HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'mailmap.blob defaults to HEAD:.mailmap in bare repo' '
+ git clone --bare non-bare bare &&
+ (
+ cd bare &&
+ echo " 1 Fake Name" >expect &&
+ git shortlog -ns HEAD >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success 'cleanup after mailmap.blob tests' '
+ rm -f .mailmap
+'
+
# Extended mailmap configurations should give us the following output for shortlog
cat >expect <<\EOF
A U Thor <author@example.com> (1):
--- /dev/null
+#!/bin/sh
+
+test_description='git archive attribute pattern tests'
+
+. ./test-lib.sh
+
+test_expect_exists() {
+ test_expect_success " $1 exists" "test -e $1"
+}
+
+test_expect_missing() {
+ test_expect_success " $1 does not exist" "test ! -e $1"
+}
+
+test_expect_success 'setup' '
+ echo ignored >ignored &&
+ echo ignored export-ignore >>.git/info/attributes &&
+ git add ignored &&
+
+ mkdir not-ignored-dir &&
+ echo ignored-in-tree >not-ignored-dir/ignored &&
+ echo not-ignored-in-tree >not-ignored-dir/ignored-only-if-dir &&
+ git add not-ignored-dir &&
+
+ mkdir ignored-only-if-dir &&
+ echo ignored by ignored dir >ignored-only-if-dir/ignored-by-ignored-dir &&
+ echo ignored-only-if-dir/ export-ignore >>.git/info/attributes &&
+ git add ignored-only-if-dir &&
+
+
+ mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir &&
+ echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir &&
+ git add one-level-lower &&
+
+ git commit -m. &&
+
+ git clone --bare . bare &&
+ cp .git/info/attributes bare/info/attributes
+'
+
+test_expect_success 'git archive' '
+ git archive HEAD >archive.tar &&
+ (mkdir archive && cd archive && "$TAR" xf -) <archive.tar
+'
+
+test_expect_missing archive/ignored
+test_expect_missing archive/not-ignored-dir/ignored
+test_expect_exists archive/not-ignored-dir/ignored-only-if-dir
+test_expect_exists archive/not-ignored-dir/
+test_expect_missing archive/ignored-only-if-dir/
+test_expect_missing archive/ignored-ony-if-dir/ignored-by-ignored-dir
+test_expect_exists archive/one-level-lower/
+test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/
+test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir
+
+
+test_done
git branch -D frotz
fi &&
git tag -f frotz &&
- git push testrepo frotz &&
+ git push -f testrepo frotz &&
check_push_result $the_commit tags/frotz &&
check_push_result $the_first_commit heads/frotz
)
'
+test_expect_success 'push requires --force to update lightweight tag' '
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (
+ cd child1 &&
+ git tag Tag &&
+ git push ../child2 Tag &&
+ git push ../child2 Tag &&
+ >file1 &&
+ git add file1 &&
+ git commit -m "file1" &&
+ git tag -f Tag &&
+ test_must_fail git push ../child2 Tag &&
+ git push --force ../child2 Tag &&
+ git tag -f Tag &&
+ test_must_fail git push ../child2 Tag HEAD~ &&
+ git push --force ../child2 Tag
+ )
+'
+
+test_expect_success 'push requires --force to update annotated tag' '
+ mk_test heads/master &&
+ mk_child child1 &&
+ mk_child child2 &&
+ (
+ cd child1 &&
+ git tag -a -m "message 1" Tag &&
+ git push ../child2 Tag:refs/tmp/Tag &&
+ git push ../child2 Tag:refs/tmp/Tag &&
+ >file1 &&
+ git add file1 &&
+ git commit -m "file1" &&
+ git tag -f -a -m "message 2" Tag &&
+ test_must_fail git push ../child2 Tag:refs/tmp/Tag &&
+ git push --force ../child2 Tag:refs/tmp/Tag &&
+ git tag -f -a -m "message 3" Tag HEAD~ &&
+ test_must_fail git push ../child2 Tag:refs/tmp/Tag &&
+ git push --force ../child2 Tag:refs/tmp/Tag
+ )
+'
+
test_expect_success 'push --porcelain' '
mk_empty &&
echo >.git/foo "To testrepo" &&
--- /dev/null
+#!/bin/sh
+
+test_description='avoiding conflicting update thru symref aliasing'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit one &&
+ git clone . src &&
+ git clone src dst1 &&
+ git clone src dst2 &&
+ test_commit two &&
+ ( cd src && git pull )
+'
+
+test_expect_success 'push' '
+ (
+ cd src &&
+ git push ../dst1 "refs/remotes/*:refs/remotes/*"
+ ) &&
+ git ls-remote src "refs/remotes/*" >expect &&
+ git ls-remote dst1 "refs/remotes/*" >actual &&
+ test_cmp expect actual &&
+ ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect &&
+ ( cd dst1 && git symbolic-ref refs/remotes/origin/HEAD ) >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch' '
+ (
+ cd dst2 &&
+ git fetch ../src "refs/remotes/*:refs/remotes/*"
+ ) &&
+ git ls-remote src "refs/remotes/*" >expect &&
+ git ls-remote dst2 "refs/remotes/*" >actual &&
+ test_cmp expect actual &&
+ ( cd src && git symbolic-ref refs/remotes/origin/HEAD ) >expect &&
+ ( cd dst2 && git symbolic-ref refs/remotes/origin/HEAD ) >actual &&
+ test_cmp expect actual
+'
+
+test_done
+++ /dev/null
-#!/bin/sh
-#
-# Copyright (c) 2010 Sverre Rabbelier
-#
-
-test_description='Test remote-helper import and export commands'
-
-. ./test-lib.sh
-
-if ! test_have_prereq PYTHON ; then
- skip_all='skipping git-remote-hg tests, python not available'
- test_done
-fi
-
-"$PYTHON_PATH" -c '
-import sys
-if sys.hexversion < 0x02040000:
- sys.exit(1)
-' || {
- skip_all='skipping git-remote-hg tests, python version < 2.4'
- test_done
-}
-
-compare_refs() {
- git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
- git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
- test_cmp expect actual
-}
-
-test_expect_success 'setup repository' '
- git init --bare server/.git &&
- git clone server public &&
- (cd public &&
- echo content >file &&
- git add file &&
- git commit -m one &&
- git push origin master)
-'
-
-test_expect_success 'cloning from local repo' '
- git clone "testgit::${PWD}/server" localclone &&
- test_cmp public/file localclone/file
-'
-
-test_expect_success 'cloning from remote repo' '
- git clone "testgit::file://${PWD}/server" clone &&
- test_cmp public/file clone/file
-'
-
-test_expect_success 'create new commit on remote' '
- (cd public &&
- echo content >>file &&
- git commit -a -m two &&
- git push)
-'
-
-test_expect_success 'pulling from local repo' '
- (cd localclone && git pull) &&
- test_cmp public/file localclone/file
-'
-
-test_expect_success 'pulling from remote remote' '
- (cd clone && git pull) &&
- test_cmp public/file clone/file
-'
-
-test_expect_success 'pushing to local repo' '
- (cd localclone &&
- echo content >>file &&
- git commit -a -m three &&
- git push) &&
- compare_refs localclone HEAD server HEAD
-'
-
-# Generally, skip this test. It demonstrates a now-fixed race in
-# git-remote-testgit, but is too slow to leave in for general use.
-: test_expect_success 'racily pushing to local repo' '
- test_when_finished "rm -rf server2 localclone2" &&
- cp -R server server2 &&
- git clone "testgit::${PWD}/server2" localclone2 &&
- (cd localclone2 &&
- echo content >>file &&
- git commit -a -m three &&
- GIT_REMOTE_TESTGIT_SLEEPY=2 git push) &&
- compare_refs localclone2 HEAD server2 HEAD
-'
-
-test_expect_success 'synch with changes from localclone' '
- (cd clone &&
- git pull)
-'
-
-test_expect_success 'pushing remote local repo' '
- (cd clone &&
- echo content >>file &&
- git commit -a -m four &&
- git push) &&
- compare_refs clone HEAD server HEAD
-'
-
-test_expect_success 'fetch new branch' '
- (cd public &&
- git checkout -b new &&
- echo content >>file &&
- git commit -a -m five &&
- git push origin new
- ) &&
- (cd localclone &&
- git fetch origin new
- ) &&
- compare_refs public HEAD localclone FETCH_HEAD
-'
-
-test_expect_success 'fetch multiple branches' '
- (cd localclone &&
- git fetch
- ) &&
- compare_refs server master localclone refs/remotes/origin/master &&
- compare_refs server new localclone refs/remotes/origin/new
-'
-
-test_expect_success 'push when remote has extra refs' '
- (cd clone &&
- echo content >>file &&
- git commit -a -m six &&
- git push
- ) &&
- compare_refs clone master server master
-'
-
-test_expect_success 'push new branch by name' '
- (cd clone &&
- git checkout -b new-name &&
- echo content >>file &&
- git commit -a -m seven &&
- git push origin new-name
- ) &&
- compare_refs clone HEAD server refs/heads/new-name
-'
-
-test_expect_failure 'push new branch with old:new refspec' '
- (cd clone &&
- git push origin new-name:new-refspec
- ) &&
- compare_refs clone HEAD server refs/heads/new-refspec
-'
-
-test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2010 Sverre Rabbelier
+#
+
+test_description='Test python remote-helper framework'
+
+. ./test-lib.sh
+
+if ! test_have_prereq PYTHON ; then
+ skip_all='skipping python remote-helper tests, python not available'
+ test_done
+fi
+
+"$PYTHON_PATH" -c '
+import sys
+if sys.hexversion < 0x02040000:
+ sys.exit(1)
+' || {
+ skip_all='skipping python remote-helper tests, python version < 2.4'
+ test_done
+}
+
+compare_refs() {
+ git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
+ git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup repository' '
+ git init --bare server/.git &&
+ git clone server public &&
+ (cd public &&
+ echo content >file &&
+ git add file &&
+ git commit -m one &&
+ git push origin master)
+'
+
+test_expect_success 'cloning from local repo' '
+ git clone "testpy::${PWD}/server" localclone &&
+ test_cmp public/file localclone/file
+'
+
+test_expect_success 'cloning from remote repo' '
+ git clone "testpy::file://${PWD}/server" clone &&
+ test_cmp public/file clone/file
+'
+
+test_expect_success 'create new commit on remote' '
+ (cd public &&
+ echo content >>file &&
+ git commit -a -m two &&
+ git push)
+'
+
+test_expect_success 'pulling from local repo' '
+ (cd localclone && git pull) &&
+ test_cmp public/file localclone/file
+'
+
+test_expect_success 'pulling from remote remote' '
+ (cd clone && git pull) &&
+ test_cmp public/file clone/file
+'
+
+test_expect_success 'pushing to local repo' '
+ (cd localclone &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git push) &&
+ compare_refs localclone HEAD server HEAD
+'
+
+# Generally, skip this test. It demonstrates a now-fixed race in
+# git-remote-testpy, but is too slow to leave in for general use.
+: test_expect_success 'racily pushing to local repo' '
+ test_when_finished "rm -rf server2 localclone2" &&
+ cp -R server server2 &&
+ git clone "testpy::${PWD}/server2" localclone2 &&
+ (cd localclone2 &&
+ echo content >>file &&
+ git commit -a -m three &&
+ GIT_REMOTE_TESTGIT_SLEEPY=2 git push) &&
+ compare_refs localclone2 HEAD server2 HEAD
+'
+
+test_expect_success 'synch with changes from localclone' '
+ (cd clone &&
+ git pull)
+'
+
+test_expect_success 'pushing remote local repo' '
+ (cd clone &&
+ echo content >>file &&
+ git commit -a -m four &&
+ git push) &&
+ compare_refs clone HEAD server HEAD
+'
+
+test_expect_success 'fetch new branch' '
+ (cd public &&
+ git checkout -b new &&
+ echo content >>file &&
+ git commit -a -m five &&
+ git push origin new
+ ) &&
+ (cd localclone &&
+ git fetch origin new
+ ) &&
+ compare_refs public HEAD localclone FETCH_HEAD
+'
+
+test_expect_success 'fetch multiple branches' '
+ (cd localclone &&
+ git fetch
+ ) &&
+ compare_refs server master localclone refs/remotes/origin/master &&
+ compare_refs server new localclone refs/remotes/origin/new
+'
+
+test_expect_success 'push when remote has extra refs' '
+ (cd clone &&
+ echo content >>file &&
+ git commit -a -m six &&
+ git push
+ ) &&
+ compare_refs clone master server master
+'
+
+test_expect_success 'push new branch by name' '
+ (cd clone &&
+ git checkout -b new-name &&
+ echo content >>file &&
+ git commit -a -m seven &&
+ git push origin new-name
+ ) &&
+ compare_refs clone HEAD server refs/heads/new-name
+'
+
+test_expect_failure 'push new branch with old:new refspec' '
+ (cd clone &&
+ git push origin new-name:new-refspec
+ ) &&
+ compare_refs clone HEAD server refs/heads/new-refspec
+'
+
+test_done
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2010 Sverre Rabbelier
+#
+
+test_description='Test remote-helper import and export commands'
+
+. ./test-lib.sh
+
+if ! type "${BASH-bash}" >/dev/null 2>&1; then
+ skip_all='skipping remote-testgit tests, bash not available'
+ test_done
+fi
+
+compare_refs() {
+ git --git-dir="$1/.git" rev-parse --verify $2 >expect &&
+ git --git-dir="$3/.git" rev-parse --verify $4 >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'setup repository' '
+ git init server &&
+ (cd server &&
+ echo content >file &&
+ git add file &&
+ git commit -m one)
+'
+
+test_expect_success 'cloning from local repo' '
+ git clone "testgit::${PWD}/server" local &&
+ test_cmp server/file local/file
+'
+
+test_expect_success 'create new commit on remote' '
+ (cd server &&
+ echo content >>file &&
+ git commit -a -m two)
+'
+
+test_expect_success 'pulling from local repo' '
+ (cd local && git pull) &&
+ test_cmp server/file local/file
+'
+
+test_expect_success 'pushing to local repo' '
+ (cd local &&
+ echo content >>file &&
+ git commit -a -m three &&
+ git push) &&
+ compare_refs local HEAD server HEAD
+'
+
+test_expect_success 'fetch new branch' '
+ (cd server &&
+ git reset --hard &&
+ git checkout -b new &&
+ echo content >>file &&
+ git commit -a -m five
+ ) &&
+ (cd local &&
+ git fetch origin new
+ ) &&
+ compare_refs server HEAD local FETCH_HEAD
+'
+
+test_expect_success 'fetch multiple branches' '
+ (cd local &&
+ git fetch
+ ) &&
+ compare_refs server master local refs/remotes/origin/master &&
+ compare_refs server new local refs/remotes/origin/new
+'
+
+test_expect_success 'push when remote has extra refs' '
+ (cd local &&
+ git reset --hard origin/master &&
+ echo content >>file &&
+ git commit -a -m six &&
+ git push
+ ) &&
+ compare_refs local master server master
+'
+
+test_expect_success 'push new branch by name' '
+ (cd local &&
+ git checkout -b new-name &&
+ echo content >>file &&
+ git commit -a -m seven &&
+ git push origin new-name
+ ) &&
+ compare_refs local HEAD server refs/heads/new-name
+'
+
+test_expect_failure 'push new branch with old:new refspec' '
+ (cd local &&
+ git push origin new-name:new-refspec
+ ) &&
+ compare_refs local HEAD server refs/heads/new-refspec
+'
+
+test_expect_success 'cloning without refspec' '
+ GIT_REMOTE_TESTGIT_REFSPEC="" \
+ git clone "testgit::${PWD}/server" local2 &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'pulling without refspecs' '
+ (cd local2 &&
+ git reset --hard &&
+ GIT_REMOTE_TESTGIT_REFSPEC="" git pull) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_failure 'pushing without refspecs' '
+ test_when_finished "(cd local2 && git reset --hard origin)" &&
+ (cd local2 &&
+ echo content >>file &&
+ git commit -a -m ten &&
+ GIT_REMOTE_TESTGIT_REFSPEC="" git push) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'pulling with straight refspec' '
+ (cd local2 &&
+ GIT_REMOTE_TESTGIT_REFSPEC="*:*" git pull) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_failure 'pushing with straight refspec' '
+ test_when_finished "(cd local2 && git reset --hard origin)" &&
+ (cd local2 &&
+ echo content >>file &&
+ git commit -a -m eleven &&
+ GIT_REMOTE_TESTGIT_REFSPEC="*:*" git push) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'pulling without marks' '
+ (cd local2 &&
+ GIT_REMOTE_TESTGIT_NO_MARKS=1 git pull) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_failure 'pushing without marks' '
+ test_when_finished "(cd local2 && git reset --hard origin)" &&
+ (cd local2 &&
+ echo content >>file &&
+ git commit -a -m twelve &&
+ GIT_REMOTE_TESTGIT_NO_MARKS=1 git push) &&
+ compare_refs local2 HEAD server HEAD
+'
+
+test_expect_success 'push all with existing object' '
+ (cd local &&
+ git branch dup2 master &&
+ git push origin --all
+ ) &&
+ compare_refs local dup2 server dup2
+'
+
+test_expect_success 'push ref with existing object' '
+ (cd local &&
+ git branch dup master &&
+ git push origin dup
+ ) &&
+ compare_refs local dup server dup
+'
+
+test_done
test_description='git rev-list --pretty=format test'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
test_tick
test_expect_success 'setup' '
'
# usage: test_format name format_string <expected_output
-test_format() {
+test_format () {
cat >expect.$1
test_expect_success "format $1" "
-git rev-list --pretty=format:'$2' master >output.$1 &&
-test_cmp expect.$1 output.$1
-"
+ git rev-list --pretty=format:'$2' master >output.$1 &&
+ test_cmp expect.$1 output.$1
+ "
+}
+
+# Feed to --format to provide predictable colored sequences.
+AUTO_COLOR='%C(auto,red)foo%C(auto,reset)'
+has_color () {
+ printf '\033[31mfoo\033[m\n' >expect &&
+ test_cmp expect "$1"
+}
+
+has_no_color () {
+ echo foo >expect &&
+ test_cmp expect "$1"
}
test_format percent %%h <<'EOF'
\e[1;31;43mfoo\e[m
EOF
+test_expect_success '%C(auto) does not enable color by default' '
+ git log --format=$AUTO_COLOR -1 >actual &&
+ has_no_color actual
+'
+
+test_expect_success '%C(auto) enables colors for color.diff' '
+ git -c color.diff=always log --format=$AUTO_COLOR -1 >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto) enables colors for color.ui' '
+ git -c color.ui=always log --format=$AUTO_COLOR -1 >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto) respects --color' '
+ git log --format=$AUTO_COLOR -1 --color >actual &&
+ has_color actual
+'
+
+test_expect_success '%C(auto) respects --no-color' '
+ git -c color.ui=always log --format=$AUTO_COLOR -1 --no-color >actual &&
+ has_no_color actual
+'
+
+test_expect_success TTY '%C(auto) respects --color=auto (stdout is tty)' '
+ (
+ TERM=vt100 && export TERM &&
+ test_terminal \
+ git log --format=$AUTO_COLOR -1 --color=auto >actual &&
+ has_color actual
+ )
+'
+
+test_expect_success '%C(auto) respects --color=auto (stdout not tty)' '
+ (
+ TERM=vt100 && export TERM &&
+ git log --format=$AUTO_COLOR -1 --color=auto >actual &&
+ has_no_color actual
+ )
+'
+
cat >commit-msg <<'EOF'
Test printing of complex bodies
--- /dev/null
+#!/bin/sh
+
+test_description='test globbing (and noglob) of pathspec limiting'
+. ./test-lib.sh
+
+test_expect_success 'create commits with glob characters' '
+ test_commit unrelated bar &&
+ test_commit vanilla foo &&
+ # insert file "f*" in the commit, but in a way that avoids
+ # the name "f*" in the worktree, because it is not allowed
+ # on Windows (the tests below do not depend on the presence
+ # of the file in the worktree)
+ git update-index --add --cacheinfo 100644 "$(git rev-parse HEAD:foo)" "f*" &&
+ test_tick &&
+ git commit -m star &&
+ test_commit bracket "f[o][o]"
+'
+
+test_expect_success 'vanilla pathspec matches literally' '
+ echo vanilla >expect &&
+ git log --format=%s -- foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'star pathspec globs' '
+ cat >expect <<-\EOF &&
+ bracket
+ star
+ vanilla
+ EOF
+ git log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'bracket pathspec globs and matches literal brackets' '
+ cat >expect <<-\EOF &&
+ bracket
+ vanilla
+ EOF
+ git log --format=%s -- "f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (vanilla)' '
+ echo vanilla >expect &&
+ git --literal-pathspecs log --format=%s -- foo >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (star)' '
+ echo star >expect &&
+ git --literal-pathspecs log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob option matches literally (bracket)' '
+ echo bracket >expect &&
+ git --literal-pathspecs log --format=%s -- "f[o][o]" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'no-glob environment variable works' '
+ echo star >expect &&
+ GIT_LITERAL_PATHSPECS=1 git log --format=%s -- "f*" >actual &&
+ test_cmp expect actual
+'
+
+test_done
(
cd addtest &&
git submodule add -b initial "$submodurl" submod-branch &&
+ test "initial" = "$(git config -f .gitmodules submodule.submod-branch.branch)" &&
git submodule init
) &&
)
'
+test_expect_success 'submodule update --remote should fetch upstream changes' '
+ (cd submodule &&
+ echo line4 >> file &&
+ git add file &&
+ test_tick &&
+ git commit -m "upstream line4"
+ ) &&
+ (cd super &&
+ git submodule update --remote --force submodule &&
+ cd submodule &&
+ test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline)"
+ )
+'
+
+test_expect_success 'local config should override .gitmodules branch' '
+ (cd submodule &&
+ git checkout -b test-branch &&
+ echo line5 >> file &&
+ git add file &&
+ test_tick &&
+ git commit -m "upstream line5" &&
+ git checkout master
+ ) &&
+ (cd super &&
+ git config submodule.submodule.branch test-branch &&
+ git submodule update --remote --force submodule &&
+ cd submodule &&
+ test "$(git log -1 --oneline)" = "$(GIT_DIR=../../submodule/.git git log -1 --oneline test-branch)"
+ )
+'
+
test_expect_success 'submodule update --rebase staying on master' '
(cd super/submodule &&
git checkout master
test_debug '
git --version
- which git
- which svnrdump
+ type git
+ type svnrdump
'
test_expect_success REMOTE_SVN 'simple fetch' '
(
cd limit-by-paths &&
git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
- test_cmp output expected
+ test_cmp expected output
)
'
(
cd limit-by-paths &&
git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
- test_cmp output expected
+ test_cmp expected output
)
'
(
cd limit-by-paths &&
git fast-export master~2..master~1 > output &&
- test_cmp output expected
+ test_cmp expected output
)
'
)
'
+test_expect_success 'test bidirectionality' '
+ >marks-cur &&
+ >marks-new &&
+ git init marks-test &&
+ git fast-export --export-marks=marks-cur --import-marks=marks-cur --branches | \
+ git --git-dir=marks-test/.git fast-import --export-marks=marks-new --import-marks=marks-new &&
+ (cd marks-test &&
+ git reset --hard &&
+ echo Wohlauf > file &&
+ git commit -a -m "back in time") &&
+ git --git-dir=marks-test/.git fast-export --export-marks=marks-new --import-marks=marks-new --branches | \
+ git fast-import --export-marks=marks-cur --import-marks=marks-cur
+'
+
+cat > expected << EOF
+blob
+mark :13
+data 5
+bump
+
+commit refs/heads/master
+mark :14
+author A U Thor <author@example.com> 1112912773 -0700
+committer C O Mitter <committer@example.com> 1112912773 -0700
+data 5
+bump
+from :12
+M 100644 :13 file
+
+EOF
+
+test_expect_success 'avoid uninteresting refs' '
+ > tmp-marks &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks master > /dev/null &&
+ git tag v1.0 &&
+ git branch uninteresting &&
+ echo bump > file &&
+ git commit -a -m bump &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks ^uninteresting ^v1.0 master > actual &&
+ test_cmp expected actual
+'
+
+cat > expected << EOF
+reset refs/heads/master
+from :14
+
+EOF
+
+test_expect_success 'refs are updated even if no commits need to be exported' '
+ > tmp-marks &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks master > /dev/null &&
+ git fast-export --import-marks=tmp-marks \
+ --export-marks=tmp-marks master > actual &&
+ test_cmp expected actual
+'
+
test_done
line7
line8
EOF
- cp filek fileko &&
- sed -i "s/Revision/Revision: do not scrub me/" fileko
- cp fileko file_text &&
- sed -i "s/Id/Id: do not scrub me/" file_text
+ sed "s/Revision/Revision: do not scrub me/" <filek >fileko &&
+ sed "s/Id/Id: do not scrub me/" <fileko >file_text &&
p4 add -t text+k filek &&
p4 submit -d "filek" &&
p4 add -t text+ko fileko &&
(
cd "$git" &&
git config git-p4.skipSubmitEdit true &&
- sed -i "s/^line7/line7 edit/" filek &&
+ sed "s/^line7/line7 edit/" <filek >filek.tmp &&
+ mv -f filek.tmp filek &&
git commit -m "filek line7 edit" filek &&
git p4 submit &&
scrub_k_check filek
cd "$git" &&
git config git-p4.skipSubmitEdit true &&
git config git-p4.attemptRCSCleanup true &&
- sed -i "s/^line4/line4 edit/" filek &&
+ sed "s/^line4/line4 edit/" <filek >filek.tmp &&
+ mv -f filek.tmp filek &&
git commit -m "filek line4 edit" filek &&
git p4 submit &&
scrub_k_check filek
cd "$git" &&
git config git-p4.skipSubmitEdit true &&
git config git-p4.attemptRCSCleanup true &&
- sed -i "/Revision/d" filek &&
+ sed "/Revision/d" <filek >filek.tmp &&
+ mv -f filek.tmp filek &&
git commit -m "filek remove Revision line" filek &&
git p4 submit &&
scrub_k_check filek
cd "$git" &&
git config git-p4.skipSubmitEdit true &&
git config git-p4.attemptRCSCleanup true &&
- sed -i "s/^line4/line4 edit/" fileko &&
+ sed "s/^line4/line4 edit/" <fileko >fileko.tmp &&
+ mv -f fileko.tmp fileko &&
git commit -m "fileko line4 edit" fileko &&
git p4 submit &&
scrub_ko_check fileko &&
cd "$git" &&
git config git-p4.skipSubmitEdit true &&
git config git-p4.attemptRCSCleanup true &&
- sed -i "s/^line4/line4 edit/" file_text &&
+ sed "s/^line4/line4 edit/" <file_text >file_text.tmp &&
+ mv -f file_text.tmp file_text &&
git commit -m "file_text line4 edit" file_text &&
(
cd "$cli" &&
p4 open file_text &&
- sed -i "s/^line5/line5 p4 edit/" file_text &&
+ sed "s/^line5/line5 p4 edit/" <file_text >file_text.tmp &&
+ mv -f file_text.tmp file_text &&
p4 submit -d "file5 p4 edit"
) &&
echo s | test_expect_code 1 git p4 submit &&
error)
tput bold; tput setaf 1;; # bold red
skip)
- tput bold; tput setaf 2;; # bold green
+ tput setaf 4;; # blue
+ warn)
+ tput setaf 3;; # brown/yellow
pass)
- tput setaf 2;; # green
+ tput setaf 2;; # green
info)
- tput setaf 3;; # brown
+ tput setaf 6;; # cyan
*)
test -n "$quiet" && return;;
esac
test_failure_ () {
test_failure=$(($test_failure + 1))
- say_color error "not ok - $test_count $1"
+ say_color error "not ok $test_count - $1"
shift
echo "$@" | sed -e 's/^/# /'
test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
test_known_broken_ok_ () {
test_fixed=$(($test_fixed+1))
- say_color "" "ok $test_count - $@ # TODO known breakage"
+ say_color error "ok $test_count - $@ # TODO known breakage vanished"
}
test_known_broken_failure_ () {
test_broken=$(($test_broken+1))
- say_color skip "not ok $test_count - $@ # TODO known breakage"
+ say_color warn "not ok $test_count - $@ # TODO known breakage"
}
test_debug () {
if test "$test_fixed" != 0
then
- say_color pass "# fixed $test_fixed known breakage(s)"
+ say_color error "# $test_fixed known breakage(s) vanished; please update test(s)"
fi
if test "$test_broken" != 0
then
- say_color error "# still have $test_broken known breakage(s)"
- msg="remaining $(($test_count-$test_broken)) test(s)"
+ say_color warn "# still have $test_broken known breakage(s)"
+ fi
+ if test "$test_broken" != 0 || test "$test_fixed" != 0
+ then
+ test_remaining=$(( $test_count - $test_broken - $test_fixed ))
+ msg="remaining $test_remaining test(s)"
else
+ test_remaining=$test_count
msg="$test_count test(s)"
fi
case "$test_failure" in
if test $test_external_has_tap -eq 0
then
- if test $test_count -gt 0
+ if test $test_remaining -gt 0
then
say_color pass "# passed all $msg"
fi
do
case "$this_test" in
$skp)
- say_color skip >&3 "skipping test $this_test altogether"
+ say_color info >&3 "skipping test $this_test altogether"
skip_all="skip all tests in $this_test"
test_done
esac
#include "cache.h"
+#include "string-list.h"
+
+/*
+ * A "string_list_each_func_t" function that normalizes an entry from
+ * GIT_CEILING_DIRECTORIES. If the path is unusable for some reason,
+ * die with an explanation.
+ */
+static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
+{
+ const char *ceil = item->string;
+ int len = strlen(ceil);
+ char buf[PATH_MAX+1];
+
+ if (len == 0)
+ die("Empty path is not supported");
+ if (len > PATH_MAX)
+ die("Path \"%s\" is too long", ceil);
+ if (!is_absolute_path(ceil))
+ die("Path \"%s\" is not absolute", ceil);
+ if (normalize_path_copy(buf, ceil) < 0)
+ die("Path \"%s\" could not be normalized", ceil);
+ len = strlen(buf);
+ if (len > 1 && buf[len-1] == '/')
+ die("Normalized path \"%s\" ended with slash", buf);
+ free(item->string);
+ item->string = xstrdup(buf);
+ return 1;
+}
int main(int argc, char **argv)
{
}
if (argc == 4 && !strcmp(argv[1], "longest_ancestor_length")) {
- int len = longest_ancestor_length(argv[2], argv[3]);
+ int len;
+ struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
+ char *path = xstrdup(argv[2]);
+
+ /*
+ * We have to normalize the arguments because under
+ * Windows, bash mangles arguments that look like
+ * absolute POSIX paths or colon-separate lists of
+ * absolute POSIX paths into DOS paths (e.g.,
+ * "/foo:/foo/bar" might be converted to
+ * "D:\Src\msysgit\foo;D:\Src\msysgit\foo\bar"),
+ * whereas longest_ancestor_length() requires paths
+ * that use forward slashes.
+ */
+ if (normalize_path_copy(path, path))
+ die("Path \"%s\" could not be normalized", argv[2]);
+ string_list_split(&ceiling_dirs, argv[3], PATH_SEP, -1);
+ filter_string_list(&ceiling_dirs, 0,
+ normalize_ceiling_entry, NULL);
+ len = longest_ancestor_length(path, &ceiling_dirs);
+ string_list_clear(&ceiling_dirs, 0);
+ free(path);
printf("%d\n", len);
return 0;
}
return 0;
}
- if (argc == 4 && !strcmp(argv[1], "longest_prefix")) {
- /* arguments: <colon-separated-prefixes>|- <string> */
- struct string_list prefixes = STRING_LIST_INIT_DUP;
- int retval;
- const char *prefix_string = argv[2];
- const char *string = argv[3];
- const char *match;
-
- parse_string_list(&prefixes, prefix_string);
- match = string_list_longest_prefix(&prefixes, string);
- if (match) {
- printf("%s\n", match);
- retval = 0;
- }
- else
- retval = 1;
- string_list_clear(&prefixes, 0);
- return retval;
- }
-
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;
free(msg);
msg = NULL;
}
+ else if (!strcmp(msg, "already exists")) {
+ status = REF_STATUS_REJECT_ALREADY_EXISTS;
+ free(msg);
+ msg = NULL;
+ }
}
if (*ref)
/* Check for statuses set by set_ref_status_for_push() */
switch (ref->status) {
case REF_STATUS_REJECT_NONFASTFORWARD:
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
case REF_STATUS_UPTODATE:
continue;
default:
const char *msg;
strcpy(quickref, status_abbrev(ref->old_sha1));
- if (ref->nonfastforward) {
+ if (ref->requires_force) {
strcat(quickref, "...");
type = '+';
msg = "forced update";
print_ref_status('!', "[rejected]", ref, ref->peer_ref,
"non-fast-forward", porcelain);
break;
+ case REF_STATUS_REJECT_ALREADY_EXISTS:
+ print_ref_status('!', "[rejected]", ref, ref->peer_ref,
+ "already exists", porcelain);
+ break;
case REF_STATUS_REMOTE_REJECT:
print_ref_status('!', "[remote rejected]", ref,
ref->deletion ? NULL : ref->peer_ref,
}
void transport_print_push_status(const char *dest, struct ref *refs,
- int verbose, int porcelain, int *nonfastforward)
+ int verbose, int porcelain, unsigned int *reject_reasons)
{
struct ref *ref;
int n = 0;
if (ref->status == REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
- *nonfastforward = 0;
+ *reject_reasons = 0;
for (ref = refs; ref; ref = ref->next) {
if (ref->status != REF_STATUS_NONE &&
ref->status != REF_STATUS_UPTODATE &&
ref->status != REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
- if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD &&
- *nonfastforward != NON_FF_HEAD) {
+ if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) {
if (!strcmp(head, ref->name))
- *nonfastforward = NON_FF_HEAD;
+ *reject_reasons |= REJECT_NON_FF_HEAD;
else
- *nonfastforward = NON_FF_OTHER;
+ *reject_reasons |= REJECT_NON_FF_OTHER;
+ } else if (ref->status == REF_STATUS_REJECT_ALREADY_EXISTS) {
+ *reject_reasons |= REJECT_ALREADY_EXISTS;
}
}
}
int transport_push(struct transport *transport,
int refspec_nr, const char **refspec, int flags,
- int *nonfastforward)
+ unsigned int *reject_reasons)
{
- *nonfastforward = 0;
+ *reject_reasons = 0;
transport_verify_remote_names(refspec_nr, refspec);
if (transport->push) {
if (!quiet || err)
transport_print_push_status(transport->url, remote_refs,
verbose | porcelain, porcelain,
- nonfastforward);
+ reject_reasons);
if (flags & TRANSPORT_PUSH_SET_UPSTREAM)
set_upstreams(transport, remote_refs, pretend);
void transport_set_verbosity(struct transport *transport, int verbosity,
int force_progress);
-#define NON_FF_HEAD 1
-#define NON_FF_OTHER 2
+#define REJECT_NON_FF_HEAD 0x01
+#define REJECT_NON_FF_OTHER 0x02
+#define REJECT_ALREADY_EXISTS 0x04
+
int transport_push(struct transport *connection,
int refspec_nr, const char **refspec, int flags,
- int * nonfastforward);
+ unsigned int * reject_reasons);
const struct ref *transport_get_remote_refs(struct transport *transport);
int transport_refs_pushed(struct ref *ref);
void transport_print_push_status(const char *dest, struct ref *refs,
- int verbose, int porcelain, int *nonfastforward);
+ int verbose, int porcelain, unsigned int *reject_reasons);
typedef void alternate_ref_fn(const struct ref *, void *);
extern void for_each_alternate_ref(alternate_ref_fn, void *);
return 0;
}
+/*
+ * Perform matching on the leading non-wildcard part of
+ * pathspec. item->nowildcard_len must be greater than zero. Return
+ * non-zero if base is matched.
+ */
+static int match_wildcard_base(const struct pathspec_item *item,
+ const char *base, int baselen,
+ int *matched)
+{
+ const char *match = item->match;
+ /* the wildcard part is not considered in this function */
+ int matchlen = item->nowildcard_len;
+
+ if (baselen) {
+ int dirlen;
+ /*
+ * Return early if base is longer than the
+ * non-wildcard part but it does not match.
+ */
+ if (baselen >= matchlen) {
+ *matched = matchlen;
+ return !strncmp(base, match, matchlen);
+ }
+
+ dirlen = matchlen;
+ while (dirlen && match[dirlen - 1] != '/')
+ dirlen--;
+
+ /*
+ * Return early if base is shorter than the
+ * non-wildcard part but it does not match. Note that
+ * base ends with '/' so we are sure it really matches
+ * directory
+ */
+ if (strncmp(base, match, baselen))
+ return 0;
+ *matched = baselen;
+ } else
+ *matched = 0;
+ /*
+ * we could have checked entry against the non-wildcard part
+ * that is not in base and does similar never_interesting
+ * optimization as in match_entry. For now just be happy with
+ * base comparison.
+ */
+ return entry_interesting;
+}
+
/*
* Is a tree entry interesting given the pathspec we have?
*
const struct pathspec_item *item = ps->items+i;
const char *match = item->match;
const char *base_str = base->buf + base_offset;
- int matchlen = item->len;
+ int matchlen = item->len, matched = 0;
if (baselen >= matchlen) {
/* If it doesn't match, move along... */
&never_interesting))
return entry_interesting;
- if (item->use_wildcard) {
- if (!fnmatch(match + baselen, entry->path, 0))
+ if (item->nowildcard_len < item->len) {
+ if (!git_fnmatch(match + baselen, entry->path,
+ item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
+ item->nowildcard_len - baselen))
return entry_interesting;
/*
}
match_wildcards:
- if (!item->use_wildcard)
+ if (item->nowildcard_len == item->len)
continue;
+ if (item->nowildcard_len &&
+ !match_wildcard_base(item, base_str, baselen, &matched))
+ return entry_not_interesting;
+
/*
* Concatenate base and entry->path into one and do
* fnmatch() on it.
+ *
+ * While we could avoid concatenation in certain cases
+ * [1], which saves a memcpy and potentially a
+ * realloc, it turns out not worth it. Measurement on
+ * linux-2.6 does not show any clear improvements,
+ * partly because of the nowildcard_len optimization
+ * in git_fnmatch(). Avoid micro-optimizations here.
+ *
+ * [1] if match_wildcard_base() says the base
+ * directory is already matched, we only need to match
+ * the rest, which is shorter so _in theory_ faster.
*/
strbuf_add(base, entry->path, pathlen);
- if (!fnmatch(match, base->buf + base_offset, 0)) {
+ if (!git_fnmatch(match, base->buf + base_offset,
+ item->flags & PATHSPEC_ONESTAR ? GFNM_ONESTAR : 0,
+ item->nowildcard_len)) {
strbuf_setlen(base, base_offset + baselen);
return entry_interesting;
}
if (old && same(old, a)) {
int update = 0;
- if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) {
+ if (o->reset && o->update && !ce_uptodate(old) && !ce_skip_worktree(old)) {
struct stat st;
if (lstat(old->name, &st) ||
ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
va_end(params);
}
+#undef error
int error(const char *err, ...)
{
va_list params;
* If indent is negative, assume that already -indent columns have been
* consumed (and no extra indent is necessary for the first line).
*/
-int strbuf_add_wrapped_text(struct strbuf *buf,
+void strbuf_add_wrapped_text(struct strbuf *buf,
const char *text, int indent1, int indent2, int width)
{
int indent, w, assume_utf8 = 1;
if (width <= 0) {
strbuf_add_indented_text(buf, text, indent1, indent2);
- return 1;
+ return;
}
retry:
if (w <= width || !space) {
const char *start = bol;
if (!c && text == start)
- return w;
+ return;
if (space)
start = space;
else
strbuf_addchars(buf, ' ', indent);
strbuf_add(buf, start, text - start);
if (!c)
- return w;
+ return;
space = text;
if (c == '\t')
w |= 0x07;
}
}
-int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
+void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
int indent, int indent2, int width)
{
char *tmp = xstrndup(data, len);
- int r = strbuf_add_wrapped_text(buf, tmp, indent, indent2, width);
+ strbuf_add_wrapped_text(buf, tmp, indent, indent2, width);
free(tmp);
- return r;
}
int is_encoding_utf8(const char *name)
int is_encoding_utf8(const char *name);
int same_encoding(const char *, const char *);
-int strbuf_add_wrapped_text(struct strbuf *buf,
+void strbuf_add_wrapped_text(struct strbuf *buf,
const char *text, int indent, int indent2, int width);
-int strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
+void strbuf_add_wrapped_bytes(struct strbuf *buf, const char *data, int len,
int indent, int indent2, int width);
#ifndef NO_ICONV
int access_or_warn(const char *path, int mode)
{
int ret = access(path, mode);
- if (ret && errno != ENOENT)
+ if (ret && errno != ENOENT && errno != ENOTDIR)
warn_on_inaccessible(path);
return ret;
}
+int access_or_die(const char *path, int mode)
+{
+ int ret = access(path, mode);
+ if (ret && errno != ENOENT && errno != ENOTDIR)
+ die_errno(_("unable to access '%s'"), path);
+ return ret;
+}
+
struct passwd *xgetpwuid_self(void)
{
struct passwd *pw;