Orchestrate

Git and GitHub for AI engineers: branches, pull requests and review

Save every change with Git, put the course folder on GitHub privately, and ship changes through branches and reviewed pull requests, the way SAP teams move transports.

Updated Oct 3, 2026Foundational 8 minDeep 40 min
Foundational layer · 8 min read

The 60-second version

Git is a tool that keeps a history of every saved change to a set of files. Each saved change, a commit, records what changed, who changed it, when and why. You can compare any two versions and go back to any of them.

GitHub is a website that stores a copy of that history online and adds teamwork on top. The most important teamwork feature is the pull request: a proposal to add a set of changes to the main version, which other people read, comment on and approve before it goes in.

For SAP people, the idea is familiar. A transport request bundles changes, gets released, and moves from development to quality to production under control. Git and pull requests give AI code the same discipline: nothing reaches the main line without being visible, reviewable and reversible.

Why it matters to the business

An AI solution is more than a model. It is code, prompts, configuration, test cases and evaluation results. Each one changes behavior. A one-word edit to a prompt can change which blocked sales orders get escalated to a credit analyst.

Without version control, nobody can answer basic questions after an incident: what changed, who approved it, and how do we go back? With Git and pull requests, those answers are a click away.

Three business outcomes follow:

  • Audit trail. Every change to the logic carries an author, a timestamp, a reason and a reviewer. That matters for internal controls, especially for anything that touches finance or credit decisions.
  • Safe rollback. If a new prompt or threshold makes things worse, the team returns to the last good version in minutes instead of rebuilding from memory.
  • Faster, safer teamwork. Several engineers can work at once on separate branches. Review catches mistakes, leaked keys and real customer data before they spread.

Example. A forward deployed engineer (FDE) changes the rule that decides which blocked sales orders a person must look at, from 50,000 EUR to 60,000 EUR. In a pull request, the credit team lead sees the exact line that changed, asks why, and approves. Three weeks later an auditor asks who approved the change. The answer is on the pull request.

How SAP does it

SAP's own development tools already work this way, with SAP names. As of October 2026:

  • Change and Transport System (CTS). The classic tool for ABAP development and Customizing. Changes are recorded in transport requests and moved through the system landscape, typically development, quality and production.
  • Git-enabled CTS (gCTS). Lets ABAP teams use Git as the version store for their transports, which opens the door to automated build and test pipelines for ABAP.
  • SAP BTP, ABAP environment. Code is organized in software components, each tied to one Git repository. SAP's learning material describes releasing a transport request as pushing the objects to that repository.
  • SAP Continuous Integration and Delivery. A BTP service that watches a Git repository, such as one on GitHub, and runs a build, test and deploy pipeline on every change. It can hand the result to SAP Cloud Transport Management to move it through development, test and production accounts.

The AI side-by-side extensions you build in this course (Python services, CAP apps) live in Git repositories from day one. Knowing Git is how you plug into these pipelines later.

Git terms for SAP people

The match is not exact, but it helps to map the words:

Git or GitHub Closest SAP idea The difference
Repository The objects of a package or software component A repository holds any files: Python, prompts, test data
Commit A saved change inside a transport task Commits are cheap; engineers make many a day
Branch A separate line of work, like a development track Creating one takes seconds and costs nothing
Pull request Release and approval of a transport Review happens line by line, before the merge
Merge into main Import into the next system Deployment is a separate, often automated step
Revert Transporting a correction A revert is itself a new, reviewable commit

Questions to ask

  • Is every prompt, configuration file and evaluation set for our AI solution in a Git repository, or only the code?
  • Who must approve a pull request before it reaches the main branch, and is that rule enforced by the platform or only agreed?
  • Do our repositories block or scan for secrets such as API keys? What is the process if one leaks?
  • Where does real customer or company data live? It should never be committed to a repository.
  • How does a merged change reach SAP BTP or the ABAP system: a manual step, or a pipeline with transports?
  • Can we show an auditor, for any production behavior, the change that caused it and who approved it?

Common misconceptions

  • "Git is only for developers." Prompts, test cases and decision thresholds are business logic. Product owners and functional consultants review pull requests too; GitHub shows changes in plain text.
  • "A private repository keeps secrets safe." Private limits who can see it today. Anyone with access, and every copy they made, keeps the full history. A key committed once must be replaced.
  • "Review slows us down." A small pull request reviewed in minutes is faster than finding the same mistake in production.
  • "GitHub replaces SAP transports." For ABAP and Customizing, CTS or gCTS still moves changes between SAP systems. Git and GitHub cover the code around them, and gCTS connects the two.

Key terms

  • Repository (repo): a folder whose full change history Git tracks.
  • Commit: one saved snapshot of the files, with a message saying why.
  • Branch: a named line of work; main is the agreed, working version.
  • Remote: a copy of the repository on another computer, such as GitHub.
  • Push / pull: send your commits to the remote / bring others' commits down.
  • Pull request (PR): a request to merge one branch into another, with review.
  • Merge conflict: two changes to the same lines that Git can't combine on its own; a person decides.

Check yourself

Pick one answer for each question. The explanation appears after you choose.
  1. 1Why should prompts and decision thresholds for an AI solution be in Git, not only the code?

    Answer: B. A one-word prompt edit or a new threshold changes which orders get escalated. Keeping them in Git gives the same audit trail, review and rollback as code. Nothing in SAP or the model forces this; it is a control the team chooses.
  2. 2What does a pull request give a business that a direct change to the main version does not?

    Answer: D. A pull request shows the exact lines that changed, lets others comment and approve, and keeps that record. That answers "who approved this?" months later. It does not deploy anything by itself and does not guarantee correctness.
  3. 3An API key was committed to a private repository and pushed. What must happen first?

    Answer: C. Anyone with access, and every copy they made, keeps the full history, so a deleted file is still there. GitHub's guidance is to revoke or rotate the secret first. Cleaning history comes after, and does not reach existing copies.
  4. 4How do Git and GitHub relate to SAP's Change and Transport System?

    Answer: B. CTS still moves ABAP and Customizing changes through the landscape. gCTS lets ABAP teams use Git as the version store and enables pipelines. The Python and CAP extensions in this course live in Git repositories directly.
  5. 5Which SAP BTP service runs a build, test and deploy pipeline when a change is pushed to a Git repository?

    Answer: D. SAP Continuous Integration and Delivery connects to a repository, is triggered by a webhook on push, and runs stages such as build, test and deploy. It can hand the result to SAP Cloud Transport Management to move it through accounts.
  6. 6Your team says reviews are "agreed" but not enforced. What is the right follow-up question?

    Answer: A. An agreed rule can be skipped under pressure; a platform rule cannot. GitHub can require a pull request, approvals and passing checks before merging, depending on the plan. Prompt changes need review as much as code.
Deep layer · 40 min read

Mental model: snapshots, pointers, and a proposal to move a pointer

Git stores your project as a chain of snapshots. Each commit is a full picture of the tracked files, plus a pointer to the commit before it, an author and a message. Nothing in the chain is ever edited; new commits are added on top.

A branch is just a name that points at one commit. When you commit on a branch, the name moves forward to the new commit. main is the branch everyone agrees is the working version. HEAD is the pointer to the branch you are on right now.

So a pull request is a proposal: "move main forward to include the commits on my branch." Review is the conversation before that pointer moves. Once you see it that way, branches stop being scary. They are cheap labels, and the history underneath is safe.

flowchart LR
  A[commit A<br/>set up folder] --> B[commit B<br/>add triage.py]
  B --> C[commit C<br/>raise threshold]
  M([main]) -.-> B
  F([raise-threshold]) -.-> C
  H([HEAD]) -.-> F

In the picture, main still points at B. Your branch raise-threshold points at C. Merging the pull request moves main to include C.

How it works

Three places your work lives

Every change passes through three places on your computer, then one online:

  1. Working folder. The files you edit in VS Code.
  2. Staging area (also called the index). The changes you have chosen for the next commit, with git add.
  3. Local history. Commits, created with git commit, stored in the hidden .git folder.
  4. Remote. A copy of the history on GitHub, updated with git push and read with git pull.
flowchart LR
  W[Working folder] -->|git add| S[Staging area]
  S -->|git commit| L[Local history]
  L -->|git push| R[GitHub remote]
  R -->|git pull| L
  L -->|git restore| W

Staging exists so you can commit part of your work. You can fix a bug and tidy a comment, then commit them separately with clear messages.

The commands you will use every day

Command What it does When
git status Shows the branch and which files changed, staged or not Before every other command
git diff Shows unstaged line changes; git diff --staged shows staged ones Before you stage or commit
git add <file> Stages a file's current changes When a change is ready
git commit -m "why" Saves staged changes as a commit Several times a day
git log --oneline Lists commits, newest first To see history
git restore <file> Throws away unstaged edits to a file, back to the staged or last committed version When an edit went wrong
git restore --staged <file> Unstages a file, keeping your edits When you staged too much
git switch -c <name> Creates a branch and moves to it Start of each change
git switch main Moves back to main After a merge
git push / git pull Sends / receives commits Sharing work
git merge <branch> Combines another branch into the current one Bringing in others' work

Branches, pull requests and review

A team workflow on GitHub has the same shape every time:

sequenceDiagram
  participant You
  participant GitHub
  participant Reviewer
  You->>You: git switch -c raise-threshold
  You->>You: edit, git add, git commit
  You->>GitHub: git push -u origin raise-threshold
  You->>GitHub: open pull request into main
  GitHub->>Reviewer: request review
  Reviewer->>GitHub: comment or approve
  GitHub->>GitHub: merge, main moves forward
  You->>You: git switch main, git pull

GitHub's pull request page shows the conversation, the commits and the changed files, line by line. A merge box tells you whether the PR can be merged: whether required reviews and checks are satisfied, and whether there are conflicts.

Merge conflicts

Git combines changes automatically when they touch different lines. When two branches change the same lines differently, Git stops and asks you. It writes both versions into the file between markers:

<<<<<<< HEAD
ESCALATE_ABOVE = 40000  # the version on your current branch
=======
ESCALATE_ABOVE = 55000  # the version on the branch you are merging in
>>>>>>> main

You edit the file to the version you want, delete all three marker lines, then git add and git commit. A conflict is not an error. It is Git refusing to guess a business decision.

Why AI work needs this more, not less

Model behavior depends on things that look like text, not code: prompts, few-shot examples, thresholds, tool descriptions, evaluation sets. A changed sentence in a prompt is a changed business rule. Treat these files exactly like code: one change per pull request, a message saying why, and the evaluation result in the PR description. Unit 8 (evaluation) and Unit 10 (production) build on this when you version evaluation sets and track regressions.

Build it yourself: put your course on GitHub and ship a reviewed change

Before you start: complete Set up your computer for this course. You need the orchestrate-course folder with Git installed, a .gitignore and at least one commit (its Step 7).

You will add a small blocked-orders script, learn the daily Git loop, put your course folder on GitHub as a private repository, change the script on a branch, review and merge it with a pull request, and then resolve a merge conflict on purpose. A check script makes sure no key ever reaches GitHub.

flowchart LR
  E[Edit on a branch] --> C[Commit]
  C --> P[Push to GitHub]
  P --> PR[Pull request]
  PR --> R[Review]
  R --> M[Merge into main]
  M --> E

What you need

  • The course folder from the setup topic, with Git working (git --version prints a version).
  • A free GitHub account. Private repositories are included on GitHub Free.
  • The GitHub CLI (gh), free; Step 5 installs it.
  • About 60 to 90 minutes. No SAP access, no model key, no cost.

Step 1: Open the course folder and check where you are

  1. Open VS Code, choose File > Open Folder, and open orchestrate-course.

  2. Open a terminal with Terminal > New Terminal. Activate the virtual environment if VS Code didn't:

    • Windows (PowerShell): .venv\Scripts\Activate.ps1
    • macOS / Linux: source .venv/bin/activate
  3. Run:

    git status
    git log --oneline

You should see something like:

On branch master
nothing to commit, working tree clean
3f2a9c1 Add setup check
8b71d0e Set up the course folder

Your branch may be called master or main; Git's default name depends on your version and settings. Step 6 renames it to main.

Step 2: Add the blocked-orders script

  1. In VS Code, create a folder unit01 in the course folder if it isn't there already.
  2. Create a new file, paste the code below, and save it as unit01/triage.py.
"""Decide which blocked sales orders to escalate today.

Made-up, SAP-shaped sample data; no SAP access or key needed.
SalesOrder, SoldToParty and TotalNetAmount are field names from SAP's Sales Order API;
"block" is a simplified label for this exercise, not an SAP field.
Run it from your course folder:  python unit01/triage.py
"""
ESCALATE_ABOVE = 50000  # net value (EUR) above which a blocked order goes to a person

ORDERS = [
    {"SalesOrder": "5000001", "SoldToParty": "17100001", "TotalNetAmount": 72000.00, "block": "credit"},
    {"SalesOrder": "5000002", "SoldToParty": "17100002", "TotalNetAmount": 8400.00, "block": "credit"},
    {"SalesOrder": "5000003", "SoldToParty": "17100003", "TotalNetAmount": 51500.00, "block": "delivery"},
    {"SalesOrder": "5000004", "SoldToParty": "17100001", "TotalNetAmount": 23900.00, "block": "delivery"},
]


def main() -> None:
    print(f"Escalate blocked orders above {ESCALATE_ABOVE:,.0f} EUR\n")
    for order in ORDERS:
        action = "ESCALATE" if order["TotalNetAmount"] > ESCALATE_ABOVE else "queue"
        print(f"  {order['SalesOrder']}  {order['TotalNetAmount']:>10,.2f}  {order['block']:<8}  {action}")


if __name__ == "__main__":
    main()
  1. Run it:

    python unit01/triage.py
Escalate blocked orders above 50,000 EUR

  5000001   72,000.00  credit    ESCALATE
  5000002    8,400.00  credit    queue
  5000003   51,500.00  delivery  ESCALATE
  5000004   23,900.00  delivery  queue

The one line that matters is ESCALATE_ABOVE. It is a business rule written as code, which is exactly the kind of line that needs review.

Step 3: The daily loop: status, diff, add, commit

  1. See what Git noticed:

    git status
    Untracked files:
      unit01/

    "Untracked" means Git sees the file but isn't saving its history yet.

  2. Stage it and look at what you are about to commit:

    git add unit01/triage.py
    git diff --staged

    Lines starting with + are additions. Press q if the viewer opens.

  3. Commit, with a message that says why:

    git commit -m "Add blocked-order triage rule with sample data"
  4. Check the history:

    git log --oneline

    Your new commit is at the top.

Step 4: Undo a bad edit with restore

Mistakes before a commit are easy to throw away.

  1. Open unit01/triage.py and change 50000 to 5. Save. Run python unit01/triage.py: every order is now escalated.

  2. See the change, then throw it away:

    git diff
    git restore unit01/triage.py
    git status
On branch master
nothing to commit, working tree clean

The file is back to the last committed version. git restore only discards changes you haven't committed, so use it with care: there is no undo for the undo.

Step 5: Add the repository safety check, then install GitHub's CLI

Before anything goes online, prove that no key will go with it.

  1. Create a new file in the course folder (next to check_setup.py), paste the script below and save it as check_repo.py.
"""Check that your course folder is safe and ready to share on GitHub.

Run it from your course folder:  python check_repo.py
It only reads; it changes nothing and sends nothing anywhere.
"""
import re
import shutil
import subprocess
import sys

problems = 0

# Text that looks like a real secret. A match means "look at this line", not proof.
SECRET_PATTERNS = [
    re.compile(r"sk-ant-[A-Za-z0-9_-]{10,}"),                  # Anthropic-style key
    re.compile(r"gh[pousr]_[A-Za-z0-9]{20,}"),                 # GitHub token
    re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),         # private key file
    re.compile(r"(?i)(api_?key|secret|password|token)\s*[=:]\s*[\"'][^\"'\s]{16,}[\"']"),
]


def report(ok: bool, label: str, fix: str = "", optional: bool = False) -> None:
    """Print one line: OK, MISSING (must fix) or LATER (optional for now)."""
    global problems
    if ok:
        print(f"  OK       {label}")
    elif optional:
        print(f"  LATER    {label}  ->  {fix}")
    else:
        problems += 1
        print(f"  MISSING  {label}  ->  {fix}")


def git(*args: str) -> subprocess.CompletedProcess:
    """Run one git command in this folder and capture its output as text."""
    return subprocess.run(["git", *args], capture_output=True, text=True)


print("\n1. Git")
if shutil.which("git") is None:
    report(False, "git is installed", "install Git (see Set up your computer)")
    sys.exit(1)
report(True, "git is installed")
in_repo = git("rev-parse", "--is-inside-work-tree").returncode == 0
report(in_repo, "this folder is a Git repository", "run: git init")
if not in_repo:
    sys.exit(1)
branch = git("branch", "--show-current").stdout.strip() or "(none)"
report(True, f"current branch: {branch}")

print("\n2. Secrets stay out")
report(git("check-ignore", "-q", "--no-index", ".env").returncode == 0, ".env is ignored",
       "add the line .env to .gitignore")
report(git("ls-files", "--error-unmatch", ".env").returncode != 0, ".env is not tracked",
       "run: git rm --cached .env, commit, and rotate any key that was pushed")
report(git("ls-files", ".venv").stdout.strip() == "", ".venv is not tracked",
       "add .venv/ to .gitignore, then run: git rm -r --cached .venv")
hits = []
for path in git("ls-files").stdout.splitlines():
    try:
        with open(path, encoding="utf-8") as f:
            for number, line in enumerate(f, start=1):
                if any(p.search(line) for p in SECRET_PATTERNS):
                    hits.append(f"{path}:{number}")
    except (UnicodeDecodeError, OSError):
        continue  # skip binary or unreadable files
report(not hits, "no secret-looking text in tracked files",
       "check and remove: " + ", ".join(hits[:5]))

print("\n3. Work saved")
dirty = git("status", "--porcelain").stdout.strip()
report(not dirty, "no uncommitted changes", "review with git status, then commit", optional=True)
report(git("log", "-1").returncode == 0, "at least one commit", "run: git add . and git commit -m \"...\"")

print("\n4. GitHub")
remote = git("remote", "get-url", "origin")
report(remote.returncode == 0, "remote 'origin': " + (remote.stdout.strip() or "not set"),
       "create the GitHub repository (Step 6)", optional=True)
if remote.returncode == 0:
    counts = git("rev-list", "--left-right", "--count", "@{upstream}...HEAD")
    if counts.returncode == 0:
        behind, ahead = counts.stdout.split()
        report(ahead == "0", f"all commits pushed ({ahead} not on GitHub yet)", "run: git push", optional=True)
        report(behind == "0", f"up to date with GitHub ({behind} new there)", "run: git pull", optional=True)
    else:
        report(False, f"branch {branch} pushed to GitHub", f"run: git push -u origin {branch}", optional=True)

print()
if problems:
    print(f"{problems} item(s) to fix before you push. Fix them, then run this again.")
    sys.exit(1)
print("Safe to share. Nothing secret is tracked by Git.")
  1. Run it, then commit it:

    python check_repo.py
    git add check_repo.py
    git commit -m "Add repository safety check"
1. Git
  OK       git is installed
  OK       this folder is a Git repository
  OK       current branch: master

2. Secrets stay out
  OK       .env is ignored
  OK       .env is not tracked
  OK       .venv is not tracked
  OK       no secret-looking text in tracked files

3. Work saved
  LATER    no uncommitted changes  ->  review with git status, then commit
  OK       at least one commit

4. GitHub
  LATER    remote 'origin': not set  ->  create the GitHub repository (Step 6)

Safe to share. Nothing secret is tracked by Git.

That is the output of the first run, before the commit: check_repo.py itself is the uncommitted change. Any MISSING line in section 2 must be fixed before Step 6. LATER under GitHub is expected until the next step.

  1. Create a GitHub account if you don't have one: go to github.com, click Sign up, and follow the prompts. The free plan is enough.

  2. Install the GitHub CLI:

    • Windows (PowerShell):

      winget install --id GitHub.cli --source winget

      Then close the terminal window completely and open a new one. A new tab is not enough for the new command to be found.

    • macOS with Homebrew:

      brew install gh

      Without Homebrew, use the macOS download on cli.github.com.

    • Linux: follow the instructions for your distribution linked from cli.github.com.

  3. Check it works with gh --version.

  4. Sign in:

    gh auth login

    Answer the questions with the arrow keys and Enter: GitHub.com, then HTTPS, then Yes to authenticate Git with your GitHub credentials, then Login with a web browser. Copy the one-time code it shows and press Enter. In the browser page that opens, sign in if asked, paste the code, and approve the access it asks for. The terminal then confirms you are logged in.

    gh stores the resulting token in your system's credential store. You never paste it into a file.

Step 6: Create your private repository and push

  1. Make sure your main branch is called main (it's the name GitHub uses by default):

    git branch -M main
  2. Create the repository on GitHub from your folder, private, and push your history:

    gh repo create orchestrate-course --private --source=. --push
✓ Created repository your-name/orchestrate-course on github.com
✓ Added remote https://github.com/your-name/orchestrate-course.git
✓ Pushed commits to https://github.com/your-name/orchestrate-course.git

The exact wording may differ between gh versions. What matters is that it created, added a remote, and pushed.

  1. Open the repository in your browser:

    gh repo view --web

    You see your files and commits. Check two things: there is a Private label next to the name, and there is no .env file and no .venv folder in the list.

  2. Run python check_repo.py again. The GitHub section now shows OK lines.

Step 7: Make a change on a branch

The credit team wants fewer escalations. You'll propose raising the threshold, without touching main directly.

  1. Create a branch and switch to it:

    git switch -c raise-threshold
    Switched to a new branch 'raise-threshold'
  2. In unit01/triage.py, change ESCALATE_ABOVE = 50000 to ESCALATE_ABOVE = 60000. Save.

  3. Run the script and note that order 5000003 (51,500 EUR) now goes to the queue.

  4. Review, stage and commit:

    git diff
    git add unit01/triage.py
    git commit -m "Raise escalation threshold to 60,000 EUR to cut credit team workload"
  5. Push the branch to GitHub. -u links your local branch to the GitHub one, so later git push and git pull need no extra words:

    git push -u origin raise-threshold

Step 8: Open, review and merge a pull request

  1. Open a pull request into main:

    gh pr create --base main --title "Raise escalation threshold to 60,000 EUR" --body "Credit team asked for fewer escalations. Effect on sample data: order 5000003 (51,500 EUR) moves from ESCALATE to queue."

    It prints a link to the new pull request.

  2. Open it with gh pr view --web. On the web you could also have used the yellow Compare & pull request banner on the repository page.

  3. Review it as a reviewer would. Click the Files changed tab. You see one red line (old) and one green line (new). Click the + next to the green line and leave a comment, for example "Agreed with credit team lead?".

    On a team, a colleague does this review. In your own course repository, you review your own work; the habit is what counts.

  4. Merge it. Back in the terminal:

    gh pr merge --squash --delete-branch

    --squash turns the branch's commits into one commit on main. --delete-branch removes the branch locally and on GitHub, since its work is now in main.

  5. Make sure your local main has the merged change:

    git switch main
    git pull
    git log --oneline

The top commit is your squashed change, on main. On GitHub, the Pull requests tab, filter Closed, keeps the whole conversation for later.

Step 9: Cause and resolve a merge conflict

Two people changed the same rule. You'll play both.

  1. On a new branch, lower the threshold:

    git switch -c lower-threshold

    Change ESCALATE_ABOVE = 60000 to ESCALATE_ABOVE = 40000, save, then:

    git commit -am "Lower threshold to 40,000 EUR"

    (-a stages every already-tracked file that changed, so you can skip git add here.)

  2. Go back to main and make a different change to the same line:

    git switch main

    Change ESCALATE_ABOVE = 60000 to ESCALATE_ABOVE = 55000, save, then:

    git commit -am "Set threshold to 55,000 EUR after review with credit team"
  3. Switch to your branch and bring main into it, as you would before opening a pull request:

    git switch lower-threshold
    git merge main
Auto-merging unit01/triage.py
CONFLICT (content): Merge conflict in unit01/triage.py
Automatic merge failed; fix conflicts and then commit the result.
  1. Open unit01/triage.py. VS Code highlights the conflict:
<<<<<<< HEAD
ESCALATE_ABOVE = 40000  # net value (EUR) above which a blocked order goes to a person
=======
ESCALATE_ABOVE = 55000  # net value (EUR) above which a blocked order goes to a person
>>>>>>> main

HEAD is your branch's version. Below ======= is the version from main. 5. Decide. The 55,000 EUR value was agreed with the credit team, so keep it. Delete the 40,000 line and all three marker lines, so one ESCALATE_ABOVE = 55000 line remains. VS Code's Accept Incoming Change link above the conflict does the same. Save. 6. Prove the file works, then finish the merge:

python unit01/triage.py
git add unit01/triage.py
git commit -m "Resolve conflict: keep 55,000 EUR agreed with credit team"
git log --oneline --graph
*   adf8e4b Resolve conflict: keep 55,000 EUR agreed with credit team
|\
| * 1a6d083 Set threshold to 55,000 EUR after review with credit team
* | 21d373d Lower threshold to 40,000 EUR
|/
* 05dbb70 Raise escalation threshold to 60,000 EUR (#1)

Your hashes differ. The graph shows two lines of work joining.

  1. Tidy up. The commit on main from step 2 went in without a pull request; that is fine in a practice repository, and Production concerns explains how teams prevent it. Push main, and delete the practice branch:

    git switch main
    git push
    git branch -D lower-threshold
  2. Run python check_repo.py one last time. Every line should be OK.

What each part of the code does

Part What it does
ESCALATE_ABOVE in triage.py The business rule: a single, reviewable line
ORDERS in triage.py Made-up orders using field names from SAP's Sales Order API, so later units can swap in real API data
git() in check_repo.py Runs one Git command and returns its output and exit code
check-ignore --no-index .env Asks whether .gitignore covers .env, even if it was tracked by mistake
ls-files --error-unmatch .env Fails (good) when .env is not tracked
SECRET_PATTERNS Flags lines in tracked files that look like keys, tokens or private keys
rev-list --left-right --count Counts commits that are only on GitHub, or only on your computer
report() Prints OK, MISSING (fix before pushing) or LATER (fine for now), like check_setup.py

The secret patterns catch common shapes, not every possible secret. Treat a clean result as a safety net, not a guarantee.

If something goes wrong

What you see What it means What to do
git is not recognized, or command not found Git isn't installed, or the terminal predates the install Install Git as in the setup topic, then open a new terminal
python is not recognized Python isn't on PATH, or .venv isn't active Activate .venv (Step 1); on macOS/Linux try python3
gh is not recognized after installing The terminal hasn't picked up the new PATH Close the whole terminal window (Windows) and open a new one
fatal: not a git repository You are in the wrong folder cd ~/orchestrate-course, then retry
Author identity unknown Git doesn't know your name and email Run the two git config --global lines from the setup topic
gh auth login fails, or git push says Authentication failed Not signed in, or the stored login expired Run gh auth login again; choose HTTPS and say Yes to authenticating Git
Could not resolve host or a timeout on push The network or a company proxy blocks GitHub Try another network, or ask IT to allow github.com
An error saying the name already exists on your account You already have a repository with that name Use another name in gh repo create, or connect the existing one with git remote add origin <url>
rejected ... (fetch first) on push GitHub has commits you don't Run git pull, fix any conflict, then git push
error: Your local changes ... would be overwritten on git switch You have uncommitted edits Commit them, or discard them with git restore <file>
MISSING .env is not tracked .env was committed earlier git rm --cached .env, commit, push, and replace every key in it
Terminal stuck in a full-screen editor git commit without -m opened Vim Press Esc, type :q!, Enter; rerun with -m "message"

The SAP way

SAP has had change control for decades. What's new is Git underneath it. As of October 2026, these are the pieces an FDE meets.

Change and Transport System (CTS)

CTS organizes development in the ABAP Workbench and in Customizing and transports changes between the systems of a landscape. A classic three-system landscape moves a transport from development to quality to production. Each import is controlled, and the transport is the unit of change, approval and audit.

Most AI projects still touch CTS. If your AI service needs a new custom field, a CDS view or a released API configured in S/4HANA, that change travels by transport, on the ABAP team's schedule. Plan for it in the solution design, as covered in Writing a solution design document for an AI use case.

Git-enabled CTS (gCTS)

gCTS lets ABAP teams manage change and transport processes with Git as the external version store. SAP's sample repository SAP-samples/s4hana-gcts shows task-based committing tied to releasing tasks in the Transport Organizer, and SAP positions gCTS as the way to set up CI/CD for ABAP.

SAP BTP, ABAP environment

In the ABAP environment, code is grouped into software components. SAP's learning material describes each one as tied to exactly one Git repository and one transport layer, managed in the Manage Software Components app (create or clone, branches, tags). Two models exist:

  • SAP-managed gCTS: SAP runs the Git repository; you don't access it directly.
  • Bring Your Own Git (BYOG): the software component connects to your own GitHub, GitLab or Bitbucket repository, so it fits your existing pipelines.

Releasing a transport request in the ABAP development tools for Eclipse pushes the objects to the associated repository. abapGit is a separate open-source tool, which SAP's material names for migrating code from on-premise to cloud and between cloud systems.

SAP Continuous Integration and Delivery and Cloud Transport Management

For the side-by-side apps you build in Python or CAP, the repository lives on GitHub or a similar host. SAP Continuous Integration and Delivery, a BTP service, connects to that repository. A webhook (GitHub calling the service on every push) triggers a job. The job runs stages such as build, test and deploy, made of steps; each run is a build. A step can hand the built application to SAP Cloud Transport Management, which moves it through development, test and production subaccounts with transport-style control. Unit 6's Deploying AI apps on SAP BTP covers the deployment itself; pipelines come back in the production units.

Build vs. SAP

Situation Use Why
Python or CAP AI service, prompts, eval sets Git on GitHub (or the customer's Git host), pull requests It's plain files; review and history are what you need
Deploying that service to SAP BTP with controls SAP Continuous Integration and Delivery + Cloud Transport Management, or the customer's own pipeline Repeatable deploys and transport-style promotion between accounts
ABAP objects and Customizing in S/4HANA on-premise or private cloud CTS; gCTS where the team runs it SAP systems import changes by transport; Git adds history and pipelines
ABAP in SAP BTP, ABAP environment Software components with SAP-managed gCTS or BYOG Built into the environment's lifecycle
One-time move of ABAP code between systems or to the cloud abapGit Named by SAP for migration scenarios

The practical rule for an FDE: your own code is in Git from the first hour; the customer's SAP changes follow the customer's transport process. The solution design names where the two meet.

Production concerns

Branch protection. On a team, nobody should be able to push to main directly. GitHub branch protection can require a pull request, a number of approvals, passing status checks and up-to-date branches before merging. Availability depends on the plan: protected branches work on public repositories with GitHub Free, and on private repositories with GitHub Pro, Team or Enterprise. Your free private course repository can't enforce it, which is why Step 9 could commit to main directly. Customer repositories should enforce it.

Secrets. Keys live in .env locally and in the platform's secret store or service bindings in production, never in the repository. A leaked key is handled in this order: revoke or rotate it, then clean history. Rewriting history doesn't reach clones, forks or cached views. Run a check like check_repo.py before every push, and enable your Git host's secret scanning where the plan offers it.

Data. Never commit real customer or company data, including extracts "just for testing". Use made-up, SAP-shaped samples, as triage.py does. Large files, model weights and datasets don't belong in a normal Git repository either; store them in object storage and commit a reference.

Review that means something. Keep pull requests small: one decision per PR. Put the "why" and the evaluation result in the description. For AI changes, a reviewer should see the before and after on the evaluation set, not only the diff. Ask a business owner to approve changes to business rules, like the escalation threshold.

Traceability to SAP. Link the pull request to the change ticket and, where SAP objects change too, to the transport request. An auditor can then follow one change from the business request to the code to the import into production.

Clean core. Keep AI logic in side-by-side extensions under Git, and keep changes inside S/4HANA to released APIs and extension points, moved by transport. The repository then documents exactly what sits outside the core.

Pitfalls

  • Committing .env once. The key stays in history. Check .gitignore before the first push and run check_repo.py.
  • git add . without looking. It stages everything, including stray exports and logs. Run git status and git diff --staged first.
  • Huge pull requests. A 2,000-line PR gets a rubber stamp, not a review. Split it.
  • Messages like "fix" or "update". Write why: "Raise threshold to 60,000 EUR to cut credit team workload".
  • Working on main. Start every change with git switch -c <name>.
  • Long-lived branches. The longer a branch lives, the bigger its conflicts. Merge main into it often, or finish it quickly.
  • Resolving conflicts by picking "mine" blindly. A conflict is a business question when the lines hold rules. Ask the other author.
  • Assuming a private repository is a vault. Access changes over time and copies outlive it.

Exercise: ship one reviewed improvement to your Unit 1 code

  1. Open your course folder and run python check_repo.py. Fix anything marked MISSING.

  2. Create a branch named add-readme:

    git switch -c add-readme
  3. Create README.md in the course folder with three short sections: What this is (your Orchestrate course portfolio), How to run (activate .venv, python check_setup.py, python unit01/triage.py) and Rules (no keys, no real customer data).

  4. Commit it with a message that says why, and push the branch:

    git add README.md
    git commit -m "Add README so reviewers know how to run the course code"
    git push -u origin add-readme
  5. Open a pull request with gh pr create --fill. (--fill uses your commit message as the title and body.)

  6. On GitHub, open Files changed and leave at least one review comment on your own README. Then fix what you commented on, commit and git push again. Watch the pull request update with the new commit.

  7. Merge with gh pr merge --squash --delete-branch, then run git switch main and git pull.

Done when: your private GitHub repository shows README.md on main, the Closed pull requests list shows at least two merged pull requests (the threshold change and the README), and python check_repo.py prints Safe to share. Every later unit adds its code to this repository through a branch and a pull request, and Unit 15's portfolio projects start from it.

Check yourself

Pick one answer for each question. The explanation appears after you choose.
  1. 1In Git, what is a branch?

    Answer: B. A branch is a lightweight pointer to a commit, and it moves forward each time you commit on it. That is why creating one is instant and cheap. Copies and backups are what remotes and clones are for.
  2. 2You edited triage.py, haven't committed, and the change is wrong. What do you run?

    Answer: C. git restore <file> throws away uncommitted edits to the working file. --staged only unstages and keeps your edits. Committing would save the mistake, and deleting a branch is unrelated.
  3. 3What does git push -u origin raise-threshold do the first time you run it?

    Answer: D. Push sends your commits to the remote named origin. -u links the local branch to the GitHub branch, so later git push and git pull need no extra words. Merging happens only through the pull request.
  4. 4git merge main stops with CONFLICT (content) in triage.py. What is the right next move?

    Answer: B. Both branches changed the same line, so Git refuses to guess. You edit the file to the intended version, remove the <<<<<<<, ======= and >>>>>>> lines, stage and commit. When the line is a business rule, the right value is a business decision, so ask the other author if unsure.
  5. 5In check_repo.py, why does the .env check use ls-files --error-unmatch, and what should you do if it reports MISSING?

    Answer: C. The command fails when .env is not tracked, which is the safe case. If it is tracked, git rm --cached .env stops tracking it, but the key remains in history. GitHub's guidance is to revoke or rotate the secret first.
  6. 6A customer wants every change to main reviewed, in a private repository on GitHub Free. What do you tell them?

    Answer: D. GitHub documents protected branches for public repositories on Free, and for private repositories on Pro, Team and Enterprise. Pull requests work everywhere, but only protection enforces "no merge without approval". Without it, review is just an agreement.
  7. 7In SAP BTP, ABAP environment, how does a software component relate to Git?

    Answer: A. SAP's learning material describes a 1:1 link between a software component, a Git repository and a transport layer. The repository is SAP-managed or your own (Bring Your Own Git). abapGit is a separate tool named for migration.
  8. 8Your AI service is a Python app on GitHub. How do pushes reach BTP with transport-style control using SAP services?

    Answer: B. SAP Continuous Integration and Delivery connects to the repository; a webhook on push starts a job with build, test and deploy stages. A step can pass the result to SAP Cloud Transport Management, which moves it between development, test and production subaccounts.
  9. 9Why should a change to the escalation threshold or a prompt go through its own small pull request?

    Answer: C. A threshold or a prompt is a business rule. A small, focused pull request lets a reviewer, ideally the business owner, see exactly what changes and why, and leaves an audit record. Large mixed PRs get rubber-stamped.

Sources

Sign in to track your progress

We'll email you a one-time sign-in link. No password needed.

or

Tell us a little about you

Optional, every field. It helps us pitch answers to your questions at the right level and decide which topics to write next. It is never shown publicly, and you can change or clear it anytime from the account menu.

SAP areas you work in