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

Add new 'last-edit' tour versioning scheme. #252

Open
wants to merge 1 commit 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,15 @@ In order to make it simpler to call common commands, CodeTour will prompt you wi

When you record a tour, you'll be asked which git "ref" to associate it with. This allows you to define how resilient you want the tour to be, as changes are made to the respective codebase.

<img width="600px" src="https://user-images.githubusercontent.com/116461/76692462-3f8ff700-6614-11ea-88a1-6fbded8e8507.png" />
<img width="600px" src="https://user-images.githubusercontent.com/6195185/181656075-e90b479c-fbf0-45fb-a49d-65cb40b43320.png" />

You can choose to associate with the tour with the following ref types:

- `None` - The tour isn't associated with any ref. The benefit of this option is that it enables the code to be edited as part of the tour, since the tour will walk the user through whichever branch/commit they have checked out (e.g. interactive tutorials).
- `Current Branch` - The tour is restricted to the current branch. This can have the same resiliency challenges as `None`, but, it allows you to maintain a special branch for your tours that can be versioned seperately. If the end-user has the associated branch checked out, then the tour will enable them to make edits to files as its taken. Otherwise, the tour will replay with read-only files.
- `Current Commit` - The tour is restricted to the current commit, and therefore, will never get out of sync. If the end-user's `HEAD` points at the specified commit, then the tour will enable them to make edits to files as its taken. Otherwise, the tour will replay with read-only files.
- Tags - The tour is restricted to the selected tag, and therefore, will never get out of sync. The repo's entire list of tags will be displayed, which allows you to easily select one.
- `Last Commit that modified the tour` - The tour is restricted to the most recent commit that modified the tour file. If the end-user's `HEAD` points at the specified commit, then the tour will enable them to make edits to files as its taken. Otherwise, the tour will replay with read-only files. This allows you to bundle a code tour along with the changes that it's explaining into a single git commit.

At any time, you can edit the tour's ref by right-clicking it in the `CodeTour` tree and selecting `Change Git Ref`. This let's you "rebase" a tour to a tag/commit as you change/update your code and/or codebase.

Expand Down
17 changes: 17 additions & 0 deletions src/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,25 @@ export interface RepositoryState {
readonly refs: Ref[];
}

export interface LogOptions {
/** Max number of log entries to retrieve. If not specified, the default is 32. */
readonly maxEntries?: number;
readonly path?: string;
}

export interface Commit {
readonly hash: string;
readonly message: string;
readonly parents: string[];
readonly authorDate?: Date;
readonly authorName?: string;
readonly authorEmail?: string;
readonly commitDate?: Date;
}

export interface Repository {
readonly state: RepositoryState;
log(options?: LogOptions): Promise<Commit[]>;
}

interface GitAPI {
Expand Down
2 changes: 1 addition & 1 deletion src/notebook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CodeTourNotebookProvider implements vscode.NotebookSerializer {
let steps: any[] = [];

for (let item of tour.steps) {
const uri = await getStepFileUri(item, workspaceRoot, tour.ref);
const uri = await getStepFileUri(item, workspaceRoot, tour);
const document = await vscode.workspace.openTextDocument(uri);

const startLine = item.line! > 10 ? item.line! - 10 : 0;
Expand Down
2 changes: 1 addition & 1 deletion src/player/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ async function renderCurrentStep() {
}

const workspaceRoot = store.activeTour?.workspaceRoot;
const uri = await getStepFileUri(step, workspaceRoot, currentTour.ref);
const uri = await getStepFileUri(step, workspaceRoot, currentTour);

let line = step.line
? step.line - 1
Expand Down
7 changes: 7 additions & 0 deletions src/recorder/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,13 @@ export function registerRecorderCommands() {
description: "Keep the tour associated with a specific commit",
ref: repository.state.HEAD ? repository.state.HEAD.commit! : "",
alwaysShow: true
},
{
label: "$(git-commit) Last commit that modified the tour",
description:
"Keep the tour associated with the most recent commit that modified the tour",
ref: "last-edit",
alwaysShow: true
}
];

Expand Down
2 changes: 1 addition & 1 deletion src/store/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export async function exportTour(tour: CodeTour) {
}

const workspaceRoot = getWorkspaceUri(tour);
const stepFileUri = await getStepFileUri(step, workspaceRoot, tour.ref);
const stepFileUri = await getStepFileUri(step, workspaceRoot, tour);
const contents = await readUriContents(stepFileUri);

delete step.markerTitle;
Expand Down
25 changes: 21 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ export function getFileUri(file: string, workspaceRoot?: Uri) {
export async function getStepFileUri(
step: CodeTourStep,
workspaceRoot?: Uri,
ref?: string
tour?: CodeTour
): Promise<Uri> {
const ref = tour?.ref;
let uri;
if (step.contents) {
uri = Uri.parse(`${FS_SCHEME}://current/${step.file}`);
Expand All @@ -79,10 +80,26 @@ export async function getStepFileUri(
? Uri.parse(step.uri)
: getFileUri(step.file!, workspaceRoot);

if (api && ref && ref !== "HEAD") {
if (api && tour && ref && ref !== "HEAD") {
const repo = api.getRepository(uri);

if (
if (ref === "last-edit") {
const tourUri = Uri.parse(tour.id);
const tourPath = tourUri.path;

if (repo && repo.state.HEAD) {
const logResults = await repo.log({
maxEntries: 1,
path: tourPath
});
if (logResults.length > 0) {
const commit = logResults[0];
if (repo.state.HEAD.commit !== commit.hash) {
uri = await api.toGitUri(uri, commit.hash);
}
}
}
} else if (
repo &&
repo.state.HEAD &&
repo.state.HEAD.name !== ref && // The tour refs the user's current branch
Expand Down Expand Up @@ -184,7 +201,7 @@ async function updateMarkerTitleForStep(tour: CodeTour, stepNumber: number) {
const uri = await getStepFileUri(
tour.steps[stepNumber],
getWorkspaceUri(tour),
tour.ref
tour
);

const document = await workspace.openTextDocument(uri);
Expand Down