Update projects

dfetch update fetches every dependency listed in dfetch.yaml and places the requested version in its destination folder. VCS type (Git, SVN, or plain archive) is detected automatically.

Fetching all projects

Run without arguments to fetch every project in the manifest:

$ dfetch update

Dfetch reads dfetch.yaml, resolves each project’s VCS type, and writes the requested revision into the destination folder. A .dfetch_data.yaml metadata file is created inside each destination so Dfetch can track what version is present.

Example: Git projects are specified in the manifest
Scenario: Git projects are specified in the manifest
    Given the manifest 'dfetch.yaml'
        """
        manifest:
          version: '0.0'

          remotes:
            - name: github-com-dfetch-org
              url-base: https://github.com/dfetch-org/test-repo

          projects:
            - name: ext/test-repo-rev-only
              revision: e1fda19a57b873eb8e6ae37780594cbb77b70f1a
              dst: ext/test-repo-rev-only

            - name: ext/test-rev-and-branch
              revision: 8df389d0524863b85f484f15a91c5f2c40aefda1
              branch: main
              dst: ext/test-rev-and-branch

            - name: ext/test-repo-tag-v1
              tag: v1
              dst: ext/test-repo-tag-v1

        """
    When I run "dfetch update"
    Then the following projects are fetched
        | path                       |
        | ext/test-repo-rev-only     |
        | ext/test-rev-and-branch    |
        | ext/test-repo-tag-v1       |
Example: Tag is updated in manifest
Scenario: Tag is updated in manifest
    Given the manifest 'dfetch.yaml'
        """
        manifest:
          version: '0.0'

          projects:
            - name: ext/test-repo-tag
              url: https://github.com/dfetch-org/test-repo
              tag: v1

        """
    And all projects are updated
    When the manifest 'dfetch.yaml' is changed to
        """
        manifest:
          version: '0.0'

          projects:
            - name: ext/test-repo-tag
              url: https://github.com/dfetch-org/test-repo
              tag: v2.0

        """
    And I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          ext/test-repo-tag:
          > Fetched v2.0
        """
Example: Version check ignored when force flag is given
Scenario: Version check ignored when force flag is given
    Given the manifest 'dfetch.yaml'
        """
        manifest:
          version: '0.0'

          projects:
            - name: ext/test-repo-tag
              url: https://github.com/dfetch-org/test-repo
              tag: v1

        """
    And all projects are updated
    When I run "dfetch update --force"
    Then the output shows
        """
        Dfetch (0.14.3)
          ext/test-repo-tag:
          > Fetched v1
        """
Example: Update with empty manifest does nothing
Scenario: Update with empty manifest does nothing
    Given the manifest 'dfetch.yaml'
        """
        manifest:
          version: '0.0'
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
        """

Updating a single project

Pass one or more project names to limit which entries are updated:

$ dfetch update mylib
$ dfetch update mylib myother

Overwriting local changes

By default Dfetch skips a project that is already at the requested version or that has local modifications. Use --force (-f) to re-fetch and overwrite regardless:

$ dfetch update --force mylib

Warning

Any unsaved local edits in the destination directory will be lost. Capture them first with dfetch diff — see Patch a project for the full patch workflow.

Sub-manifests

A fetched project may itself contain a dfetch.yaml. Dfetch reads it after fetching and reports any further dependencies it finds, so you can decide whether to vendor those as well.

To skip this check entirely:

$ dfetch update --no-recommendations
Example: Git projects are specified in the manifest
Scenario: Git projects are specified in the manifest
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: SomeProjectWithManifest
                  dst: third-party/SomeProjectWithManifest
                  url: some-remote-server/SomeProjectWithManifest.git
                  tag: v1
                - name: SomeProjectWithoutManifest
                  dst: third-party/SomeProjectWithoutManifest
                  url: some-remote-server/SomeProjectWithoutManifest.git
                  tag: v1
        """
    And a git-repository "SomeProjectWithManifest.git" with the manifest:
        """
        manifest:
            version: 0.0
            remotes:
                - name: github-com-dfetch-org
                  url-base: https://github.com/dfetch-org/test-repo

            projects:
                - name: SomeOtherProject
                  dst: SomeOtherProject
                  url: some-remote-server/SomeOtherProject.git
                  tag: v1

                - name: ext/test-repo-tag-v1
                  tag: v1
        """
    And a git repository "SomeProjectWithoutManifest.git"
    When I run "dfetch update" in MyProject
    Then the output shows
        """
        Dfetch (0.14.3)
          SomeProjectWithManifest:
          > Fetched v1
          > "SomeProjectWithManifest" depends on the following project(s) which are not part of your manifest:
            (found in third-party/SomeProjectWithManifest/dfetch.yaml)

            -   name: SomeOtherProject
                url: some-remote-server/SomeOtherProject.git
                tag: v1
            -   name: ext/test-repo-tag-v1
                url: https://github.com/dfetch-org/test-repo
                tag: v1

          SomeProjectWithoutManifest:
          > Fetched v1
        """
    And 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            third-party/
                SomeProjectWithManifest/
                    .dfetch_data.yaml
                    README.md
                    dfetch.yaml
                SomeProjectWithoutManifest/
                    .dfetch_data.yaml
                    README.md
        """
Example: A project from a submanifest has an invalid manifest
Scenario: A project from a submanifest has an invalid manifest
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: SomeProject
                  dst: third-party/SomeProject
                  url: some-remote-server/SomeProject.git
                  tag: v1
        """
    And a git-repository "SomeProject.git" with the manifest:
        """
        very-invalid-manifest
        """
    When I run "dfetch update" in MyProject
    Then the output shows
        """
        Dfetch (0.14.3)
          SomeProject:
          > Fetched v1
          > SomeProject/dfetch.yaml: Schema validation failed:
                "very-invalid-manifest\n"
                 ^ (line: 1)
            found arbitrary text
        """
    And 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            third-party/
                SomeProject/
                    .dfetch_data.yaml
                    README.md
                    dfetch.yaml
        """

Git submodules

When a Git dependency contains submodules, Dfetch fetches and resolves them automatically — no extra manifest entries or git submodule commands are needed. Each submodule is checked out at the exact revision pinned by the parent repository.

$ dfetch update

Dfetch (0.13.0)
my-project:
> Found & fetched submodule "./ext/vendor-lib"  (https://github.com/example/vendor-lib @ master - 79698c9…)
> Fetched master - e1fda19…

Nested submodules are resolved recursively. Pinned details for each submodule are recorded in .dfetch_data.yaml and are visible in Report.

Example: A project with a git submodule is fetched at the pinned revision
Scenario: A project with a git submodule is fetched at the pinned revision
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: my-project-with-submodules
                  url: some-remote-server/SomeInterestingProject.git
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          my-project-with-submodules:
          > Found & fetched submodule "./ext/test-repo1"  (some-remote-server/TestRepo.git @ master - 79698c99152e4a4b7b759c9def50a130bc91a2ff)
          > Found & fetched submodule "./ext/test-repo2"  (some-remote-server/TestRepo.git @ master - 79698c99152e4a4b7b759c9def50a130bc91a2ff)
          > Fetched master - e1fda19a57b873eb8e6ae37780594cbb77b70f1a
        """
    Then 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            my-project-with-submodules/
                .dfetch_data.yaml
                README.md
                ext/
                    test-repo1/
                        README.md
                    test-repo2/
                        README.md
        """
Example: A project with a git submodule that itself has a nested submodule is fetched at the pinned revision
Scenario: A project with a git submodule that itself has a nested submodule is fetched at the pinned revision
    Given a git repository "LeafProject.git"
    And a git-repository "MiddleProject.git" with the following submodules
        | path     | url                                | revision |
        | ext/leaf | some-remote-server/LeafProject.git | master   |
    And a git-repository "OuterProject.git" with the following submodules
        | path       | url                                  | revision |
        | ext/middle | some-remote-server/MiddleProject.git | master   |
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: outer-project
                  url: some-remote-server/OuterProject.git
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          outer-project:
          > Found & fetched submodule "./ext/middle"  (some-remote-server/MiddleProject.git @ master - 79698c99152e4a4b7b759c9def50a130bc91a2ff)
          > Fetched master - e1fda19a57b873eb8e6ae37780594cbb77b70f1a
        """
    Then 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            outer-project/
                .dfetch_data.yaml
                README.md
                ext/
                    middle/
                        README.md
                        ext/
                            leaf/
                                README.md
        """
Example: Submodule changes are reported in the project report
Scenario: Submodule changes are reported in the project report
    Given a fetched and committed MyProject with the manifest
        """
        manifest:
            version: 0.0
            projects:
                - name: my-project-with-submodules
                  url: some-remote-server/SomeInterestingProject.git
        """
    When I run "dfetch report" in MyProject
    Then the output shows
        """
        Dfetch (0.14.3)
          my-project-with-submodules:
          - remote            : <none>
            remote url        : some-remote-server/SomeInterestingProject.git
            branch            : master
            tag               : <none>
            last fetch        : 26/02/2026, 20:28:24
            revision          : 79698c99152e4a4b7b759c9def50a130bc91a2ff
            patch             : <none>
            licenses          : <none>

            dependencies      :
            - path            : ext/test-repo1
              url             : some-remote-server/TestRepo.git
              branch          : master
              tag             : <none>
              revision        : e1fda19a57b873eb8e6ae37780594cbb77b70f1a
              source-type     : git-submodule

            - path            : ext/test-repo2
              url             : some-remote-server/TestRepo.git
              branch          : master
              tag             : <none>
              revision        : 8df389d0524863b85f484f15a91c5f2c40aefda1
              source-type     : git-submodule
        """
Example: Subfolder is matched through a glob is fetched and submodules are resolved
Scenario: Subfolder is matched through a glob is fetched and submodules are resolved
    Given a git-repository "GlobProject.git" with the following submodules
        | path                     | url                             | revision |
        | some_dir_a/ext/test-repo | some-remote-server/TestRepo.git | master   |
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: glob-project
                  url: some-remote-server/GlobProject.git
                  src: some_dir_*
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          glob-project:
          > Found & fetched submodule "./ext/test-repo"  (some-remote-server/TestRepo.git @ master - 79698c99152e4a4b7b759c9def50a130bc91a2ff)
          > Fetched master - e1fda19a57b873eb8e6ae37780594cbb77b70f1a
        """
    Then 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            glob-project/
                .dfetch_data.yaml
                ext/
                    test-repo/
                        README.md
        """
Example: A submodule within a plain src directory is fetched
Scenario: A submodule within a plain src directory is fetched
    Given a git-repository "PlainSrcProject.git" with the following submodules
        | path                    | url                             | revision |
        | src_folder/ext/sub-repo | some-remote-server/TestRepo.git | master   |
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: plain-src-project
                  url: some-remote-server/PlainSrcProject.git
                  src: src_folder
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          plain-src-project:
          > Found & fetched submodule "./ext/sub-repo"  (some-remote-server/TestRepo.git @ master - 79698c99152e4a4b7b759c9def50a130bc91a2ff)
          > Fetched master - e1fda19a57b873eb8e6ae37780594cbb77b70f1a
        """
    Then 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            plain-src-project/
                .dfetch_data.yaml
                ext/
                    sub-repo/
                        README.md
        """
Example: A submodule outside the src folder is not fetched when src is specified
Scenario: A submodule outside the src folder is not fetched when src is specified
    Given a git-repository "MixedSubmoduleProject.git" with the following submodules
        | path                  | url                             | revision |
        | src_folder/ext/inside | some-remote-server/TestRepo.git | master   |
        | other_ext/outside     | some-remote-server/TestRepo.git | master   |
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: mixed-project
                  url: some-remote-server/MixedSubmoduleProject.git
                  src: src_folder
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          mixed-project:
          > Found & fetched submodule "./ext/inside"  (some-remote-server/TestRepo.git @ master - 79698c99152e4a4b7b759c9def50a130bc91a2ff)
          > Fetched master - e1fda19a57b873eb8e6ae37780594cbb77b70f1a
        """
    Then 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            mixed-project/
                .dfetch_data.yaml
                ext/
                    inside/
                        README.md
        """
Example: A sibling submodule at the same top-level dir as src is not fetched
Scenario: A sibling submodule at the same top-level dir as src is not fetched
    Given a git-repository "SiblingSubmoduleProject.git" with the following submodules
        | path        | url                             | revision |
        | apps/lib    | some-remote-server/TestRepo.git | master   |
        | apps/widget | some-remote-server/TestRepo.git | master   |
    Given the manifest 'dfetch.yaml' in MyProject
        """
        manifest:
            version: 0.0
            projects:
                - name: sibling-project
                  url: some-remote-server/SiblingSubmoduleProject.git
                  src: apps/lib
        """
    When I run "dfetch update"
    Then the output shows
        """
        Dfetch (0.14.3)
          sibling-project:
          > Fetched master - e1fda19a57b873eb8e6ae37780594cbb77b70f1a
        """
    Then 'MyProject' looks like:
        """
        MyProject/
            dfetch.yaml
            sibling-project/
                .dfetch_data.yaml
                README.md
        """

Line endings

If your superproject is a git repository and its .gitattributes file contains a global or per-directory eol rule, DFetch lets the version control system that performs the fetch produce matching line endings, so the vendored files match your project’s enforced style on every platform:

# force LF everywhere (common on cross-platform projects)
* text=auto eol=lf

# force CRLF everywhere (less common, but valid)
* text=auto eol=crlf

DFetch uses git check-attr to resolve the effective setting for each dependency’s destination directory, so per-directory rules are honoured as well as global ones. The conversion itself is done natively by the VCS that fetches the project, so even large dependencies convert at full speed:

  • Git dependencies are checked out with the requested eol configured and renormalised by git itself; git’s own text detection decides which files are text, so binary files are never converted.

  • SVN dependencies are exported with svn export --native-eol, which applies the requested ending to every file carrying the svn:eol-style=native property; files without that property keep their bytes exactly as stored.

If your superproject is an SVN repository, declare the preference with SVN’s own mechanism instead — the svn:auto-props property:

svn propset svn:auto-props '* = svn:eol-style=LF' .

DFetch reads the property (including values inherited from parent directories) for each dependency’s destination and applies it the same way.

Archive dependencies are extracted byte-for-byte. If no preference is set, the platform and VCS defaults apply unchanged.

The scenarios below cover both Git and SVN subprojects, and verify all four combinations of remote content (LF or CRLF) against each superproject eol setting — expressed concisely via Gherkin Examples tables. They also verify that a superproject using another VCS (such as SVN) leaves the fetched line endings untouched:

Example: Git superproject forces <eol> on git subproject with <remote_eol> remote content
Scenario Outline: Git superproject forces <eol> on git subproject with <remote_eol> remote content
    Given a git-repository "SomeProject.git" with <remote_eol> content
    And a local git repo "MyProject" with the manifest
        """
        manifest:
          version: '0.0'

          projects:
            - name: SomeProject
              url: some-remote-server/SomeProject.git
              tag: v1
        """
    And ".gitattributes" in MyProject is created and committed with
        """
        * text=auto eol=<eol>
        """
    When I run "dfetch update" in MyProject
    Then 'MyProject/SomeProject/README.md' has <eol> line endings

    Examples:
        | remote_eol | eol  |
        | LF         | crlf |
        | CRLF       | lf   |
        | CRLF       | crlf |
        | LF         | lf   |
Example: SVN superproject forces <eol> on git subproject with <remote_eol> remote content
Scenario Outline: SVN superproject forces <eol> on git subproject with <remote_eol> remote content
    An SVN superproject declares its line-ending preference with svn's own
    mechanism: the ``svn:auto-props`` property (e.g. ``* = svn:eol-style=LF``).

    Given a git-repository "SomeProject.git" with <remote_eol> content
    And a local svn repo "MyProject" with the manifest
        """
        manifest:
          version: '0.0'

          projects:
            - name: SomeProject
              url: some-remote-server/SomeProject.git
              tag: v1
        """
    And svn:auto-props in MyProject requests <eol> line endings
    When I run "dfetch update" in MyProject
    Then 'MyProject/SomeProject/README.md' has <eol> line endings

    Examples:
        | remote_eol | eol  |
        | LF         | crlf |
        | CRLF       | lf   |
        | CRLF       | crlf |
        | LF         | lf   |
Example: SVN superproject forces <eol> on SVN subproject with <remote_eol> remote content
Scenario Outline: SVN superproject forces <eol> on SVN subproject with <remote_eol> remote content
    Given a svn-server "SomeSvnProject" with <remote_eol> content
    And a local svn repo "MyProject" with the manifest
        """
        manifest:
          version: '0.0'

          projects:
            - name: SomeSvnProject
              url: some-remote-server/SomeSvnProject
              tag: v1
        """
    And svn:auto-props in MyProject requests <eol> line endings
    When I run "dfetch update" in MyProject
    Then 'MyProject/SomeSvnProject/README.md' has <eol> line endings

    Examples:
        | remote_eol | eol  |
        | LF         | crlf |
        | CRLF       | lf   |
        | CRLF       | crlf |
        | LF         | lf   |
Example: SVN superproject without a preference leaves line endings unchanged
Scenario: SVN superproject without a preference leaves line endings unchanged
    Without a line-ending preference (``.gitattributes`` in a git
    superproject, ``svn:auto-props`` in an SVN superproject), fetched files
    keep the line endings the remote provides. A stray ``.gitattributes``
    file next to the manifest of an SVN superproject is not used.

    Given a git-repository "SomeProject.git" with CRLF content
    And a local svn repo "MyProject" with the manifest
        """
        manifest:
          version: '0.0'

          projects:
            - name: SomeProject
              url: some-remote-server/SomeProject.git
              tag: v1
        """
    And ".gitattributes" in MyProject is changed with
        """
        * text=auto eol=lf
        """
    When I run "dfetch update" in MyProject
    Then 'MyProject/SomeProject/README.md' has CRLF line endings
Example: Git superproject forces <eol> on SVN subproject with <remote_eol> remote content
Scenario Outline: Git superproject forces <eol> on SVN subproject with <remote_eol> remote content
    Given a svn-server "SomeSvnProject" with <remote_eol> content
    And a local git repo "MyProject" with the manifest
        """
        manifest:
          version: '0.0'

          projects:
            - name: SomeSvnProject
              url: some-remote-server/SomeSvnProject
              tag: v1
        """
    And ".gitattributes" in MyProject is created and committed with
        """
        * text=auto eol=<eol>
        """
    When I run "dfetch update" in MyProject
    Then 'MyProject/SomeSvnProject/README.md' has <eol> line endings

    Examples:
        | remote_eol | eol  |
        | LF         | crlf |
        | CRLF       | lf   |
        | CRLF       | crlf |
        | LF         | lf   |
Example: Remote gitattributes eol=crlf for a file type overrides the superproject lf preference
Scenario: Remote gitattributes eol=crlf for a file type overrides the superproject lf preference
    A remote repo that marks ``*.bat`` files as ``eol=crlf`` in its own
    ``.gitattributes`` keeps those line endings even when the superproject
    globally requests ``lf``, so Windows batch files are never corrupted.

    Given a git-repository "SomeProject.git" with a CRLF "script.bat" and "*.bat eol=crlf" gitattributes
    And a local git repo "MyProject" with the manifest
        """
        manifest:
          version: '0.0'

          projects:
            - name: SomeProject
              url: some-remote-server/SomeProject.git
              tag: v1
        """
    And ".gitattributes" in MyProject is created and committed with
        """
        * text=auto eol=lf
        """
    When I run "dfetch update" in MyProject
    Then 'MyProject/SomeProject/script.bat' has CRLF line endings