Workspaces -- How to commit, push, and publish

I recently created a workspace for my Question Bank Creator project following the pattern laid out in the Rust Book. I used cargo new to create a new project directory and then copied my files over from the old, independent directory where the project has been residing up until now. That worked fine, and, as long as I'm working in the new project directory, I'm able to commit the project without any hiccups. I also used cargo new to create a second folder where (eventually) another part of the project will reside. (Nothing there to commit since at this point it's still in the "Hello, World!" stage.)

I have a repository for Question Bank Creator, but it was made before I decided that I needed a workspace. Just yesterday, after cleaning up some annoying warnings, I committed the project and then tried to push it to Github, and that is where I got stuck. No surprise there, since the working directory is now a sub-directory in the workspace folder. So, here's my question: Working in a workspace, how do I commit and push my project to Github? I will eventually want to publish, so maybe I should include that in my question. How do I publish a project contained in a workspace?

Actually, I did not really understood workspace use after reading the official book and the one from JIm Blandy. With Google and some AI support, my feeling is now that Workspaces are just something that one uses locally. Workspaces are never published as a whole to Crates.io. The member packages can be published to Crates.io or pushed to GitHub. But I have never tested it, as I do not yet have a own workspace, and as I did not want to pollute Crates.io. My current understanding of Workspaces is summarized in

Packages - Rust for C-Programmers

Cargo workspaces - Rust for C-Programmers

I will try to update that content when required.

[EDIT]

I forgot to mention that you might find more details in the

Introduction - The Cargo Book

and that there is a separate package offering more support:

crates.io: Rust Package Registry

I'm unclear on what your problem is. You should just be able to commit & push like a normal Git repository, and running git commit in a subdirectory shouldn't be a problem. Did you somehow create one local "outer" Git repository and also create an "inner" Git repository in a subdirectory? A workspace should just be a single repository, rooted at the directory with the workspace's Cargo.toml and containing all subdirectories (except for ignored stuff like target/).

@StefanSalewski & @jwodder -- Check my understanding of what you are telling me.

  1. Workspaces exist and are used only on the computer where I am doing my work and don't affect anything on Github.

  2. I should be able to push my working project folder up to its corresponding Github repository independent of whatever I'm doing in the workspace.

Check me on this. If these statements are correct, then the issue is probably centered around the fact that my new working directory (on my drive as a subfolder of the workspace) isn't properly set up to interact with its corresponding Github repository. If I'm right about that, then the question becomes: How do I take my new working directory and connect it to the Github repository that already exists for this project? Here's what I've tried:

git push --set-upstream origin master

and then it asked for a username and password, which I dutifully entered. Following that I got this message:

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/jtreagan/Question-Bank-Creator/'

I suppose I could create an all-new Github repository and then delete the old one. Is that my best solution? If I do that then I'll have to re-build all the issues under the issues tab that I've already identified, so creating an all-new repository isn't my favorite option.

Either I'm misunderstanding you, or this is completely wrong (Well, aside from "Workspaces exist"). Workspaces are largely intended for cases where you're developing multiple Rust packages together that depend on each other, so you store the code for all of those packages together in a single Git repository[1] – which in turn corresponds to a single GitHub repository — and cargo handles the interdependencies gracefully.

I'm not even sure how you'd get into a state where a subdirectory of a Git repository "isn't properly set up to interact with its corresponding GitHub repository."

(EDIT: I just realized you said "GitHub repository" and not "Git repository." It's easier to mess up that connection, but messing it up should affect the entire workspace repository, not just a subdirectory.)

I don't believe that's your best solution. Even if your local Git setup is completely screwy, you can still create a new local Git repository, copy everything over from the old repository, commit, and then do a force push to the GitHub repository to complete replace the contents & history while keeping the issues intact.


Regarding the error when pushing: In order to push to a GitHub repository, you should generally be using the SSH URL for the repository instead of the HTTPS URL. You can get this URL by clicking on the green "Code" button at the top-right of the file listing on the GitHub repo's main page, then click "SSH" in the popup, then copy the URL (starting with git@github.com) displayed below. To get Git to use this URL instead of the HTTPS URL, run git remote set-url origin INSERT_SHH_URL_HERE. You will also need to have an SSH public key added to your GitHub account; see Adding a new SSH key to your GitHub account - GitHub Docs.


  1. Often called a "monorepo" in non-Rust contexts ↩︎

My first reply was mostly related to the fact that we typically do not publish a complete workspace to Crates.io, but only single (or all) packages as described in cargo publish - The Cargo Book. I have assumed that hosting at GitHub is something independent that we can do however we want -- the whole workspace or single packages. When we want to use a workspace, we typically create a toplevel directory with the workspace name, which contains the associated packages. So when we want to host the complete workspace at github, we ensure that the created toplevel package directory is an initialized GIT project. And when we want to host only the single packages, then those directories have to be initialized GIT projects. I would assume that we get problems when the toplevel workspace project is initialized as a git project, and subfolders as well. But as I told, I never tested that, sorry.

My problem with being able to push my project up to the Github repository has been solved. My process was as follows:

First, I created the new workspace, again following the pattern laid out in the Rust Book.

Second, I created a new working folder, within the new workspace directory, for my project using Cargo new. That added the new directory to the members line in the workspace's Cargo.toml file.

Third, I copied everything over from the old working folder.

Then I went to work, made some corrections, decided a commit & push was appropriate and then nothing worked. It turns out that the .git folder didn't copy over from the old working directory. Probably because it was hidden. So, while the new working folder had a .git folder, it only contained the most current commit and wasn't connected in any way to my project's Github repository. Anyway, I copied the old .git folder over and everything committed and pushed just fine.

Thanks for your help, everyone. :grinning:

My main issue has been solved, but I still have some questions about how workspaces interact with GitHub and my Github repositories. Now that I have a workspace locally, should I go on to create a repository on Github for that workspace? It does make sense. The workspace gathers my interdependent crates into one place. I can see how that might help a potential collaborator accomplish his/her project-related tasks. That leads me to the question of how do I migrate my current Github repository to that new workspace repository?

Just rearrange your local Git repository so that it contains the workspace, then commit & push. I can't give more details without knowing exactly how your stuff is laid out locally.

If I understand your structure, you started committing from a subdirectory of your current project (replace a/ by the name of your workspace directory)

a/
  |
  +- Cargo.toml "workspace"
  +- other files at workspace level
  +- rebuild6/
      |
      +- .git/                  | only this is in 
      +- Cargo.toml "rebuild6"  | your
      +- other files            | repository
      +- src/                   | now

You just have to move everything that is under rebuild6/ (or whatever name you have for your initial crate—git doesn't see that directory yet) in a new rebuild6/rebuild6/, except .git, so that git sees the subdirectory rebuild6/.

It's important to move those items with a git move, so you keep your history for those items (otherwise you may as well start with a fresh repository). Don't modify the files at the same time you're moving them, this might confuse Git.

a/
  |
  +- Cargo.toml "workspace"
  +- other files at workspace level
  +- rebuild6/
      |
      +- .git/
      +- rebuild6/
          |
          +- Cargo.toml "rebuild6"
          +- other files
          +- src/

Commit, then move the rest so that git finally sees your whole project.

a/
  |
  +- rebuild6/
      |
      +- .git/
      +- Cargo.toml "workspace"
      +- other files at workspace level
      +- rebuild6/
          |
          +- Cargo.toml "rebuild6"
          +- other files
          +- src/

Finally, add the new relevant files to git and commit. After that, you can cleanup the structure by moving everything back under a/, including .git (you'll have to rename the top rebuild6 before).

1 Like

This isn't correct. Git does not store move information. Moving the files with any file manager, then git adding all the additions and deletions, is equivalent to using git mv. (git status will detect the identical files and display it as a rename operation, but that is a display convenience, not part of the data in the repository.)

For @preacherdad's situation, I would suggest just moving the .git directory itself up to the workspace level, then adding all the resulting changes. That’ll do the same thing in fewer steps.

2 Likes

I know git has no real move operation, but it's a safety that saved my history more than once, and using the other method sometimes failed.

@Redglyph You put in a lot of work creating those charts. Thanks! The chart copied below is correct for the current setup:

a/
  |
  +- Cargo.toml "workspace"
  +- other files at workspace level
  +- rebuild6/                                     +-eq_editor
      |                                            |
      +- .git/                                     |
      +- Cargo.toml "rebuild6"                     +-Cargo.toml "eq_editor"
      +- other files                               |
      +- src/                                      +-src/

I added the right column to show that there is another related directory in the workspace. eq_editor (equation editor) has nothing yet to commit, so I didn't include a .git/ directory for it. Still it is part of the workspace and will eventually need it's own commits.

Is the second rebuild6 directory really necessary? It adds another layer of complexity. Would it work to simply use @kpreid's suggestion of just moving the .git/ folder from rebuild6 up one level to the main workspace directory?

After all that is taken care of, what will the workflow look like for me? Say I make some significant changes to QBC (rebuild6) and need to commit it. Do I change to the workspace directory and then git commit from there? Would I do the same once eq_editor is ready for a first commit?

Yes.

Git commands will look for .git in the current directory or its parent directories. You don't need to change directories first — you can be anywhere within the repository working tree. (This is like how Cargo works — it looks for a Cargo.toml in the current directory or its parent directories to find a package or workspace.)

While you work through this I recommend using git status often to see what Git thinks its situation is (e.g. what files haven't been added or committed yet).

1 Like

I redid a few tests and it seems fine, so I'm not sure why I had those problems sometimes (files were not modified). Thanks for pointing that out!

It's just to proceed by steps, but if you feel comfortable, you can do it all at once: moving existing files and adding new ones by moving .git. Maybe move your .gitignore at the same time.

Look on git-log documentation for options --find-renames, --find-copies and, as last resort --find-copies-harder.

Also note how all these options are for the git log and, notably, not for the git add or git commit.

Git detects renames when it shows you the log, which, by extension, implies that different tools used to show these logs may use different approaches to detect them and show different results.

Very, very good advice. It took lots of git add, git status and git reset commands, but I think I finally got it right.

Now for the next step. Now that the workspace is committing properly, how does that affect the project's Github repository when I git push the workspace up to Github?

1 Like

It will reflect what git has committed locally, so you'll see everything at the .git level and below, except what's defined in the .gitignore file (and except empty directories).

Note that if you try to publish a workspace, you'll get an error message asking you to specify with the option -p <package> which package you want to publish. There's an option to publish them all, but I believe it's unstable; there are other tools to do that, but I've never tried.

It's possible to publish a package with a workspace, which means you define in Cargo.toml a package with all the information for the publication, and a workspace which doesn't include the package itself, but rather other ones like examples and other separate tests. It's not uncommon to see that in other crates (e.g. syn). I don't think that's what you want, though. AFAIK, those other packages aren't part of what is published by default.