diff --git a/CHANGELOG.md b/CHANGELOG.md index 34375f426..40582d7ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,11 +19,11 @@ _When adding new entries to the changelog, please include issue/PR numbers where - However, new repositories are still V1 repositories unless V2 is explicitly requested, since V2 is still in development. - Tracking issue [#72](https://github.com/koordinates/sno/issues/72) -### Using Datasets V1 +#### Using Datasets V1 * Unless specific action is taken, existing repositories will remain V1, and new repositories still default to V1. -### Using Datasets V2 +#### Using Datasets V2 * An entire repository must be either V1 or V2, so to use V2, all data must be imported as V2. * Data can be imported as V2 using `sno init --import= --version=2` or `sno import --version=2` @@ -35,6 +35,10 @@ _When adding new entries to the changelog, please include issue/PR numbers where * Schemas can be changed at HEAD and the new schemas committed, but checking out commits where the schema is different to the current working copy is not yet supported. * Geometry storage format is not yet finalised. +### Other changes in this release + + * `sno clone` now support shallow clones (`--depth N`) to avoid cloning a repo's entire history [#174](https://github.com/koordinates/sno/issues/174) + ## 0.4.1 ### Packaging fix: diff --git a/sno/clone.py b/sno/clone.py index 029980c41..a15325a67 100644 --- a/sno/clone.py +++ b/sno/clone.py @@ -45,32 +45,38 @@ def get_directory_from_url(url): default=True, help="Whether to report progress to stderr", ) +@click.option( + "--depth", + type=click.INT, + help="Create a shallow clone with a history truncated to the specified number of commits.", +) @click.argument("url", nargs=1) @click.argument( "directory", type=click.Path(exists=False, file_okay=False, writable=True), required=False, ) -def clone(ctx, do_checkout, do_progress, url, directory): +def clone(ctx, do_checkout, do_progress, depth, url, directory): """ Clone a repository into a new directory """ repo_path = Path(directory or get_directory_from_url(url)) + args = [ + "git", + "clone", + "--progress" if do_progress else "--quiet", + "--bare", + "--config", + "remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*", + url, + str(repo_path.resolve()), + ] + if depth is not None: + args.append(f'--depth={depth}') try: # we use subprocess because it deals with credentials much better & consistently than we can do at the moment. # pygit2.clone_repository() works fine except for that - subprocess.check_call( - [ - "git", - "clone", - "--progress" if do_progress else "--quiet", - "--bare", - "--config", - "remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*", - url, - str(repo_path.resolve()), - ] - ) + subprocess.check_call(args) except subprocess.CalledProcessError as e: sys.exit(translate_subprocess_exit_code(e.returncode)) diff --git a/sno/show.py b/sno/show.py index 85d1e800c..ab2e3ee29 100644 --- a/sno/show.py +++ b/sno/show.py @@ -42,10 +42,18 @@ def show(ctx, *, refish, output_format, json_style, **kwargs): # Ensures we were given a reference to a commit, and not a tree or something commit = CommitWithReference.resolve(repo, refish).commit - if commit.parents: - parent = f"{refish}^" - else: + try: + parents = commit.parents + except KeyError: + # one or more parents doesn't exist. + # This is okay if this is the first commit of a shallow clone. + # (how to tell?) parent = EMPTY_TREE_SHA + else: + if parents: + parent = f"{refish}^" + else: + parent = EMPTY_TREE_SHA patch_writer = globals()[f"patch_output_{output_format}"] return diff.diff_with_writer( diff --git a/tests/test_diff.py b/tests/test_diff.py index 213d17e99..bc65a3cc0 100644 --- a/tests/test_diff.py +++ b/tests/test_diff.py @@ -1401,3 +1401,15 @@ def test_show_json_format(data_archive_readonly, cli_runner): assert r.exit_code == 0, r # output is compact, no indentation assert '"sno.diff/v1+hexwkb": {"' in r.stdout + + +def test_show_shallow_clone(data_archive_readonly, cli_runner, tmp_path, chdir): + # just checking you can 'show' the first commit of a shallow clone + with data_archive_readonly("points") as original_path: + clone_path = tmp_path / "shallow-clone" + r = cli_runner.invoke(["clone", "--depth=1", original_path, clone_path]) + assert r.exit_code == 0, r + + with chdir(clone_path): + r = cli_runner.invoke(["show"]) + assert r.exit_code == 0, r