Personally, I find it easier to reason about changes entirely at the REPL. Just @eval Base begin ... end is a great way to interactively override small things and it makes the history much more tractable.
Just for completeness, what I usually do is just Revise.track(Base), which automatically detects and applies all changes you made to Base and also lets you make changes during a running Julia session.
One thing to be aware of when modifying Base is that not everything that works when modifying it after you have a fully working Julia will actually work when building Julia. That is because Julia is built incrementally in itself, ie bootstrapped, and not all functionality that will eventually be available is always available when your code is evaluated during that process. For example, there are files in base that are evaluated so early that integer arithmetic doesn’t yet work, so you can’t use a for loop to define a series of definitions concisely and instead have to simply spell out all the cases. To deal with this, a good approach is to get the change you’re implementing working in the REPL and once it works, try recompiling Julia and see what breaks—expecting that something very well might break. If something breaks it will be because it uses some functionality at compile time that doesn’t yet exist, which can be fixed by moving around the order of definitions. This can be quite tedious but isn’t fundamentally hard. You can move the text of the definitions to a later file or move the order of files in Base.jl.