[PC-BSD Commits] r6331 - pcbsd/trunk/SysInstaller
svn at pcbsd.org
svn at pcbsd.org
Wed Mar 3 06:52:19 PST 2010
Author: kris
Date: 2010-03-03 06:52:19 -0800 (Wed, 03 Mar 2010)
New Revision: 6331
Added:
pcbsd/trunk/SysInstaller/dialogZFSMount.cpp
pcbsd/trunk/SysInstaller/dialogZFSMount.h
pcbsd/trunk/SysInstaller/dialogZFSMount.ui
Modified:
pcbsd/trunk/SysInstaller/SysInstaller.pro
pcbsd/trunk/SysInstaller/dialogFileSystem.cpp
pcbsd/trunk/SysInstaller/dialogFileSystem.h
pcbsd/trunk/SysInstaller/sys-diskwidget.cpp
Log:
Large update of SysInstaller, now we have full ZFS control, able to set ZFS FileSystem mount points
per-zpool, enable options such as mirror / raidz in the creation of said zpool
Modified: pcbsd/trunk/SysInstaller/SysInstaller.pro
===================================================================
--- pcbsd/trunk/SysInstaller/SysInstaller.pro 2010-03-03 10:03:56 UTC (rev 6330)
+++ pcbsd/trunk/SysInstaller/SysInstaller.pro 2010-03-03 14:52:19 UTC (rev 6331)
@@ -15,11 +15,13 @@
sys-userwidget.cpp \
dialogFileSystem.cpp \
dialogSelectNet.cpp \
+ dialogZFSMount.cpp \
sysinstaller.cpp \
backend.cpp
HEADERS += sysinstaller.h \
dialogFileSystem.h \
dialogSelectNet.h \
+ dialogZFSMount.h \
backend.h
TRANSLATIONS = i18n/SysInstaller_af.ts \
i18n/SysInstaller_ar.ts \
@@ -90,5 +92,5 @@
INSTALLS += dotrans
-FORMS += sysinstaller.ui dialogFileSystem.ui dialogSelectNet.ui
+FORMS += sysinstaller.ui dialogFileSystem.ui dialogSelectNet.ui dialogZFSMount.ui
RESOURCES += sysinstaller.qrc
Modified: pcbsd/trunk/SysInstaller/dialogFileSystem.cpp
===================================================================
--- pcbsd/trunk/SysInstaller/dialogFileSystem.cpp 2010-03-03 10:03:56 UTC (rev 6330)
+++ pcbsd/trunk/SysInstaller/dialogFileSystem.cpp 2010-03-03 14:52:19 UTC (rev 6331)
@@ -19,6 +19,8 @@
// Connnect our slots
connect(pushCancel, SIGNAL(clicked()), this, SLOT(slotPushCancel()));
connect(pushSave, SIGNAL(clicked()), this, SLOT(slotPushSave()));
+ connect(pushAddZFS, SIGNAL(clicked()), this, SLOT(slotAddZFSMount()));
+ connect(pushRemoveZFS, SIGNAL(clicked()), this, SLOT(slotRemoveZFSMount()));
connect(comboDiskSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(slotDiskChanged(int)));
connect(comboDiskType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotTypeChanged(int)));
connect(horizontalSizeSlider, SIGNAL(sliderMoved(int)), this, SLOT(slotSliderChangedValue(int)));
@@ -49,6 +51,8 @@
// Add the available zpool items
populateZpoolList();
+ listZFSFileSystems->setSortingEnabled(true);
+
// Check if we should enable the save slot
slotCheckSanity();
@@ -79,6 +83,21 @@
return true;
}
+// Disk no mirror setup
+bool dialogFileSystem::diskNoZFSMirrorSet(QString Device)
+{
+ // Loop through the disk layouts and look for any "XtraOptions"
+ for (int i=0; i < sysFinalDiskLayout.count(); ++i) {
+ QStringList XtraOpts = sysFinalDiskLayout.at(i).at(5).split(" ");
+ for (int z = 0; z < XtraOpts.size(); ++z)
+ if ( XtraOpts.at(z) == Device )
+ return false;
+ }
+
+ return true;
+}
+
+
// Function which checks that we don't have any partitions on this disk already specified
bool dialogFileSystem::diskNoExistingPartition(QString Device)
{
@@ -102,7 +121,7 @@
// Start adding / listing our disks / partitions available
for (int i=0; i < sysDisks.count(); ++i) {
// Make sure to only add the drives to the comboDiskList
- if ( sysDisks.at(i).at(0) == "DRIVE" && diskNoExistingPartition(sysDisks.at(i).at(1)) && calculateFreeSpace(i) > 0 ) {
+ if ( sysDisks.at(i).at(0) == "DRIVE" && diskNoExistingPartition(sysDisks.at(i).at(1)) ) {
availSize.setNum(calculateFreeSpace(i));
desc = sysDisks.at(i).at(3);
desc = desc.remove(0, 1);
@@ -110,7 +129,7 @@
desc.truncate(20);
comboDiskSelection->addItem(sysDisks.at(i).at(1) + " - " + sysDisks.at(i).at(2) + "MB (" + availSize + " Avail) " + desc);
comboMirrorDisk->addItem(sysDisks.at(i).at(1) + " - " + sysDisks.at(i).at(2) + "MB " + sysDisks.at(i).at(3));
- } else if ( sysDisks.at(i).at(0) == "SLICE" && sliceNoExistingDiskPartition(sysDisks.at(i).at(2) ) && calculateFreeSpace(i) > 0 ) {
+ } else if ( sysDisks.at(i).at(0) == "SLICE" && sliceNoExistingDiskPartition(sysDisks.at(i).at(2) ) ) {
availSize.setNum(calculateFreeSpace(i));
desc = sysDisks.at(i).at(4);
if ( desc != "Unused Space") {
@@ -154,7 +173,8 @@
if ( sysDisks.at(i).at(0) == "DRIVE" \
&& diskNoExistingPartition(sysDisks.at(i).at(1)) \
&& curDevice != sysDisks.at(i).at(1) \
- && calculateFreeSpace(i) > 0 ) {
+ ) {
+ //&& calculateFreeSpace(i) > 0 ) {
availSize.setNum(calculateFreeSpace(i));
desc = sysDisks.at(i).at(3);
desc = desc.remove(0, 1);
@@ -163,6 +183,8 @@
QListWidgetItem *zpoolDev = new QListWidgetItem(sysDisks.at(i).at(1) + " - " + sysDisks.at(i).at(2) + "MB (" + availSize + " Avail) " + desc, listZPoolDevices);
zpoolDev->setCheckState(Qt::Unchecked);
+ if ( calculateFreeSpace(i) == 0 )
+ zpoolDev->setHidden(true);
}
}
@@ -171,20 +193,25 @@
// Public slot which lets us set if we are editing a partition, or creating a new one
void dialogFileSystem::setEditing( int id )
{
- QString searchDev, cText, tmp;
+ QString searchDev, cText, tmp, FsType, XtraOpts;
int size;
bool ok;
isEditing = true;
editIndex = id;
+ FsType = sysFinalDiskLayout.at(id).at(3);
+ XtraOpts = sysFinalDiskLayout.at(id).at(5);
+
// Lets figure out which slice / disk to show in our combo box before disabling it
if ( sysFinalDiskLayout.at(id).at(1) == "ALL" )
searchDev = sysFinalDiskLayout.at(id).at(0);
else
searchDev = sysFinalDiskLayout.at(id).at(0) + sysFinalDiskLayout.at(id).at(1);
+ //qDebug() << "Edit Dev:" << searchDev;
+
for ( int i = 0; i <= comboDiskSelection->count(); i++ )
{
cText = comboDiskSelection->itemText(i);
@@ -238,12 +265,17 @@
size = tmp.toInt(&ok);
if ( ok )
{
- int maxsize = calculateFreeSpace(getSysIndexFromCombo(comboDiskSelection->currentIndex())) + size;
+ int maxsize = calculateFreeSpace(getSysIndexFromCombo(comboDiskSelection->currentIndex() ) ) + size;
horizontalSizeSlider->setMinimum(0);
horizontalSizeSlider->setMaximum(maxsize);
horizontalSizeSlider->setValue(size);
+ horizontalSizeSliderZFS->setMinimum(0);
+ horizontalSizeSliderZFS->setMaximum(maxsize);
+ horizontalSizeSliderZFS->setValue(size);
spinSize->setRange(0, maxsize);
spinSize->setValue(size);
+ spinSizeZFS->setRange(0, maxsize);
+ spinSizeZFS->setValue(size);
}
// Figure out the mnt
@@ -261,12 +293,45 @@
break;
}
}
+ } else {
+ // If this is a ZFS file-system
+ if ( FsType.indexOf("ZFS") != -1 ) {
+ stackedWidgetOptions->setCurrentIndex(2);
+
+ // Add the ZFS mounts
+ QStringList ZMounts = tmp.split(",");
+ for (int i = 0; i < ZMounts.size(); ++i)
+ listZFSFileSystems->addItem(ZMounts.at(i));
+
+ // Check for any XtraOpts for ZFS
+ if ( XtraOpts.indexOf("mirror:") != -1 ) {
+ comboPoolType->setCurrentIndex(1) ;
+ XtraOpts = XtraOpts.remove(0, XtraOpts.indexOf(":") + 1);
+ }
+ if ( XtraOpts.indexOf("raidz:") != -1 ) {
+ comboPoolType->setCurrentIndex(2);
+ XtraOpts = XtraOpts.remove(0, XtraOpts.indexOf(":") + 1);
+ }
+ XtraOpts = XtraOpts.simplified();
+ // Check any other devices included in the zpool
+ QStringList PoolDevs = XtraOpts.split(" ");
+ for (int i = 0; i < PoolDevs.size(); ++i)
+ for ( int z=0; z<listZPoolDevices->count(); ++z)
+ if ( listZPoolDevices->item(z)->text().indexOf(PoolDevs.at(i) + " ") == 0 ) {
+ listZPoolDevices->item(z)->setCheckState(Qt::Checked);
+ listZPoolDevices->item(z)->setHidden(false);
+ }
- } else {
- if ( tmp == "none" )
- tmp = "";
- lineEditMount->setText(tmp);
+ } else {
+ if ( tmp == "none" )
+ tmp = "";
+ // Not ZFS, regular UFS
+ lineEditMount->setText(tmp);
+ }
}
+
+ // Go ahead and enable the save button assuming things are OK
+ slotCheckSanity();
}
@@ -290,6 +355,36 @@
slotCheckSanity();
}
+// Slot to add a new ZFS mount to our list
+void dialogFileSystem::slotAddZFSMount()
+{
+ dialogZFSMount *dialogZFS = new dialogZFSMount();
+ dialogZFS->dialogInit();
+ connect(dialogZFS, SIGNAL(saved(QString)), this, SLOT(slotSaveNewZFSMount(QString)));
+ dialogZFS->show();
+
+}
+
+// Slot to add the new ZFS mount to our array
+void dialogFileSystem::slotSaveNewZFSMount(QString newMount)
+{
+ listZFSFileSystems->addItem(newMount);
+
+ slotCheckSanity();
+}
+
+// Slot to remove a ZFS mount to our list
+void dialogFileSystem::slotRemoveZFSMount()
+{
+ if ( listZFSFileSystems->currentRow() == -1 )
+ return;
+
+ delete listZFSFileSystems->takeItem(listZFSFileSystems->currentRow());
+
+ slotCheckSanity();
+}
+
+
// Slot for checking the current selected disk / partition
void dialogFileSystem::slotDiskChanged(int index)
{
@@ -394,7 +489,6 @@
sysIndex = diskIndex;
- // Set our vars with Disk / Slice info, size, etc
if ( sysDisks.at(sysIndex).at(0) == "DRIVE") {
Disk = sysDisks.at(sysIndex).at(1);
Slice = "ALL";
@@ -412,6 +506,14 @@
if (!ok)
return 0;
+ // Check if this disk / slice is used in a raid already
+ if ( ! diskNoZFSMirrorSet(sysDisks.at(sysIndex).at(1)) )
+ return 0;
+ if ( Slice != "ALL" )
+ if ( ! diskNoZFSMirrorSet(sysDisks.at(sysIndex).at(1) + sysDisks.at(sysIndex).at(2)) )
+ return 0;
+
+
// Set the available size to the entire thing, and start decrementing
availSize = fullSize - 5;
@@ -426,6 +528,8 @@
}
}
+
+ //qDebug() << "Disk:" << Disk << "Slice:" << Slice << "Avail Size:" << availSize;
return availSize;
}
@@ -455,7 +559,8 @@
// If we are on a non-mirror type
- if ( comboDiskType->currentText() != "MIRROR" ) {
+ if ( comboDiskType->currentText() != "MIRROR" \
+ && comboDiskType->currentText() != "ZFS" ) {
if ( spinSize->value() <= 0)
ready = false;
@@ -470,15 +575,15 @@
ready = false;
}
+ // Check if we already have one of these mounts
+ if ( ! checkExistingMount() ) {
+ labelStatus->setText(tr("Error: A partition with this mount point already exists!"));
+ ready = false;
+ }
+
int totalPart = 0;
for (int i=0; i < sysFinalDiskLayout.count(); ++i) {
- // Make sure we don't have a duplicate mount point and not editing
- if ( sysFinalDiskLayout.at(i).at(2) == lineEditMount->text() && ! isEditing ) {
- ready = false;
- labelStatus->setText(tr("Error: A partition with this mount-point already exists."));
- }
-
// Check if we already have a swap for this partition
if ( sysFinalDiskLayout.at(i).at(0) == Disk \
&& sysFinalDiskLayout.at(i).at(1) == Slice \
@@ -506,7 +611,24 @@
labelStatus->setText(tr("Error: No more available partitions for this disk/slice."));
}
+ } else if ( comboDiskType->currentText() == "ZFS" ) {
+ // ZFS Sanity Checks
+ if ( spinSize->value() <= 0)
+ ready = false;
+
+ if ( listZFSFileSystems->count() <= 0 ) {
+ labelStatus->setText(tr("Error: Need a at least one ZFS File-System mount-point."));
+ ready = false;
+ }
+
+ // Check if we already have one of these mounts
+ if ( ! checkExistingMount() ) {
+ labelStatus->setText(tr("Error: This mount point already exists!"));
+ ready = false;
+ }
+
} else {
+ // Mirror sanity checks
QString selDev = comboDiskSelection->currentText();
QString mirrorDev = comboMirrorDisk->currentText();
selDev.truncate(selDev.indexOf(" -") );
@@ -522,11 +644,36 @@
}
+// Make sure we don't have a duplicate mount point and not editing
+bool dialogFileSystem::checkExistingMount()
+{
+
+ // Check ZFS Types
+ if ( comboDiskType->currentText() == "ZFS" ) {
+ for ( int z=0; z < listZFSFileSystems->count(); ++z)
+ for (int i=0; i < sysFinalDiskLayout.count(); ++i) {
+ QStringList ZMounts = sysFinalDiskLayout.at(i).at(2).split(",");
+ for (int j = 0; j < ZMounts.size(); ++j)
+ if ( ZMounts.at(j) == listZFSFileSystems->item(z)->text() && ! isEditing )
+ return false;
+ }
+ } else {
+ for (int i=0; i < sysFinalDiskLayout.count(); ++i) {
+ QStringList ZMounts = sysFinalDiskLayout.at(i).at(2).split(",");
+ for (int j = 0; j < ZMounts.size(); ++j)
+ if ( ZMounts.at(j) == lineEditMount->text() && ! isEditing )
+ return false;
+ }
+ }
+
+ return true;
+}
+
// add the new FS to our list, and emit it back to the parent
void dialogFileSystem::addEmit()
{
QStringList fileSystem;
- QString Disk, Slice, Size, Mount, fsType, tmp;
+ QString Disk, Slice, Size, Mount, fsType, tmp, XtraOpts;
// Get the index
int sysIndex = getSysIndexFromCombo(comboDiskSelection->currentIndex());
@@ -553,6 +700,36 @@
fsType = comboDiskType->currentText();
if ( checkEncryption->isChecked() )
fsType = fsType + ".eli";
+ } else if ( comboDiskType->currentText() == "ZFS" ) {
+ // ZFS, Set our file-system mounts with a "," delimiter
+
+ for ( int z=0; z<listZFSFileSystems->count(); ++z) {
+ if ( Mount.isEmpty() )
+ Mount = listZFSFileSystems->item(z)->text();
+ else
+ Mount = Mount + "," + listZFSFileSystems->item(z)->text();
+
+ // Save the XtraOpts for ZFS
+ XtraOpts = comboPoolType->currentText();
+ if ( XtraOpts == "basic" )
+ XtraOpts = "";
+ else
+ XtraOpts = XtraOpts + ":";
+
+ for ( int z=0; z<listZPoolDevices->count(); ++z)
+ if ( listZPoolDevices->item(z)->checkState() == Qt::Checked ) {
+ tmp = listZPoolDevices->item(z)->text();
+ tmp.truncate(tmp.indexOf(" "));
+ XtraOpts = XtraOpts + " " + tmp;
+ XtraOpts = XtraOpts.simplified();
+ }
+ } // Enf of ZFS Setup
+
+
+ Size = tmp.setNum(spinSize->value());
+ fsType = comboDiskType->currentText();
+ if ( checkEncryption->isChecked() )
+ fsType = fsType + ".eli";
} else {
Size = tmp.setNum(spinSize->value());
Mount = lineEditMount->text();
@@ -561,7 +738,9 @@
fsType = fsType + ".eli";
}
+
+
// If we are editing an existing device, update it
if ( isEditing )
{
@@ -570,9 +749,10 @@
sysFinalDiskLayout[editIndex][2] = Mount;
sysFinalDiskLayout[editIndex][3] = fsType;
sysFinalDiskLayout[editIndex][4] = Size;
+ sysFinalDiskLayout[editIndex][5] = XtraOpts;
} else {
// Add a new Device
- fileSystem << Disk << Slice << Mount << fsType << Size;
+ fileSystem << Disk << Slice << Mount << fsType << Size << XtraOpts;
sysFinalDiskLayout << fileSystem;
}
Modified: pcbsd/trunk/SysInstaller/dialogFileSystem.h
===================================================================
--- pcbsd/trunk/SysInstaller/dialogFileSystem.h 2010-03-03 10:03:56 UTC (rev 6330)
+++ pcbsd/trunk/SysInstaller/dialogFileSystem.h 2010-03-03 14:52:19 UTC (rev 6331)
@@ -7,6 +7,7 @@
#include "ui_dialogFileSystem.h"
+#include "dialogZFSMount.h"
#include "backend.h"
@@ -32,13 +33,19 @@
void slotSpinBoxChanged(int newVal);
void slotSliderChangedValue(int newVal);
void slotCheckSanity();
+ void slotAddZFSMount();
+ void slotRemoveZFSMount();
+ void slotSaveNewZFSMount(QString newMount);
private:
+ dialogZFSMount *dialogZFS;
int getSysIndexFromCombo(int cIndex);
void populateZpoolList();
void addEmit();
bool diskNoExistingPartition(QString Device);
+ bool diskNoZFSMirrorSet(QString Device);
bool sliceNoExistingDiskPartition(QString Device);
+ bool checkExistingMount();
void addDisksSane();
bool isEditing;
int editIndex;
Modified: pcbsd/trunk/SysInstaller/sys-diskwidget.cpp
===================================================================
--- pcbsd/trunk/SysInstaller/sys-diskwidget.cpp 2010-03-03 10:03:56 UTC (rev 6330)
+++ pcbsd/trunk/SysInstaller/sys-diskwidget.cpp 2010-03-03 14:52:19 UTC (rev 6331)
@@ -104,12 +104,15 @@
// Start by checking for some important mounts
for (int i=0; i < sysFinalDiskLayout.count(); ++i) {
- if ( sysFinalDiskLayout.at(i).at(2) == "/" )
- haveRoot = true;
- if ( sysFinalDiskLayout.at(i).at(2) == "/usr" )
- haveUsr = true;
- if ( sysFinalDiskLayout.at(i).at(2) == "/var" )
- haveVar = true;
+ QStringList mounts = sysFinalDiskLayout.at(i).at(2).split(",");
+ for (int z = 0; z < mounts.size(); ++z) {
+ if ( mounts.at(z) == "/" )
+ haveRoot = true;
+ if ( mounts.at(z) == "/usr" )
+ haveUsr = true;
+ if ( mounts.at(z) == "/var" )
+ haveVar = true;
+ }
if ( sysFinalDiskLayout.at(i).at(3) == "SWAP" \
|| sysFinalDiskLayout.at(i).at(3) == "SWAP.eli" )
haveSwap = true;
@@ -403,7 +406,7 @@
{
QStringList tmpList;
QList<QStringList> copyList;
- QString tmp, workingDisk, workingSlice, tmpSlice;
+ QString tmp, workingDisk, workingSlice, tmpSlice, XtraTmp;
int disk = 0;
// Copy over the list to a new variable we can mangle without modifying the original
@@ -461,22 +464,33 @@
// Start by looking for the root partition
for (int i=0; i < copyList.count(); ++i) {
- if ( copyList.at(i).at(0) == workingDisk \
- && copyList.at(i).at(1) == workingSlice \
- && copyList.at(i).at(2) == "/" ) {
- tmpList << "disk" + tmp.setNum(disk) + "-part=" \
- + copyList.at(i).at(3) + " " + copyList.at(i).at(4) \
- + " " + copyList.at(i).at(2);
+ QStringList mounts = copyList.at(i).at(2).split(",");
+ for (int z = 0; z < mounts.size(); ++z) {
+ if ( copyList.at(i).at(0) == workingDisk \
+ && copyList.at(i).at(1) == workingSlice \
+ && mounts.at(z) == "/" ) {
- summaryList << "";
- summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
- summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
- summaryList << tr("Size:") + " " + copyList.at(i).at(4) + "MB ";
- summaryList << tr("Mount:") + " " + copyList.at(i).at(2);
+ // Check if we have any extra arguments to throw on the end
+ XtraTmp="";
+ if ( ! copyList.at(i).at(5).isEmpty() )
+ XtraTmp=" (" + copyList.at(i).at(5) + ")" ;
- // Done with this item, remove it now
- copyList.removeAt(i);
- break;
+ // Write out the partition line
+ tmpList << "disk" + tmp.setNum(disk) + "-part=" \
+ + copyList.at(i).at(3) + " " + copyList.at(i).at(4) \
+ + " " + copyList.at(i).at(2) + XtraTmp;
+
+ // Write the user summary
+ summaryList << "";
+ summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
+ summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
+ summaryList << tr("Size:") + " " + copyList.at(i).at(4) + "MB ";
+ summaryList << tr("Mount:") + " " + copyList.at(i).at(2);
+
+ // Done with this item, remove it now
+ copyList.removeAt(i);
+ break;
+ }
}
}
@@ -486,9 +500,13 @@
if ( copyList.at(i).at(0) == workingDisk \
&& copyList.at(i).at(1) == workingSlice \
&& copyList.at(i).at(2) == "SWAP" ) {
+
+ // Write the partition line
tmpList << "disk" + tmp.setNum(disk) + "-part=" \
+ copyList.at(i).at(3) + " " + copyList.at(i).at(4) \
+ " none";
+
+ // Write the user summary
summaryList << "";
summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
@@ -506,9 +524,18 @@
for (int i=0; i < count; ++i) {
if ( copyList.at(i).at(0) == workingDisk \
&& copyList.at(i).at(1) == workingSlice ) {
+
+ // Check if we have any extra arguments to throw on the end
+ XtraTmp="";
+ if ( ! copyList.at(i).at(5).isEmpty() )
+ XtraTmp=" (" + copyList.at(i).at(5) + ")" ;
+
+ // Write the partition line
tmpList << "disk" + tmp.setNum(disk) + "-part=" \
+ copyList.at(i).at(3) + " " + copyList.at(i).at(4) \
- + " " + copyList.at(i).at(2);
+ + " " + copyList.at(i).at(2) + XtraTmp;
+
+ // Write the user summary
summaryList << "";
summaryList << tr("Partition:") + " " + workingDisk + "(" + workingSlice + "):";
summaryList << tr("FileSystem:") + " " + copyList.at(i).at(3);
@@ -525,6 +552,7 @@
// Close out this partition section
tmpList << "commitDiskLabel";
+ tmpList << "";
// Increment our disk counter
disk++;
More information about the Commits
mailing list