Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@

---

## 🤝 1er octobre 2026 — Le module GitHub devient une suite de gestes

*6 leçons · exercices en étapes · sorties vérifiées sur Git 2.56 et PowerShell 7*

Les six exercices du module GitHub demandaient une seule commande. Chacun est maintenant un vrai parcours, vérifié sur l'état du dépôt et pas sur la commande tapée.

- **git remote** : ajouter `origin`, vérifier le lien avec `git remote -v`, le passer en SSH.
- **git push & pull** : une collègue a poussé ; récupérer son commit, ajouter une page, la pousser. Pousser trop tôt est refusé (« fetch first »), et si l'élève a committé avant de récupérer, le terminal lui donne la commande qui fusionne les deux historiques.
- **git fetch & clone** : télécharger, regarder ce qui arrive (`git log HEAD..origin/main`), puis intégrer.
- **Pull Requests** : une branche, un commit, `git push -u origin feature/contact`, et le lien que GitHub renvoie pour ouvrir la PR.
- **Stratégies de merge** : un commit de fusion avec `--no-ff`, puis la branche rangée. La théorie montre aussi `--squash` (et pourquoi `git branch -d` refuse ensuite) et le rebase, sur la branche de l'exercice.
- **GitHub Actions** : ranger un vrai workflow dans `.github/workflows/`, le committer, le pousser. Versions à jour : `actions/checkout@v7`, `actions/setup-node@v7`, Node 24.
- Sous Windows, `New-Item -ItemType Directory .github/workflows` crée aussi `.github`, comme dans PowerShell 7. `git pull --no-edit` est accepté.
- Écarts entre la théorie des leçons et le terminal : **120 → 90**.

---

## 📖 1er octobre 2026 — « Notre histoire » se lit dans l'ordre

*Page /story · sommaire · aucun chapitre réécrit*
Expand Down
12 changes: 12 additions & 0 deletions STORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -1233,6 +1233,18 @@ La même conversation a tranché une autre question. La page d'accueil promet un

---

### GitHub, geste par geste (1er octobre 2026)

Avec un dépôt distant qui bouge, les six leçons du module GitHub pouvaient enfin devenir des exercices complets. L'une d'elles se terminait sur `git status` : vérifier son dépôt, puis « GitHub Actions fera le reste ». L'élève n'avait rien appris d'Actions. Elle lui fait maintenant ranger un vrai workflow dans `.github/workflows/`, le committer et le pousser.

Le plus utile, dans ce chantier, a été de jouer l'élève qui se trompe d'ordre. S'il committe avant de récupérer le travail de sa collègue, `git pull` refuse : les historiques ont divergé. Le vrai Git propose trois options de configuration, ce qui n'aide pas un débutant. L'exercice lui donne la commande qui le sort de là, `git pull --no-rebase --no-edit`, et le `--no-edit` n'est pas un détail : sans lui, un vrai terminal ouvre un éditeur pour le message de fusion, souvent vim, dont beaucoup de débutants ne savent pas sortir. Même chose pour `git merge --no-ff`.

Deux vérifications ont changé ce que j'allais écrire. Sous Windows, `New-Item -ItemType Directory .github/workflows` crée bien `.github` au passage dans un vrai PowerShell 7, mais le simulateur prenait le mot `Directory` pour le nom du dossier : il le corrige maintenant. Et les versions des actions GitHub de la théorie dataient : `actions/checkout` et `actions/setup-node` en sont à la v7, Node 24 est la version maintenue. Je les ai lues sur les pages de publication de GitHub, pas dans un résultat de recherche : deux sources s'y contredisaient.

Six validateurs de plus ont disparu. Les écarts entre la théorie et le terminal passent de 120 à 90.

---

## Épilogue ouvert

Il y a des questions auxquelles on n'a pas encore de réponse.
Expand Down
27 changes: 27 additions & 0 deletions src/app/data/commands/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,20 @@ const PUSH_NAME_MISMATCH = (remote: string, upstreamBranch: string) => [
'',
];

/** `git pull --ff-only` when both sides have commits (git 2.56, 1 October 2026). */
const PULL_FF_ONLY_DIVERGENT = [
"hint: Diverging branches can't be fast-forwarded, you need to either:",
'hint:',
'hint: \tgit merge --no-ff',
'hint:',
'hint: or:',
'hint:',
'hint: \tgit rebase',
'hint:',
'hint: Disable this message with "git config set advice.diverging false"',
'fatal: Not possible to fast-forward, aborting.',
];

const PULL_NOT_DEFAULT_REMOTE = (remote: string) => [
`You asked to pull from the remote '${remote}', but did not specify`,
'a branch. Because this is not the default configured remote',
Expand Down Expand Up @@ -2383,6 +2397,11 @@ export function handleGit(
// --rebase prints what a merge prints when the branch is up to date or can be
// fast-forwarded; only replaying diverged commits differs (git 2.56, 1 October 2026).
const rebase = [o.short.delete('r'), o.long.delete('rebase')].some(Boolean);
// --no-rebase merges diverged branches; --ff-only refuses them.
const merge = o.long.delete('no-rebase');
const ffOnly = o.long.delete('ff-only');
// --no-edit keeps the merge message git proposes: this terminal never opens an editor anyway.
o.long.delete('no-edit');
if (o.short.size || o.long.size) return notSimulated(`L'option ${[...o.short].map((c) => `-${c}`).concat([...o.long.keys()].map((k) => `--${k}`))[0]} de git pull`, newState);
const [remote, branch] = o.positional;
let ref: string;
Expand Down Expand Up @@ -2418,6 +2437,14 @@ export function handleGit(
if (!theirs) return fail(1, `fatal: couldn't find remote ref ${ref.slice(ref.indexOf('/') + 1)}`);
if (tip && isAncestor(now.objects ?? {}, theirs, tip)) return { lines: [...fromLines, { text: 'Already up to date.', type: 'output' }], newState };
if (tip && !isAncestor(now.objects ?? {}, tip, theirs)) {
if (ffOnly) return { lines: [...fromLines, ...PULL_FF_ONLY_DIVERGENT.map(err)], newState, status: 128 };
if (merge) {
// The merge commit names the branch and where it came from: "Merge branch 'main' of <url>".
const from = ref.slice(0, ref.indexOf('/'));
const message = `Merge branch '${ref.slice(from.length + 1)}' of ${now.remotes[from].replace(/\.git$/, '')}`;
const merged = handleGit(newState, ['merge', '-m', message, ref], env, resolve);
return { ...merged, lines: [...fromLines, ...merged.lines] };
}
if (rebase) return notSimulated('git pull --rebase sur des branches divergentes', newState);
return { lines: [...fromLines, ...PULL_DIVERGENT.map(err)], newState, status: 128 };
}
Expand Down
19 changes: 12 additions & 7 deletions src/app/data/commands/windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,18 @@ export function handleWindows(
// ── New-Item: file or directory ───────────────────────────────────────────
case 'new-item':
case 'ni': {
const isDir = args.some((a) => a.toLowerCase() === 'directory');
const nameIdx = args.findIndex((a) => a.toLowerCase() === '-name');
const pathIdx = args.findIndex((a) => a.toLowerCase() === '-path');
const name =
nameIdx >= 0 ? args[nameIdx + 1] :
pathIdx >= 0 ? args[pathIdx + 1] :
args.find((a) => !a.startsWith('-'));
// The value of a named parameter is not the path: in
// `New-Item -ItemType Directory .github/workflows` the path is the last word.
const NAMED = ['-name', '-path', '-itemtype', '-type', '-value'];
const valueOf = (names: string[]) => {
const i = args.findIndex((a) => names.includes(a.toLowerCase()));
return i >= 0 ? args[i + 1] : undefined;
};
const isDir = valueOf(['-itemtype', '-type'])?.toLowerCase() === 'directory';
const positional = args.find((a, i) => !a.startsWith('-') && !(i > 0 && NAMED.includes(args[i - 1].toLowerCase())));
const path = valueOf(['-path']) ?? positional;
const leaf = valueOf(['-name']);
const name = path && leaf ? `${path}/${leaf}` : leaf ?? path;
if (!name) return { lines: [{ text: 'New-Item: -Name ou chemin requis', type: 'error' }], newState };
if (isDir) {
const { lines, newRoot } = deps.cmdMkdir(newState, [name]);
Expand Down
Loading
Loading