This is the point where ArjunaOS stops being a prepared machine and becomes a real source tree. We fork the upstream LineageOS manifest repository, initialize the workspace from our own fork, let the first repo sync run, and verify that the checkout now contains the platform layers we talked about earlier.
In the last article, we got the host ready.
That article was intentionally narrow. We checked the machine, installed the packages, made sure Git was sane, and got the repo launcher working from a normal shell. Useful work, yes. But it still was not the Android tree. It was only the floor under it.
This article is where the tree actually starts to exist.
We are going to fork the manifest repository first, then create the workspace, initialize it from our own fork, and let the first repo sync begin. This is the moment where the clean mental map from Article 2 stops being abstract and starts turning into directories, metadata, and a lot of downloaded source code.
Pick a workspace and stay consistent
Before the first command, decide where this checkout will live.
You can put it somewhere else if you want. There is nothing sacred about my path. But do not be casual about it either. Android trees get large fast, and they are annoying to move later if you already started building on top of them.
In your own setup, this should be whichever stable path you want to live with for the rest of the series. So write it down conceptually like this:
<path-to-your-workspace>
For this series run, that path was:
/home/prasad/Desktop/Work/arjunaos
That is why the screenshots in this article show that exact location.
If you have multiple disks, this is the time to make a smart decision. Put the tree on the faster drive with the healthier free space, not wherever your shell happened to be open.
Fork first, then initialize from your fork
Before repo init, there is one teaching choice I want to make very explicit.
For this series, we are not inventing a custom manifest repository from day one. We are first taking the upstream LineageOS/android manifest repository and forking it into our own GitHub namespace. That means the starting point is still the normal LineageOS manifest layout, just under a fork we control.
Why bother if we are not modifying it yet?
Because this is a series about building your own OS, not just compiling somebody else's tree once and walking away. Using your own fork from the start gives you a stable place to carry future manifest changes, local tracking, and ArjunaOS-specific repo decisions later without changing the basic workflow halfway through the series.
At this stage though, the important point is this: the fork is still being used in an upstream-equivalent way for manifest purposes. The repository already has ArjunaOS README and documentation edits, but we have not started changing the actual manifest structure or project definitions yet.
So the flow is:
- fork
LineageOS/androidon GitHub - use your fork URL in
repo init - keep the first sync behavior identical to upstream as much as possible
That is exactly why the command below uses your fork URL, not upstream directly.
If your own fork still shows the original upstream README, that is fine. That is a very normal fork state. In my case, the fork already has ArjunaOS README edits, but the rule is the same either way: use your fork URL, not the upstream URL, when you initialize the workspace.
Now we run the first command that makes this feel real:
repo init -u https://github.com/<your-github-username-or-org>/android.git -b lineage-23.2 --git-lfs
For this series run, that URL was:
repo init -u https://github.com/AOSPInsider/android.git -b lineage-23.2 --git-lfs
Let us break down what this is doing.
-upoints at the manifest repository-b lineage-23.2selects the branch we are following for this series--git-lfstellsrepoto handle LFS-backed content properly from the beginning
For this series run, that manifest repository lived under AOSPInsider/android.git. The important bit is not the exact name. It is that we are initializing from our fork of upstream, not from LineageOS directly. The branch still tracks the LineageOS 23.2 world we talked about earlier, and while the repo already contains ArjunaOS README edits, we have not started changing the actual manifest layout yet.
This does not download the full Android source tree yet. That confusion trips people a lot. repo init is the manifest step. It teaches the workspace what it is supposed to become. The real bulk download comes next.
The first run also tells you one more useful thing: which Git identity repo sees. If that is wrong, fix it now, not after you start creating local commits.
AOSPInsider/android is the fork used for this series. It already contains ArjunaOS README changes, but the manifest is still being used in an upstream-equivalent way for the first sync flow.
repo init should use here, even if older docs or README snippets still show the LineageOS URL.
repo init does not build the full tree yet. It initializes the manifest-driven workspace from our forked manifest repository and confirms the branch and Git identity being used.
One small detail worth noticing in the output is that repo first downloads its own source from Google's Gerrit infrastructure. That is normal. You are still initializing the workspace against the manifest repository you gave it. repo itself just updates from its own upstream source.
Look at what changed after repo init
After repo init, the directory still looks almost empty from the outside. That can feel strange if you expected hundreds of source folders immediately.
Run:
ls -la
What you should see is the beginning of the hidden control structure:
.and..like normal- a new
.repo/directory
That hidden directory is where the manifest metadata and repo management state live. In other words, the workspace now knows what to sync, even though most of the source projects are still missing.
repo init creates the control state. repo sync is what turns that into a populated source tree. This diagram uses a generic workspace placeholder; the real path for this series run is shown in the screenshots.
repo init, the visible workspace is still small. The important change is the new .repo/ directory, which means the manifest state now exists locally.
This is a good place to stop for a second and remember Article 2. We talked there about many Git repositories pretending to be one tree. .repo/ is the control room for that illusion.
Start the first real sync
This is the heavy command:
repo sync -c --no-tags --no-clone-bundle -j$(nproc --all)
This is where the machine starts paying the bill for everything we prepared in Article 3.
The flags matter:
-ckeeps the sync focused on the current branch--no-tagsskips downloading a pile of tag history we do not need right now--no-clone-bundleavoids clone bundles that sometimes behave badly on unstable connections-j$(nproc --all)scales parallel jobs to the machine instead of hardcoding a random number
That last one is practical, but not magical. More jobs are not always better if your disk or network becomes the bottleneck. Still, it is a sane place to start for a modern local machine.
Also, this is the step where people finally understand why the disk warning in Article 3 was not just me being dramatic.
repo sync is where the LineageOS tree starts taking real shape locally. It is normal for this to run a long time and chew through bandwidth and disk space.
If your sync is slow, that does not automatically mean something is broken. Android source is large. A first sync can take a while even on a decent connection. What matters is whether progress is moving and the workspace is gradually filling out the tree.
Do not start randomly killing
repo syncjobs just because the numbers look scary. A slow first sync is normal. A repeatedly failing sync is different, and that is where we start troubleshooting.
Prove that the tree is real
Once the sync has done enough work, you want one quick sanity check that this is now a real assembled source tree and not just a manifest shell.
One easy way is:
repo list | grep -E 'build/make|frameworks/base|packages/apps/Settings|device/generic/goldfish'
I like this check because it proves different layers at once:
build/makefor the build system sideframeworks/basefor the platform corepackages/apps/Settingsfor a major system appdevice/generic/goldfishfor emulator-oriented device support
That gives you a much better gut check than only staring at a progress counter.
This is also the point where Article 2 and Article 4 connect very nicely. In Article 2 we said the Android tree is many repositories arranged into one working structure. Here you can finally see pieces of that structure by name, on your own machine.
What success looks like here
By the end of this article, you should have all of this:
- a chosen workspace path
- a fork of the upstream manifest repository under your own account
- a successful
repo init - a visible
.repo/directory - a running or completed first
repo sync - proof that real LineageOS projects now exist in the assembled tree
That is enough for Article 4.
Notice what we still have not done yet:
- no
lunch - no
envsetup.sh - no actual build output
- no booted emulator
- no ArjunaOS customization yet
That is also on purpose.
This article should own workspace creation and the first sync. If we also cram the first build into the same post, the main point gets blurred. The reader needs one clean moment where the tree is now local and real before we ask the machine to compile it.
Coming up next
In the next article, we use this synced tree for the first real build.
That means sourcing the build environment, choosing an emulator target, and seeing whether the checkout we just spent all this time downloading can actually turn into something bootable.
That is the point where ArjunaOS stops being a source tree on disk and starts becoming a system image.