Skip to content
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

get: fails with head detached and no branches in place #6154

Closed
aguschin opened this issue Jun 11, 2021 · 21 comments
Closed

get: fails with head detached and no branches in place #6154

aguschin opened this issue Jun 11, 2021 · 21 comments
Labels
A: data-sync Related to dvc get/fetch/import/pull/push bug Did we break something? p3-nice-to-have It should be done this or next sprint

Comments

@aguschin
Copy link
Contributor

aguschin commented Jun 11, 2021

Bug Report

Description

With head detached and no branches available dvc get fails:

ERROR: failed to get 'data/data.xml' from '.' - unknown Git revision 'refs/remotes/origin/HEAD'

UPDATE: Skip to #6154 (comment)

Reproduce

  1. git clone https://github.com/iterative/example-get-started
  2. cd example-get-started
  3. git checkout --detach
  4. git branch -D master
  5. git --no-pager branch -v
  6. dvc get . data/data.xml

Environment information

Output of dvc doctor:

$ dvc doctor
DVC version: 2.3.0 (pip)
---------------------------------
Platform: Python 3.9.5 on macOS-10.16-x86_64-i386-64bit
Supports: azure, http, https
Cache types: reflink, hardlink, symlink
Cache directory: apfs on /dev/disk1s5s1
Caches: local
Remotes: https
Workspace directory: apfs on /dev/disk1s5s1
Repo: dvc, git

Additional Information (if any):

Interesting, that dvc pull works fine.

2021-06-11 12:45:41,751 DEBUG: Creating external repo .@None
2021-06-11 12:45:41,751 DEBUG: erepo: git clone '.' to a temporary dir
2021-06-11 12:45:41,966 DEBUG: Removing '/Users/aguschin/Git/iterative/example-get-started2/.gqZZrecSgVYzPDv8JZmpCu'                                          
2021-06-11 12:45:41,966 ERROR: failed to get 'data/data.xml' from '.' - unknown Git revision 'refs/remotes/origin/HEAD'
------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/command/get.py", line 37, in _get_file_from_repo
    Repo.get(
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/repo/get.py", line 50, in get
    with external_repo(
  File "/usr/local/Cellar/python@3.9/3.9.5/Frameworks/Python.framework/Versions/3.9/lib/python3.9/contextlib.py", line 117, in __enter__
    return next(self.gen)
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/external_repo.py", line 70, in external_repo
    repo = Repo(**repo_kwargs)
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/repo/__init__.py", line 155, in __init__
    self.root_dir, self.dvc_dir, self.tmp_dir = self._get_repo_dirs(
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/repo/__init__.py", line 103, in _get_repo_dirs
    fs = scm.get_fs(rev) if isinstance(scm, Git) and rev else None
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/scm/git/__init__.py", line 353, in get_fs
    resolved = self.resolve_rev(rev)
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/scm/git/__init__.py", line 400, in resolve_rev
    return self._resolve_rev(rev)
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/scm/git/__init__.py", line 343, in _backend_func
    return func(*args, **kwargs)
  File "/Users/aguschin/.local/share/virtualenvs/yolov5-y85dc7P1/lib/python3.9/site-packages/dvc/scm/git/backend/pygit2.py", line 217, in resolve_rev
    raise RevError(f"unknown Git revision '{rev}'")
dvc.scm.base.RevError: unknown Git revision 'refs/remotes/origin/HEAD'
------------------------------------------------------------
2021-06-11 12:45:41,974 DEBUG: Analytics is enabled.
2021-06-11 12:45:42,049 DEBUG: Trying to spawn '['daemon', '-q', 'analytics', '/var/folders/_l/xm_1fknn1479pfh9kg0ttgjc0000gq/T/tmpklkkra_a']'
2021-06-11 12:45:42,051 DEBUG: Spawned '['daemon', '-q', 'analytics', '/var/folders/_l/xm_1fknn1479pfh9kg0ttgjc0000gq/T/tmpklkkra_a']'

Initially this happened with me in Gitlab CI -- it seems like Gitlab fetches the repo without any branches and with head detached:

# output received from debugging this in gitlab CI:

$ git --no-pager branch -v
* (HEAD detached at eae5019) eae5019 update environment

$ dvc get . model
ERROR: failed to get 'model' from '.' - unknown Git revision 'refs/remotes/origin/HEAD'                                                                       

Simple hack works, like running git checkout -b temp-branch first:

git checkout -b temp-branch
dvc get . model
@aguschin aguschin added the bug Did we break something? label Jun 11, 2021
@jorgeorpinel
Copy link
Contributor

jorgeorpinel commented Jun 20, 2021

dvc.scm.base.RevError: unknown Git revision 'refs/remotes/origin/HEAD'

I think that get clones the source repo (even if it's .) and requires a --rev which is by default HEAD. The workaround is to use dvc get . data/data.xml --rev 00071e8 instead.

What change would you suggest here? (What would you expect?) Looks like a very rare edge case to me, but I do think that the error should be handled — maybe give message such as Please provide a valid `--rev`.

Interesting, that dvc pull works fine.

push/pull don't need Git at all. They use the metafiles present in the project (and config file).

@pmrowla
Copy link
Contributor

pmrowla commented Jun 21, 2021

dvc.scm.base.RevError: unknown Git revision 'refs/remotes/origin/HEAD'

I think that get clones the source repo (even if it's .) and requires a --rev which is by default HEAD. The workaround is to use dvc get . data/data.xml --rev 00071e8 instead.

I think this is a bug, the issue here is that the repo being cloned does have a HEAD, but it is detached, meaning it points to a specific revision and not to a branch. The git remote-tracking refs (like refs/remotes/origin/HEAD) are used locally to keep "copies" of refs in the cloned repo, but apparently refs/remotes/origin/HEAD isn't created when you clone a repo that is in a detached state.

This is a pretty specific edge case though, and I think cloning a repo with no branches may still cause some other issues in DVC anyways, even if we fix the origin/HEAD issue.

We maybe need to rethink how dvc get for local repos should work (and just skip the clone entirely), similar to how we should handle dvc list with local repos without cloning as well (#3590)

@pmrowla
Copy link
Contributor

pmrowla commented Jun 21, 2021

Should also note that if gitlab CI is always doing this specific detached fetch, you probably shouldn't be using dvc get . (with the local repo .) at all to begin with. Even when using dvc get . --rev <some_rev>, it will fail unless some_rev is a git SHA for a commit that is a parent of your detached HEAD. some_rev cannot be a branch or tag in this case, since gitlab CI is not fetching any upstream branch/tag revs, and some_rev cannot be a commit in a different branch or tag, since your local clone will only contain commits that are direct parents of your detached HEAD.

@aguschin it sounds like in gitlab CI, maybe you should always be doing

dvc get <upstream_git_url.git> --rev <revision_being_built_in_ci>

I think gitlab CI should have env vars that can be used to fill in the upstream URL and the revision. Otherwise, you are probably going to run into other issues with using the local repo .

@pmrowla pmrowla added the p3-nice-to-have It should be done this or next sprint label Jun 21, 2021
@aguschin
Copy link
Contributor Author

aguschin commented Jun 21, 2021

@jorgeorpinel

What change would you suggest here? (What would you expect?)

I was expecting that artefacts related to the commit eae5019 will be downloaded (in this case they are specified in dvc.lock).

push/pull don't need Git at all. They use the metafiles present in the project (and config file).

Maybe dvc get . should use the same mechanics?

@pmrowla, thank you, I think now it's the best option. For the record, in gitlab CI this will look like

dvc get $CI_PROJECT_URL --rev $CI_COMMIT_SHA path-to-file

@aguschin
Copy link
Contributor Author

aguschin commented Jun 23, 2021

It turns out the same happens in GitHub Actions: https://github.com/iterative/yolov5/runs/2893101649?check_suite_focus=true
to follow the same approach here, we can use

dvc get https://github.com/$GITHUB_REPOSITORY --rev $GITHUB_SHA path-to-file

@jorgeorpinel
Copy link
Contributor

jorgeorpinel commented Jun 23, 2021

the issue here is that the repo being cloned does have a HEAD ... but apparently refs/remotes/origin/HEAD isn't created when you clone a repo that is in a detached state

@pmrowla I see. Maybe addressing #3590 (comment) (applies to list/get/import) would also prevent this. Cc @aguschin so yes, get/import . wouldn't re-clone the repo in that case. I.e. this issue may be a dupe.

@jorgeorpinel
Copy link
Contributor

jorgeorpinel commented Jun 23, 2021

Wait according to #3590 (comment) at least list already doesn't re-clone local repos. And then that issue veered into some other UI-related discussions. So it probably makes sense to keep this one about get/import specifically.

@pmrowla
Copy link
Contributor

pmrowla commented Jun 23, 2021

@jorgeorpinel The comments in #3590 are specific to list, that change does not apply to get.

This issue is marked P3 because we can potentially change get to not clone the local repo. However, that change will only
fix the issue for when the user is trying to get the HEAD revision.

The real problem here is that the user's local repo (.) will only have the specific revisions fetched into the runner env by gitlab/github CI, so using

dvc get . --rev <any other branch or rev>

won't work at all in these CI environments.

If the user wants consistent dvc get behavior in these CI envs, they should use the proper git remote URL (and not the local repo .) as noted above.

@jorgeorpinel
Copy link
Contributor

local repo (.) will only have the specific revisions fetched into the runner env by gitlab/github CI

Yeah but as you suggest, that's not something DVC can do much about. So I basically see this issue as a request to not re-clone local repos with get/import.

@aguschin
Copy link
Contributor Author

aguschin commented Jun 23, 2021

Not sure it should be reported here, but I encountered the issue with this approach while trying to do this in CI job triggered by Studio. In that job $GITHUB_SHA references to some unknown commit which could not be found anywhere, thus dvc get https://github.com/$GITHUB_REPOSITORY --rev $GITHUB_SHA path-to-file fails:

+ dvc get https://github.com/iterative/yolov5 --rev a3a326f7237b5e997fb69f3611f51f7a80fcfda5 yolov5s.pt
ERROR: failed to get 'yolov5s.pt' from 'https://github.com/iterative/yolov5' - unknown Git revision 'a3a326f7237b5e997fb69f3611f51f7a80fcfda5'

complete log here: https://github.com/iterative/yolov5/runs/2893514130?check_suite_focus=true

Not sure what is the right way to handle this in Github Actions, any suggestions would be appreciated. Right now I've hardcoded dvc get to download file from the default branch, but it doesn't seem like general solution.

@pmrowla
Copy link
Contributor

pmrowla commented Jun 23, 2021

One other question I have here: what's the reason for using dvc get . yolo5s.pt instead of dvc pull yolo5s.pt?

@aguschin
Copy link
Contributor Author

aguschin commented Jun 23, 2021

@pmrowla, yolov5s.pt is not an output of dvc stage. It is versioned manually. Because of this, dvc pull is not working.

@pmrowla
Copy link
Contributor

pmrowla commented Jun 23, 2021

@aguschin
Copy link
Contributor Author

Sorry, it seems I didn't know how to use this. dvc pull yolov5s.pt.dvc works fine, while dvc pull yolov5s.pt fails:

ERROR: failed to pull data from the cloud - 'yolo5s.pt' does not exist as an output or a stage name in 'dvc.yaml': Stage 'yolo5s.pt' not found inside 'dvc.yaml' file

@skshetry
Copy link
Member

Sorry, it seems I didn't know how to use this. dvc pull yolov5s.pt.dvc works fine, while dvc pull yolov5s.pt fails:

ERROR: failed to pull data from the cloud - 'yolo5s.pt' does not exist as an output or a stage name in 'dvc.yaml': Stage 'yolo5s.pt' not found inside 'dvc.yaml' file

It should be supported if I remember correctly. I'll take a look into it tomorrow.

@karajan1001
Copy link
Contributor

karajan1001 commented Jun 28, 2021

56B0BF1F44A0452DD3C33C913A41BCB3

And for the dvc pull, it should support files/directories. This is a new bug.

  targets       Limit command scope to these tracked files/directories,
                .dvc files, or stage names.

@daavoo daavoo added A: data-sync Related to dvc get/fetch/import/pull/push A: get Related to dvc get and removed A: data-sync Related to dvc get/fetch/import/pull/push A: get Related to dvc get labels Feb 22, 2022
@sukhovvl
Copy link

sukhovvl commented May 29, 2022

I've just had a very similar problem with dvc params diff command (as well as similar commands such as plots diff, metrics diff etc) while trying to generate CML report within Gitlab CI workflow.

  script:
    - dvc pull data

    - pip install -r requirements.txt
    - dvc repro

    # Compare metrics to master
    - git fetch --prune
    - dvc metrics diff --show-md master >> report.md

Most basic scripts such as the one above result in:

ERROR: unexpected error - No active branch (detached HEAD)            

I could create a dummy branch like the thread author suggested, but I wonder if there is a better way to solve this?

@dberenbaum
Copy link
Contributor

@sukhovvl Could you try cml ci --unshallow or:

- uses: actions/checkout@v3
  with:
    fetch-depth: 0

@pmrowla
Copy link
Contributor

pmrowla commented Jun 7, 2022

@dberenbaum I think this is a separate issue. If it was a shallow/depth problem it would likely show up as an unknown git revision/object SHA and not the "no active branch" error.

@sukhovvl can you run your diff command with -v in gitlab CI and then post the full error traceback here

@sukhovvl
Copy link

sukhovvl commented Jun 7, 2022

Sure, here it is:

dvc params diff master -v --show-md --all
[187](#L187)2022-06-07 10:10:43,466 ERROR: unexpected error - No active branch (detached HEAD)
[188](#L188)------------------------------------------------------------
[189](#L189)Traceback (most recent call last):
[190](#L190)  File "/usr/local/lib/python3.8/dist-packages/dvc/cli/__init__.py", line 90, in main
[191](#L191)    ret = cmd.do_run()
[192](#L192)  File "/usr/local/lib/python3.8/dist-packages/dvc/cli/command.py", line 22, in do_run
[193](#L193)    return self.run()
[194](#L194)  File "/usr/local/lib/python3.8/dist-packages/dvc/commands/params.py", line 18, in run
[195](#L195)    diff = self.repo.params.diff(
[196](#L196)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/__init__.py", line 13, in diff
[197](#L197)    return diff(self.repo, *args, **kwargs)
[198](#L198)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/diff.py", line 14, in diff
[199](#L199)    params = repo.params.show(*args, **kwargs, revs=[a_rev, b_rev])
[200](#L200)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/__init__.py", line 8, in show
[201](#L201)    return show(self.repo, *args, **kwargs)
[202](#L202)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/__init__.py", line 48, in wrapper
[203](#L203)    return f(repo, *args, **kwargs)
[204](#L204)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/show.py", line 132, in show
[205](#L205)    active_branch = repo.scm.active_branch()
[206](#L206)  File "/usr/local/lib/python3.8/dist-packages/scmrepo/git/__init__.py", line 289, in _backend_func
[207](#L207)    result = func(*args, **kwargs)
[208](#L208)  File "/usr/local/lib/python3.8/dist-packages/scmrepo/git/backend/pygit2.py", line 255, in active_branch
[209](#L209)    raise SCMError("No active branch (detached HEAD)")
[210](#L210)scmrepo.exceptions.SCMError: No active branch (detached HEAD)
[211](#L211)------------------------------------------------------------
[212](#L212)2022-06-07 10:10:43,818 DEBUG: [Errno 95] no more link types left to try out: [Errno 95] 'reflink' is not supported by <class 'dvc.fs.local.LocalFileSystem'>: [Errno 95] Operation not supported
[213](#L213)------------------------------------------------------------
[214](#L214)Traceback (most recent call last):
[215](#L215)  File "/usr/local/lib/python3.8/dist-packages/dvc/cli/__init__.py", line 90, in main
[216](#L216)    ret = cmd.do_run()
[217](#L217)  File "/usr/local/lib/python3.8/dist-packages/dvc/cli/command.py", line 22, in do_run
[218](#L218)    return self.run()
[219](#L219)  File "/usr/local/lib/python3.8/dist-packages/dvc/commands/params.py", line 18, in run
[220](#L220)    diff = self.repo.params.diff(
[221](#L221)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/__init__.py", line 13, in diff
[222](#L222)    return diff(self.repo, *args, **kwargs)
[223](#L223)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/diff.py", line 14, in diff
[224](#L224)    params = repo.params.show(*args, **kwargs, revs=[a_rev, b_rev])
[225](#L225)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/__init__.py", line 8, in show
[226](#L226)    return show(self.repo, *args, **kwargs)
[227](#L227)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/__init__.py", line 48, in wrapper
[228](#L228)    return f(repo, *args, **kwargs)
[229](#L229)  File "/usr/local/lib/python3.8/dist-packages/dvc/repo/params/show.py", line 132, in show
[230](#L230)    active_branch = repo.scm.active_branch()
[231](#L231)  File "/usr/local/lib/python3.8/dist-packages/scmrepo/git/__init__.py", line 289, in _backend_func
[232](#L232)    result = func(*args, **kwargs)
[233](#L233)  File "/usr/local/lib/python3.8/dist-packages/scmrepo/git/backend/pygit2.py", line 255, in active_branch
[234](#L234)    raise SCMError("No active branch (detached HEAD)")
[235](#L235)scmrepo.exceptions.SCMError: No active branch (detached HEAD)
[236](#L236)During handling of the above exception, another exception occurred:
[237](#L237)Traceback (most recent call last):
[238](#L238)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/utils.py", line 28, in _link
[239](#L239)    func(from_path, to_path)
[240](#L240)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/base.py", line 263, in reflink
[241](#L241)    return self.fs.reflink(from_info, to_info)
[242](#L242)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/local.py", line 156, in reflink
[243](#L243)    return System.reflink(path1, path2)
[244](#L244)  File "/usr/local/lib/python3.8/dist-packages/dvc/system.py", line 112, in reflink
[245](#L245)    System._reflink_linux(source, link_name)
[246](#L246)  File "/usr/local/lib/python3.8/dist-packages/dvc/system.py", line 96, in _reflink_linux
[247](#L247)    fcntl.ioctl(d.fileno(), FICLONE, s.fileno())
[248](#L248)OSError: [Errno 95] Operation not supported
[249](#L249)The above exception was the direct cause of the following exception:
[250](#L250)Traceback (most recent call last):
[251](#L251)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/utils.py", line 69, in _try_links
[252](#L252)    return _link(link, from_fs, from_path, to_fs, to_path)
[253](#L253)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/utils.py", line 32, in _link
[254](#L254)    raise OSError(
[255](#L255)OSError: [Errno 95] 'reflink' is not supported by <class 'dvc.fs.local.LocalFileSystem'>
[256](#L256)The above exception was the direct cause of the following exception:
[257](#L257)Traceback (most recent call last):
[258](#L258)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/utils.py", line 124, in _test_link
[259](#L259)    _try_links([link], from_fs, from_file, to_fs, to_file)
[260](#L260)  File "/usr/local/lib/python3.8/dist-packages/dvc/fs/utils.py", line 77, in _try_links
[261](#L261)    raise OSError(
[262](#L262)OSError: [Errno 95] no more link types left to try out
[263](#L263)------------------------------------------------------------
[264](#L264)2022-06-07 10:10:43,820 DEBUG: Removing '/tmp/tmp.R42vmkXvGr/.cml/cml-a3zshex9w6/testproject/.nAQiZf9PGGjTJBNbdR3nWj.tmp'
[265](#L265)2022-06-07 10:10:43,820 DEBUG: Removing '/tmp/tmp.R42vmkXvGr/.cml/cml-a3zshex9w6/testproject/.nAQiZf9PGGjTJBNbdR3nWj.tmp'
[266](#L266)2022-06-07 10:10:43,820 DEBUG: Removing '/tmp/tmp.R42vmkXvGr/.cml/cml-a3zshex9w6/testproject/.nAQiZf9PGGjTJBNbdR3nWj.tmp'
[267](#L267)2022-06-07 10:10:43,821 DEBUG: Removing '/tmp/tmp.R42vmkXvGr/.cml/cml-a3zshex9w6/testproject/Model/.dvc/cache/.9QbNBmA8sFKpMUpCwHs8pC.tmp'
[268](#L268)2022-06-07 10:10:43,832 DEBUG: Version info for developers:
[269](#L269)DVC version: 2.10.2 (pip)
[270](#L270)---------------------------------
[271](#L271)Platform: Python 3.8.10 on Linux-5.13.0-1027-gcp-x86_64-with-glibc2.29
[272](#L272)Supports:
[273](#L273) gs (gcsfs = 2022.5.0),
[274](#L274) webhdfs (fsspec = 2022.5.0),
[275](#L275) http (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
[276](#L276) https (aiohttp = 3.8.1, aiohttp-retry = 2.4.6),
[277](#L277) s3 (s3fs = 2022.5.0, boto3 = 1.21.21)
[278](#L278)Cache types: hardlink, symlink
[279](#L279)Cache directory: ext4 on /dev/root
[280](#L280)Caches: local
[281](#L281)Remotes: gs, s3
[282](#L282)Workspace directory: ext4 on /dev/root
[283](#L283)Repo: dvc, git
[284](#L284)Having any troubles? Hit us up at https://dvc.org/support, we are always happy to help!
[285](#L285)2022-06-07 10:10:43,836 DEBUG: Analytics is enabled.
[286](#L286)2022-06-07 10:10:44,101 DEBUG: Trying to spawn '['daemon', '-q', 'analytics', '/tmp/tmp8tu4s5h3']'
[287](#L287)2022-06-07 10:10:44,104 DEBUG: Spawned '['daemon', '-q', 'analytics', '/tmp/tmp8tu4s5h3']'

[289](#L289)
Cleaning up project directory and file based variables
00:00
[291](#L291)
ERROR: Job failed: exit code 1

And here is the gitlab-ci.yml producting this output:

.train_and_report: &train_stage
  stage: train
  script: 
    - pip install -r requirements.txt
    - dvc pull -r ${DVC_STORAGE} --run-cache
    - dvc repro
    - git fetch --prune
    - dvc params diff master -v --show-md --all

@pmrowla
Copy link
Contributor

pmrowla commented Jun 7, 2022

@sukhovvl your issue is a bug which has already been resolved in main and the fix will be available in the next DVC release (see #7750)

@mattseddon mattseddon closed this as not planned Won't fix, can't repro, duplicate, stale Mar 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A: data-sync Related to dvc get/fetch/import/pull/push bug Did we break something? p3-nice-to-have It should be done this or next sprint
Projects
None yet
Development

No branches or pull requests

9 participants