Skip to content

Copies as alternative to Symlinks #350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ sources:
- repo: "https://github.com/google/material-design-icons"
name: material-design-icons
rev: master
copies:
- target: Icons

groups:
- name: code
Expand Down
2 changes: 1 addition & 1 deletion docs/setup/git.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ The token can also be written to `.netrc` during builds, see the guide for [Trav

## Symlinks on Windows

If you're using Windows, there are some additional prerequisites to ensure Gitman works seamlessly with symbolic links.
If you're using Windows, there are some additional prerequisites to ensure Gitman works seamlessly with symbolic links. You could consider using copies instead.

### Grant Permissions

Expand Down
48 changes: 48 additions & 0 deletions docs/use-cases/multiple-copies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Using Multiple Copies

This feature can be used to create as many copies as you need from one repository. This can be helpful e.g. on Windows where you need administrator priviledges in order to create a symbolic link.

## The Syntax

Let's say we have a simple project structure:

```text
|- include
|- src
|- docs
```

with the following `gitman.yml`:

```yaml
location: .gitman

sources:
- repo: <URL of my_dependency repository>
name: my_dependency
rev: v1.0.3
copies:
- source: include
target: vendor/partial_repo
- target: vendor/full_repo
```

This will result in the following copies:

- `<root>/.gitman/my_dependency/include` -> `<root>/vendor/partial_repo`
- `<root>/.gitman/my_dependency` -> `<root>/vendor/full_repo`

## Alternative Syntax

```yaml
location: vendor

sources:
- repo: <URL of my_dependency repository>
name: my_dependency
rev: v1.0.3
copies:
- { source: src, target: partial_repo }
- { target: full_repo }
```

17 changes: 17 additions & 0 deletions gitman.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ sources:
links:
- source: ''
target: demo/example
copies:
-
scripts:
- cat .noserc
- make foobar
Expand All @@ -22,6 +24,9 @@ sources:
-
links:
-
copies:
- source: ''
target: demo/example
scripts:
-
- repo: https://github.com/jacebrowning/gitman-demo
Expand All @@ -33,6 +38,8 @@ sources:
-
links:
-
copies:
-
scripts:
- echo "Hello, World!"
- pwd
Expand All @@ -45,6 +52,8 @@ sources:
-
links:
-
copies:
-
scripts:
-
sources_locked:
Expand All @@ -57,6 +66,8 @@ sources_locked:
-
links:
-
copies:
-
scripts:
- cat .noserc
- make foobar
Expand All @@ -69,6 +80,8 @@ sources_locked:
-
links:
-
copies:
-
scripts:
-
- repo: https://github.com/jacebrowning/gitman-demo
Expand All @@ -80,6 +93,8 @@ sources_locked:
-
links:
-
copies:
-
scripts:
- echo "Hello, World!"
- pwd
Expand All @@ -92,6 +107,8 @@ sources_locked:
-
links:
-
copies:
-
scripts:
-
groups:
Expand Down
2 changes: 2 additions & 0 deletions gitman/models/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ def install_dependencies(
assert self.root, f"Missing root: {self}"
source.create_links(self.root, force=force)
common.newline()
source.create_copies(self.root, force=force)
common.newline()
count += 1

config = load_config(search=False)
Expand Down
47 changes: 47 additions & 0 deletions gitman/models/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ class Link:
target: str = ""


@dataclass
class Copy:
source: str = ""
target: str = ""


@dataclass
class Source:
"""Represents a repository to clone and options for controlling checkout.
Expand All @@ -29,6 +35,7 @@ class Source:
| `params` | Additional arguments for `clone` | No | `null` |
| `sparse_paths` | Controls partial checkout | No | `[]` |
| `links` | Creates symlinks within a project | No | `[]` |
| `copies` | Creates copies within of a file or folder | No | `[]` |
| `scripts` | Shell commands to run after checkout | No | `[]` |

### Params
Expand All @@ -51,6 +58,10 @@ class Source:

See [using multiple links][using-multiple-links] for more information.

### Copies

See [using multiple copies][using-multiple-copies] for more information.

### Scripts

Scripts can be used to run post-checkout commands such us build steps. For example:
Expand All @@ -72,6 +83,7 @@ class Source:
params: Optional[str] = None
sparse_paths: List[str] = field(default_factory=list)
links: List[Link] = field(default_factory=list)
copies: List[Copy] = field(default_factory=list)

scripts: List[str] = field(default_factory=list)

Expand Down Expand Up @@ -205,6 +217,19 @@ def create_links(self, root: str, *, force: bool = False):
source = os.path.join(relpath, os.path.normpath(link.source))
create_sym_link(source, target, force=force)

def create_copies(self, root: str, *, force: bool = False):
"""Create copies from source to target."""
if not self.copies:
return

for copy in self.copies:
target = os.path.join(root, os.path.normpath(copy.target))
relpath = os.path.relpath(os.getcwd(), os.path.dirname(target))
source = os.path.join(
root, os.path.join(relpath, copy.source) if copy.source else relpath
)
create_copy(source, target, force=force)

def run_scripts(self, force: bool = False, show_shell_stdout: bool = False):
log.info("Running install scripts...")

Expand Down Expand Up @@ -317,6 +342,7 @@ def lock(
name=self.name,
rev=rev,
links=self.links,
copies=self.copies,
scripts=self.scripts,
sparse_paths=self.sparse_paths,
)
Expand Down Expand Up @@ -349,3 +375,24 @@ def create_sym_link(source: str, target: str, *, force: bool):
raise exceptions.UncommittedChanges(msg)

shell.ln(source, target)


def create_copy(source: str, target: str, *, force: bool):
log.info("Creating a copy...")

if os.path.islink(target):
os.remove(target)
elif os.path.exists(target):
if force:
shell.rm(target)
elif os.path.isfile(target) and os.path.isdir(source):
msg = "Preexisting file location to be replaced by folder at {}".format(
target
)
raise exceptions.UncommittedChanges(msg)
elif os.path.isfile(target) and os.path.isfile(source):
shell.rm(target)
else:
msg = "Preexisting target location at {}".format(target)
log.warn(msg)
shell.cp(source, target)
11 changes: 11 additions & 0 deletions gitman/shell.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Utilities to call shell programs."""

import os
import shutil
import subprocess

import log
Expand Down Expand Up @@ -127,6 +128,16 @@ def ln(source, target):
os.symlink(source, target)


def cp(source, target):
dirpath = os.path.dirname(target)
if not os.path.isdir(dirpath):
mkdir(dirpath)
if os.path.isdir(source):
shutil.copytree(src=source, dst=target, dirs_exist_ok=True)
else:
shutil.copy2(src=source, dst=target)


def rm(path):
if os.name == "nt":
if os.path.isfile(path):
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ nav:
- Sparse Checkouts: use-cases/sparse-checkouts.md
- Default Groups: use-cases/default-groups.md
- Multiple Links: use-cases/multiple-links.md
- Multiple Copies: use-cases/multiple-copies.md
- Extras:
- Git SVN Bridge: extras/git-svn-bridge.md
- Bundled Application: extras/bundled-application.md
Expand Down
Loading