Fix/git sync issues (#5626)

* Display the push/pull/checkout errors correctly

* use branch name to refresh the workspace

* fix checkout

* fetch the staged changes inside the modal

* fix password login with git
This commit is contained in:
James Gatz authored and GitHub committed 2023-01-06 15:36:43 +00:00
1 parent 21c78170b2
commit 9a4e5caa86
6 files changed
+108 -37

No files matched your search

+10 -3
View File
@@ -312,7 +312,7 @@ export class GitVCS {
async fetch(
singleBranch: boolean,
depth: number,
depth?: number,
gitCredentials?: GitCredentials | null,
) {
console.log('[git] Fetch remote=origin');
@@ -352,13 +352,20 @@ export class GitVCS {
console.log('[git] Checkout', {
branch,
});
const branches = await this.listBranches();
const localBranches = await this.listBranches();
const remoteBranches = await this.listRemoteBranches();
const branches = [...localBranches, ...remoteBranches];
if (branches.includes(branch)) {
await git.checkout({ ...this._baseOpts, ref: branch, remote: 'origin' });
await git.checkout({
...this._baseOpts,
ref: branch,
remote: 'origin',
});
} else {
await this.branch(branch, true);
}
}
async undoPendingChanges(fileFilter?: string[]) {
@@ -5,7 +5,7 @@ import { useFetcher, useParams } from 'react-router-dom';
import { docsGitSync } from '../../../common/documentation';
import { GitRepository } from '../../../models/git-repository';
import { getOauth2FormatName } from '../../../sync/git/utils';
import { GitRepoLoaderData, PushToGitRemoteResult } from '../../routes/git-actions';
import { GitRepoLoaderData, PullFromGitRemoteResult, PushToGitRemoteResult } from '../../routes/git-actions';
import { type DropdownHandle, Dropdown } from '../base/dropdown/dropdown';
import { DropdownButton } from '../base/dropdown/dropdown-button';
import { DropdownDivider } from '../base/dropdown/dropdown-divider';
@@ -34,7 +34,7 @@ export const GitSyncDropdown: FC<Props> = ({ className, gitRepository }) => {
const [isGitStagingModalOpen, setIsGitStagingModalOpen] = useState(false);
const gitPushFetcher = useFetcher<PushToGitRemoteResult>();
const gitPullFetcher = useFetcher();
const gitPullFetcher = useFetcher<PullFromGitRemoteResult>();
const gitCheckoutFetcher = useFetcher();
const gitRepoDataFetcher = useFetcher<GitRepoLoaderData>();
@@ -50,8 +50,6 @@ export const GitSyncDropdown: FC<Props> = ({ className, gitRepository }) => {
useEffect(() => {
const errors = [
...gitPushFetcher.data?.errors ?? [],
...gitPullFetcher.data?.errors ?? [],
...gitCheckoutFetcher.data?.errors ?? [],
];
if (errors.length > 0) {
showAlert({
@@ -59,7 +57,31 @@ export const GitSyncDropdown: FC<Props> = ({ className, gitRepository }) => {
message: errors.join('\n'),
});
}
}, [gitCheckoutFetcher.data?.errors, gitPullFetcher.data?.errors, gitPushFetcher.data?.errors]);
}, [gitPushFetcher.data?.errors]);
useEffect(() => {
const errors = [
...gitPullFetcher.data?.errors ?? [],
];
if (errors.length > 0) {
showAlert({
title: 'Pull Failed',
message: errors.join('\n'),
});
}
}, [gitPullFetcher.data?.errors]);
useEffect(() => {
const errors = [
...gitCheckoutFetcher.data?.errors ?? [],
];
if (errors.length > 0) {
showAlert({
title: 'Checkout Failed',
message: errors.join('\n'),
});
}
}, [gitCheckoutFetcher.data?.errors]);
async function handlePush({ force }: { force: boolean }) {
gitPushFetcher.submit({
@@ -82,7 +104,7 @@ export const GitSyncDropdown: FC<Props> = ({ className, gitRepository }) => {
const isLoading = gitRepoDataFetcher.state === 'loading';
const isButton = !gitRepository || (isLoading && !gitRepoDataFetcher.data) || (gitRepoDataFetcher.data && 'errors' in gitRepoDataFetcher.data);
const { log, branches, branch: currentBranch, remoteBranches, changes, statusNames } = (gitRepoDataFetcher.data && 'log' in gitRepoDataFetcher.data) ? gitRepoDataFetcher.data : { log: [], branches: [], branch: '', remoteBranches: [], changes: [], statusNames: {} };
const { log, branches, branch: currentBranch, remoteBranches } = (gitRepoDataFetcher.data && 'log' in gitRepoDataFetcher.data) ? gitRepoDataFetcher.data : { log: [], branches: [], branch: '', remoteBranches: [] };
let dropdown: React.ReactNode = null;
@@ -236,12 +258,11 @@ export const GitSyncDropdown: FC<Props> = ({ className, gitRepository }) => {
/>
}
{isGitLogModalOpen && <GitLogModal branch={currentBranch} logs={log} onHide={() => setIsGitLogModalOpen(false)} />}
{isGitStagingModalOpen && <GitStagingModal
changes={changes}
branch={currentBranch}
statusNames={statusNames}
onHide={() => setIsGitStagingModalOpen(false)}
/>}
{isGitStagingModalOpen &&
<GitStagingModal
onHide={() => setIsGitStagingModalOpen(false)}
/>
}
</Fragment>
);
};
@@ -5,7 +5,7 @@ import { useFetcher, useParams } from 'react-router-dom';
import { strings } from '../../../common/strings';
import * as models from '../../../models';
import { CommitToGitRepoResult, GitChange, GitRollbackChangesResult } from '../../routes/git-actions';
import { CommitToGitRepoResult, GitChangesLoaderData, GitRollbackChangesResult } from '../../routes/git-actions';
import { IndeterminateCheckbox } from '../base/indeterminate-checkbox';
import { type ModalHandle, Modal, ModalProps } from '../base/modal';
import { ModalBody } from '../base/modal-body';
@@ -24,16 +24,7 @@ interface Item {
editable: boolean;
}
type Props = ModalProps & {
changes: GitChange[];
branch: string;
statusNames: Record<string, string>;
};
export const GitStagingModal: FC<Props> = ({
changes,
branch,
statusNames,
export const GitStagingModal: FC<ModalProps> = ({
onHide,
}) => {
const { organizationId, projectId, workspaceId } = useParams() as {
@@ -45,7 +36,7 @@ export const GitStagingModal: FC<Props> = ({
const formRef = useRef<HTMLFormElement>(null);
const [checkAllModified, setCheckAllModified] = React.useState(false);
const [checkAllUnversioned, setCheckAllUnversioned] = React.useState(false);
const gitChangesFetcher = useFetcher<GitChangesLoaderData>();
const gitCommitFetcher = useFetcher<CommitToGitRepoResult>();
const rollbackFetcher = useFetcher<GitRollbackChangesResult>();
@@ -53,6 +44,22 @@ export const GitStagingModal: FC<Props> = ({
modalRef.current?.show();
}, []);
useEffect(() => {
if (gitChangesFetcher.state === 'idle' && !gitChangesFetcher.data) {
gitChangesFetcher.load(`/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/git/changes`);
}
}, [organizationId, projectId, workspaceId, gitChangesFetcher]);
const {
changes,
branch,
statusNames,
} = gitChangesFetcher.data || {
changes: [],
branch: '',
statusNames: {},
};
const hasChanges = Boolean(changes.length);
const modifiedChanges = changes.filter(i => !i.status.includes('added'));
+2
View File
@@ -41,6 +41,7 @@ import Root from './routes/root';
import { initializeSentry } from './sentry';
const Project = lazy(() => import('./routes/project'));
const Workspace = lazy(() => import('./routes/workspace'));
const UnitTest = lazy(() => import('./routes/unit-test'));
const Debug = lazy(() => import('./routes/debug'));
const Design = lazy(() => import('./routes/design'));
@@ -123,6 +124,7 @@ const router = createMemoryRouter(
path: ':workspaceId',
id: ':workspaceId',
loader: async (...args) => (await import('./routes/workspace')).workspaceLoader(...args),
element: <Suspense fallback={<AppLoadingIndicator />}><Workspace /></Suspense>,
children: [
{
path: `${ACTIVITY_DEBUG}`,
@@ -46,8 +46,6 @@ export type GitRepoLoaderData = {
branches: string[];
remoteBranches: string[];
gitRepository: GitRepository | null;
changes: GitChange[];
statusNames: Record<string, string>;
} | {
errors: string[];
};
@@ -128,16 +126,12 @@ export const gitRepoLoader: LoaderFunction = async ({ params }): Promise<GitRepo
setGitVCS(vcs);
const { changes, statusNames } = await getGitChanges(vcs, workspace);
return {
branch: await vcs.getBranch(),
log: await vcs.log() || [],
branches: await vcs.listBranches(),
remoteBranches: await vcs.listRemoteBranches(),
gitRepository: gitRepository,
changes,
statusNames,
};
} catch (e) {
const errorMessage = e instanceof Error ? e.message : 'Error while fetching git repository.';
@@ -231,15 +225,13 @@ export const cloneGitRepoAction: ActionFunction = async ({
oauth2format,
};
} else {
const password = formData.get('password');
invariant(typeof password === 'string', 'Password is required');
const token = formData.get('token');
invariant(typeof token === 'string', 'Token is required');
const username = formData.get('username');
invariant(typeof username === 'string', 'Username is required');
repoSettingsPatch.credentials = {
password,
password: token,
username,
};
}
@@ -635,8 +627,16 @@ export const checkoutGitBranchAction: ActionFunction = async ({
const vcs = getGitVCS();
try {
await vcs.fetch(false, 1, repo?.credentials);
} catch (e) {
console.warn('Error fetching from remote');
}
const bufferId = await database.bufferChanges();
try {
await vcs.checkout(branch);
// Fetch the last 20 commits for the branch
await vcs.fetch(true, 20, repo?.credentials);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : err;
return {
@@ -644,6 +644,21 @@ export const checkoutGitBranchAction: ActionFunction = async ({
};
}
if (workspaceMeta) {
const log = (await vcs.log()) || [];
const author = log[0] ? log[0].commit.author : null;
const cachedGitLastCommitTime = author ? author.timestamp * 1000 : null;
await models.workspaceMeta.update(workspaceMeta, {
cachedGitLastCommitTime,
cachedGitRepositoryBranch: branch,
cachedGitLastAuthor: author?.name || null,
});
}
await database.flushChanges(bufferId);
return {};
};
@@ -828,6 +843,13 @@ export const pullFromGitRemoteAction: ActionFunction = async ({
const bufferId = await database.bufferChanges();
const providerName = getOauth2FormatName(gitRepository.credentials);
try {
await vcs.fetch(false, 1, gitRepository?.credentials);
} catch (e) {
console.warn('Error fetching from remote');
}
try {
await vcs.pull(gitRepository.credentials);
trackSegmentEvent(SegmentEvent.vcsAction, { ...vcsSegmentEventProperties('git', 'pull'), providerName });
+13 -1
View File
@@ -1,14 +1,17 @@
import { LoaderFunction } from 'react-router-dom';
import React from 'react';
import { LoaderFunction, Outlet, useLoaderData } from 'react-router-dom';
import { database } from '../../common/database';
import * as models from '../../models';
import { GitRepository } from '../../models/git-repository';
import { Project } from '../../models/project';
import { Workspace } from '../../models/workspace';
import { WorkspaceMeta } from '../../models/workspace-meta';
import { invariant } from '../../utils/invariant';
export interface WorkspaceLoaderData {
activeWorkspace: Workspace;
activeWorkspaceMeta?: WorkspaceMeta;
activeProject: Project;
gitRepository: GitRepository | null;
}
@@ -44,5 +47,14 @@ export const workspaceLoader: LoaderFunction = async ({
activeWorkspace: workspace,
activeProject,
gitRepository,
activeWorkspaceMeta: workspaceMeta,
};
};
const WorkspaceRoute = () => {
const workspaceData = useLoaderData() as WorkspaceLoaderData;
const branch = workspaceData.activeWorkspaceMeta?.cachedGitRepositoryBranch;
return <Outlet key={branch} />;
};
export default WorkspaceRoute;