[Scummvm-git-logs] scummvm master -> 63b9827058128e9fc2990ceca8b30dbb47b7b0b5

neuromancer noreply at scummvm.org
Thu Jun 4 12:55:14 UTC 2026


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
81517766f8 SCUMM: RA1: properly invert Y axis for some levels
04abb43098 SCUMM: RA1: virtual keyboard support for text inputs
63b9827058 SCUMM: RA1: better gamepad controls for some levels


Commit: 81517766f874af7248778667af06b524b1f8eff8
    https://github.com/scummvm/scummvm/commit/81517766f874af7248778667af06b524b1f8eff8
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T14:54:00+02:00

Commit Message:
SCUMM: RA1: properly invert Y axis for some levels

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/rebel.h


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index fb8e22f4b00..eed59ccdb22 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -807,6 +807,25 @@ void InsaneRebel1::updateFlightVariantCursor() {
 // jump latch from FUN_231BE, plus FUN_23115's DOS mouse recenter behavior.
 // Enhanced controls are ScummVM-only: they bypass the original recentering state
 // and expose a stable absolute centered mouse axis instead.
+bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
+	if (!isTouchscreenActive())
+		return false;
+
+	switch (_currentLevel) {
+	case 1:  // Level 2
+	case 3:  // Level 4
+	case 7:  // Level 8
+	case 8:  // Level 9
+	case 9:  // Level 10
+	case 11: // Level 12
+	case 13: // Level 14
+	case 14: // Level 15
+		return true;
+	default:
+		return false;
+	}
+}
+
 void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick) {
 	if (usedJoystick)
 		*usedJoystick = false;
@@ -888,7 +907,12 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 		inputX = (int16)CLIP<int32>(((int32)(logicalX - kRA1CenterX) * 127) / kRA1CenterX, -127, 127);
 		inputY = (int16)CLIP<int32>(((int32)(logicalY - kRA1CenterY) * 127) / kRA1CenterY, -127, 127);
 
-		if (_optControlsYFlip)
+		// These handlers negate DAT_756E internally, so direct touch needs the
+		// opposite baseline while the menu option still toggles the result.
+		bool flipY = _optControlsYFlip;
+		if (shouldInvertTouchYSettingForCurrentLevel())
+			flipY = !flipY;
+		if (flipY)
 			inputY = -inputY;
 
 		return;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 6081ec18aae..e08e41245a8 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -221,6 +221,7 @@ private:
 	void updateTurretPhysics();
 	void updateTurretShipDirection(int16 offsetY);
 	void getCollisionShipCenter(int16 &x, int16 &y) const;
+	bool shouldInvertTouchYSettingForCurrentLevel() const;
 	void preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick = nullptr);
 	void rebuildProjectionTable(int16 curveStep, int16 curveExtent);
 	void resetProjectionTable();


Commit: 04abb430982dee4a771a8281fb9f237862ae0983
    https://github.com/scummvm/scummvm/commit/04abb430982dee4a771a8281fb9f237862ae0983
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T14:54:00+02:00

Commit Message:
SCUMM: RA1: virtual keyboard support for text inputs

Changed paths:
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h


diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index af7cb44fdaa..15d07ef18c2 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -274,6 +274,17 @@ int InsaneRebel1::getMainMenuResultForSelection(int selection) const {
 	return selection + 2;
 }
 
+void InsaneRebel1::setVirtualKeyboardVisible(bool visible) {
+	if (!_vm->_system->hasFeature(OSystem::kFeatureVirtualKeyboard))
+		return;
+
+	if (_virtualKeyboardActive == visible)
+		return;
+
+	_vm->_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, visible);
+	_virtualKeyboardActive = visible;
+}
+
 void InsaneRebel1::beginTextEntry(bool passcodeMode) {
 	_textEntryActive = true;
 	_textEntryPasscodeMode = passcodeMode;
@@ -292,6 +303,8 @@ void InsaneRebel1::beginTextEntry(bool passcodeMode) {
 			_textEntryPickerIndex = 1;
 		}
 	}
+
+	setVirtualKeyboardVisible(true);
 }
 
 void InsaneRebel1::finishTextEntry(bool canceled) {
@@ -299,6 +312,7 @@ void InsaneRebel1::finishTextEntry(bool canceled) {
 	if (!canceled)
 		_textEntryDone = true;
 	_textEntryActive = false;
+	setVirtualKeyboardVisible(false);
 	_vm->_smushVideoShouldFinish = true;
 }
 
@@ -1134,6 +1148,7 @@ bool InsaneRebel1::runTextEntryMenuLoop() {
 	while (!shouldAbortGameFlow() && !_textEntryDone && !_textEntryCanceled)
 		playMenuBackground();
 
+	setVirtualKeyboardVisible(false);
 	return !shouldAbortGameFlow() && !_textEntryCanceled;
 }
 
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index d4b1a7929bc..59a87b0f136 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -378,6 +378,7 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_textEntryPasscodeMode = false;
 	_textEntryDone = false;
 	_textEntryCanceled = false;
+	_virtualKeyboardActive = false;
 	_textEntryPickerIndex = 0;
 	_textEntryPickerOffsetX = 0;
 	_textEntryMaxChars = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index e08e41245a8..5264989c70c 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -549,6 +549,7 @@ private:
 	int getMainMenuResultForSelection(int selection) const;
 	void playMenuBackground();
 	bool runTextEntryMenuLoop();
+	void setVirtualKeyboardVisible(bool visible);
 	void beginTextEntry(bool passcodeMode);
 	void finishTextEntry(bool canceled);
 	void selectTextEntryChar();
@@ -599,6 +600,7 @@ private:
 	bool _textEntryPasscodeMode;
 	bool _textEntryDone;
 	bool _textEntryCanceled;
+	bool _virtualKeyboardActive;
 	int _textEntryPickerIndex;
 	int _textEntryPickerOffsetX;
 	int _textEntryMaxChars;


Commit: 63b9827058128e9fc2990ceca8b30dbb47b7b0b5
    https://github.com/scummvm/scummvm/commit/63b9827058128e9fc2990ceca8b30dbb47b7b0b5
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-06-04T14:54:00+02:00

Commit Message:
SCUMM: RA1: better gamepad controls for some levels

Changed paths:
    engines/scumm/insane/rebel1/iact.cpp
    engines/scumm/insane/rebel1/menu.cpp
    engines/scumm/insane/rebel1/rebel.cpp
    engines/scumm/insane/rebel1/rebel.h
    engines/scumm/insane/rebel1/runlevels.cpp


diff --git a/engines/scumm/insane/rebel1/iact.cpp b/engines/scumm/insane/rebel1/iact.cpp
index eed59ccdb22..8baa313f60f 100644
--- a/engines/scumm/insane/rebel1/iact.cpp
+++ b/engines/scumm/insane/rebel1/iact.cpp
@@ -34,9 +34,10 @@ inline int16 applyRebel1AnalogDeadzone(int16 axisValue) {
 	return (ABS(axis) <= deadZone) ? 0 : axisValue;
 }
 
-inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue, int16 axisMax) {
+inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue,
+		int16 axisMax, int responseDivisor) {
 	const int delta = (int)inputValue - (int)filteredValue;
-	int step = delta / 10;
+	int step = delta / responseDivisor;
 
 	if (step == 0 && delta != 0)
 		step = (delta > 0) ? 1 : -1;
@@ -45,6 +46,29 @@ inline int16 smoothRebel1Op0BAnalogInput(int16 inputValue, int16 &filteredValue,
 	return filteredValue;
 }
 
+int16 shapeRebel1Op0BGamepadAxis(int16 inputValue, int16 axisMax) {
+	const int axis = CLIP<int>(inputValue, -axisMax, axisMax);
+	const int absAxis = ABS(axis);
+	const int precisionRadius = MAX<int>(1, (axisMax * 2) / 5);
+	const int precisionOutput = MAX<int>(1, precisionRadius / 3);
+	int shaped = 0;
+
+	if (absAxis <= precisionRadius) {
+		shaped = (absAxis * precisionOutput + precisionRadius / 2) / precisionRadius;
+	} else {
+		const int outerInput = absAxis - precisionRadius;
+		const int outerInputRange = axisMax - precisionRadius;
+		const int outerOutputRange = axisMax - precisionOutput;
+		const int linear = outerInput * outerOutputRange / outerInputRange;
+		const int accel = (outerInput * outerInput * outerOutputRange +
+			(outerInputRange * outerInputRange) / 2) /
+			(outerInputRange * outerInputRange);
+		shaped = precisionOutput + (linear + 2 * accel) / 3;
+	}
+
+	return axis < 0 ? -shaped : shaped;
+}
+
 const int16 kRA1Op09AimXScale[5] = { 0, 44, 88, 128, 165 };
 const int16 kRA1Op09AimYScale[5] = { 256, 252, 240, 221, 196 };
 const int kRA1DosMouseCenterX = 0x140;
@@ -807,10 +831,7 @@ void InsaneRebel1::updateFlightVariantCursor() {
 // jump latch from FUN_231BE, plus FUN_23115's DOS mouse recenter behavior.
 // Enhanced controls are ScummVM-only: they bypass the original recentering state
 // and expose a stable absolute centered mouse axis instead.
-bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
-	if (!isTouchscreenActive())
-		return false;
-
+bool InsaneRebel1::isOp0BReticleControlLevel() const {
 	switch (_currentLevel) {
 	case 1:  // Level 2
 	case 3:  // Level 4
@@ -826,6 +847,84 @@ bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
 	}
 }
 
+bool InsaneRebel1::shouldInvertTouchYSettingForCurrentLevel() const {
+	return isTouchscreenActive() && isOp0BReticleControlLevel();
+}
+
+bool InsaneRebel1::usesRelativeGamepadAimForCurrentLevel() const {
+	return _optEnhancedControls &&
+		(_currentLevel == 3 ||
+		 (_currentLevel == 4 && _levelGameplayPhase == 2) ||
+		 _currentLevel == 9 ||
+		 _currentLevel == 11 ||
+		 _currentLevel == 13);
+}
+
+void InsaneRebel1::resetRelativeGamepadAim() {
+	_gamepadAimAxisX = 0;
+	_gamepadAimAxisY = 0;
+	_gamepadAimActive = false;
+}
+
+bool InsaneRebel1::updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *usedJoystick) {
+	if (!usesRelativeGamepadAimForCurrentLevel())
+		return false;
+
+	const int dpadX =
+		(_vm->getActionState(kScummActionInsaneRight) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneLeft) ? 1 : 0);
+	int dpadY =
+		(_vm->getActionState(kScummActionInsaneUp) ? 1 : 0) -
+		(_vm->getActionState(kScummActionInsaneDown) ? 1 : 0);
+
+	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
+	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
+	const int analogX = CLIP<int32>(((int32)analogAxisX * 127) / Common::JOYAXIS_MAX, -127, 127);
+	int analogY = CLIP<int32>((-(int32)analogAxisY * 100) / Common::JOYAXIS_MAX, -100, 100);
+
+	if (_optControlsYFlip) {
+		dpadY = -dpadY;
+		analogY = -analogY;
+	}
+
+	int deltaX = 0;
+	int deltaY = 0;
+	bool activeGamepadAim = false;
+
+	if (dpadX || dpadY) {
+		const int kDigitalAimStep = 3;
+		deltaX = dpadX * kDigitalAimStep;
+		deltaY = dpadY * kDigitalAimStep;
+		activeGamepadAim = true;
+	} else if (analogX || analogY) {
+		const int kAnalogAimMaxStep = 8;
+		deltaX = analogX * kAnalogAimMaxStep / 127;
+		deltaY = analogY * kAnalogAimMaxStep / 100;
+		activeGamepadAim = true;
+	}
+
+	if (activeGamepadAim) {
+		if (!_gamepadAimActive) {
+			_gamepadAimAxisX = CLIP<int16>(_avgInputX, -127, 127);
+			_gamepadAimAxisY = CLIP<int16>((int16)-_avgInputY, -100, 100);
+		}
+		_gamepadAimAxisX = CLIP<int16>((int16)(_gamepadAimAxisX + deltaX), -127, 127);
+		_gamepadAimAxisY = CLIP<int16>((int16)(_gamepadAimAxisY + deltaY), -100, 100);
+		_gamepadAimActive = true;
+	}
+
+	if (!_gamepadAimActive)
+		return false;
+
+	if (usedJoystick)
+		*usedJoystick = true;
+	_activeInputSource = kInputSourceJoystickRelative;
+	_mouseVirtualValid = false;
+	inputX = _gamepadAimAxisX;
+	inputY = _gamepadAimAxisY;
+	return true;
+}
+
 void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick) {
 	if (usedJoystick)
 		*usedJoystick = false;
@@ -833,6 +932,9 @@ void InsaneRebel1::preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJ
 	if (_mouseRecentering)
 		return;
 
+	if (updateRelativeGamepadAim(inputX, inputY, usedJoystick))
+		return;
+
 	const int16 analogAxisX = applyRebel1AnalogDeadzone(_joystickAxisX);
 	const int16 analogAxisY = applyRebel1AnalogDeadzone(_joystickAxisY);
 	const int joyX =
@@ -1643,14 +1745,25 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 		inputSourceName = "joystick-analog";
 	else if (_activeInputSource == kInputSourceJoystickDigital)
 		inputSourceName = "joystick-dpad";
+	else if (_activeInputSource == kInputSourceJoystickRelative)
+		inputSourceName = "joystick-relative";
 
-	if (usedJoystick && _optEnhancedControls) {
+	const bool relativeGamepadReticle = usedJoystick && _activeInputSource == kInputSourceJoystickRelative;
+	const bool preciseGamepadReticle = usedJoystick && _optEnhancedControls &&
+		isOp0BReticleControlLevel() && !relativeGamepadReticle;
+
+	if (usedJoystick && _optEnhancedControls && !relativeGamepadReticle) {
 		// The 0x0B first-person handler is shared by multiple RA1 stages. Smooth
-		// analog stick input over time so these sections keep full reach without
-		// feeling hyper-sensitive, while leaving mouse behavior untouched.
+		// analog stick input over time and shape the affected gamepad levels with
+		// a low-gain linear center plus an accelerating outer range.
 		if (op0BAnalogSmoothing) {
-			inputX = smoothRebel1Op0BAnalogInput(inputX, _level2JoystickFilteredX, 127);
-			inputY = smoothRebel1Op0BAnalogInput(inputY, _level2JoystickFilteredY, 100);
+			if (preciseGamepadReticle) {
+				inputX = shapeRebel1Op0BGamepadAxis(inputX, 127);
+				inputY = shapeRebel1Op0BGamepadAxis(inputY, 100);
+			}
+			const int analogRampDivisor = preciseGamepadReticle ? 20 : 10;
+			inputX = smoothRebel1Op0BAnalogInput(inputX, _level2JoystickFilteredX, 127, analogRampDivisor);
+			inputY = smoothRebel1Op0BAnalogInput(inputY, _level2JoystickFilteredY, 100, analogRampDivisor);
 		} else {
 			_level2JoystickFilteredX = 0;
 			_level2JoystickFilteredY = 0;
@@ -1663,11 +1776,12 @@ void InsaneRebel1::updateGameOp0BPhysics() {
 	}
 	_inputAxisDeltaX = inputX;
 
-	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
+	debugC(DEBUG_INSANE, "RA1 GAME 0x0B input: frame=%d source=%s controls=%s window=%d precisionPad=%d view=(%d,%d) health=%d prevFlags=0x%02x axis=(%d,%d) mouse=(%d,%d) actions(L,R,U,D)=(%d,%d,%d,%d) raw=(%d,%d) final=(%d,%d) level=%d opcode=0x%X",
 		_gameCounter,
 		inputSourceName,
 		_optEnhancedControls ? "enhanced" : "original",
 		gameOp0BSmoothWindow,
+		preciseGamepadReticle ? 1 : 0,
 		_perspectiveX, _perspectiveY,
 		_health, _prevDamageFlags,
 		_joystickAxisX, _joystickAxisY,
@@ -2031,6 +2145,7 @@ void InsaneRebel1::handleGameOpcode5EReset(uint32 param1) {
 	_mouseVirtualValid = false;
 	_level2JoystickFilteredX = 0;
 	_level2JoystickFilteredY = 0;
+	resetRelativeGamepadAim();
 
 	_playerFired = false;
 	_fireCooldown = 0;
diff --git a/engines/scumm/insane/rebel1/menu.cpp b/engines/scumm/insane/rebel1/menu.cpp
index 15d07ef18c2..7677cf0d985 100644
--- a/engines/scumm/insane/rebel1/menu.cpp
+++ b/engines/scumm/insane/rebel1/menu.cpp
@@ -632,8 +632,13 @@ bool InsaneRebel1::notifyEvent(const Common::Event &event) {
 		return true;
 	}
 
-	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering)
+	if (event.type == Common::EVENT_MOUSEMOVE && !_mouseRecentering) {
+		if (_gamepadAimActive && usesRelativeGamepadAimForCurrentLevel() &&
+				_interactiveVideoActive && !_menuActive &&
+				(event.relMouse.x != 0 || event.relMouse.y != 0))
+			_gamepadAimActive = false;
 		_activeInputSource = kInputSourceMouse;
+	}
 
 	// Android direct touch reports taps as left-clicks. Treat those as mouse input
 	// during gameplay so tap-to-aim-and-fire works even if a joystick event was last.
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index 59a87b0f136..248b9ae29c5 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -276,6 +276,9 @@ InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
 	_lastJoystickAxisEventTime = 0;
 	_level2JoystickFilteredX = 0;
 	_level2JoystickFilteredY = 0;
+	_gamepadAimAxisX = 0;
+	_gamepadAimAxisY = 0;
+	_gamepadAimActive = false;
 	_activeInputSource = kInputSourceMouse;
 
 	_currentLevel = 0;
diff --git a/engines/scumm/insane/rebel1/rebel.h b/engines/scumm/insane/rebel1/rebel.h
index 5264989c70c..4d1478e66ae 100644
--- a/engines/scumm/insane/rebel1/rebel.h
+++ b/engines/scumm/insane/rebel1/rebel.h
@@ -221,7 +221,11 @@ private:
 	void updateTurretPhysics();
 	void updateTurretShipDirection(int16 offsetY);
 	void getCollisionShipCenter(int16 &x, int16 &y) const;
+	bool isOp0BReticleControlLevel() const;
 	bool shouldInvertTouchYSettingForCurrentLevel() const;
+	bool usesRelativeGamepadAimForCurrentLevel() const;
+	void resetRelativeGamepadAim();
+	bool updateRelativeGamepadAim(int16 &inputX, int16 &inputY, bool *usedJoystick);
 	void preprocessMouseAxes(int16 &inputX, int16 &inputY, bool *usedJoystick = nullptr);
 	void rebuildProjectionTable(int16 curveStep, int16 curveExtent);
 	void resetProjectionTable();
@@ -364,10 +368,14 @@ private:
 	uint32 _lastJoystickAxisEventTime;
 	int16 _level2JoystickFilteredX; // Smoothed Level 2 analog X input
 	int16 _level2JoystickFilteredY; // Smoothed Level 2 analog Y input
+	int16 _gamepadAimAxisX; // Relative gamepad 0x0B aim in preprocessed input space
+	int16 _gamepadAimAxisY;
+	bool _gamepadAimActive;
 	enum InputSource {
 		kInputSourceMouse,
 		kInputSourceJoystickAnalog,
-		kInputSourceJoystickDigital
+		kInputSourceJoystickDigital,
+		kInputSourceJoystickRelative
 	};
 	InputSource _activeInputSource;
 
diff --git a/engines/scumm/insane/rebel1/runlevels.cpp b/engines/scumm/insane/rebel1/runlevels.cpp
index 3623029abfb..1b7c05a8a0a 100644
--- a/engines/scumm/insane/rebel1/runlevels.cpp
+++ b/engines/scumm/insane/rebel1/runlevels.cpp
@@ -246,6 +246,7 @@ void InsaneRebel1::resetLevelInputHistory(bool resetAxisDeltaX) {
 		_inputAxisDeltaX = 0;
 	_avgInputX = 0;
 	_avgInputY = 0;
+	resetRelativeGamepadAim();
 }
 
 void InsaneRebel1::resetLevelAttemptState(int16 flyControlMode, int16 gameplayPhase,
@@ -1603,6 +1604,7 @@ void InsaneRebel1::setupInteractiveVideoState(int32 startFrame) {
 	if (!preserveRuntimeState) {
 		_onFootInitialized = false;  // Reset so each segment triggers counter==0 init
 		resetFrameObjectState();
+		resetRelativeGamepadAim();
 	}
 	_vm->_smushVideoShouldFinish = false;
 	// Route resumes stay in the same gameplay flow in the original executable.




More information about the Scummvm-git-logs mailing list