diff --git a/io/io/inc/LinkDef.h b/io/io/inc/LinkDef.h
index 6aed5e56af922057d07ed486c9bd3a4e4f237091..121994e34c18d52ac7d5b0b8265a4de871c0d8f3 100644
--- a/io/io/inc/LinkDef.h
+++ b/io/io/inc/LinkDef.h
@@ -21,6 +21,7 @@
 #pragma link C++ class TFile-;
 #pragma link C++ class TFileCacheRead+;
 #pragma link C++ class TFileCacheWrite+;
+#pragma link C++ class TFileMerger+;
 #pragma link C++ class TFree;
 #pragma link C++ class TKey-;
 #pragma link C++ class TKeyMapFile;
diff --git a/proof/proofplayer/inc/TFileMerger.h b/io/io/inc/TFileMerger.h
similarity index 100%
rename from proof/proofplayer/inc/TFileMerger.h
rename to io/io/inc/TFileMerger.h
diff --git a/proof/proofplayer/src/TFileMerger.cxx b/io/io/src/TFileMerger.cxx
similarity index 98%
rename from proof/proofplayer/src/TFileMerger.cxx
rename to io/io/src/TFileMerger.cxx
index f16b1b7118c90f3c5dc6a5483c12419569ed1f16..99329514084f8151283a74eb3de28b90d1223b27 100644
--- a/proof/proofplayer/src/TFileMerger.cxx
+++ b/io/io/src/TFileMerger.cxx
@@ -222,9 +222,12 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist)
    path.Remove(0, path.Last(':') + 2);
 
    // Gain time, do not add the objects in the list in memory.
-   Bool_t addDirStat = gROOT->ProcessLineFast("TH1::AddDirectoryStatus()");
-   gROOT->ProcessLine("TH1::AddDirectory(kFALSE);");
-
+   Bool_t addDirStat = kTRUE;
+   if (R__TH1_Class) {
+      gROOT->ProcessLineFast("TH1::AddDirectoryStatus()");
+      gROOT->ProcessLine("TH1::AddDirectory(kFALSE);");
+   }
+   
    TDirectory *first_source = (TDirectory*)sourcelist->First();
 
    Int_t nguess = sourcelist->GetSize()+1000;
@@ -485,6 +488,8 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist)
    }
    // save modifications to target file
    target->SaveSelf(kTRUE);
-   gROOT->ProcessLine(TString::Format("TH1::AddDirectory(%d);",addDirStat));
+   if (R__TH1_Class) {
+      gROOT->ProcessLine(TString::Format("TH1::AddDirectory(%d);",addDirStat));
+   }
    return status;
 }
diff --git a/main/src/hadd.cxx b/main/src/hadd.cxx
index 4a36fcc818056f72b0100fb4ff0a012d6de5769e..93e39f02537bcc0c1ab4e568ed33c203bf6bac15 100644
--- a/main/src/hadd.cxx
+++ b/main/src/hadd.cxx
@@ -61,11 +61,8 @@
 
 #include "RConfig.h"
 #include <string>
-#include "TChain.h"
 #include "TFile.h"
 #include "THashList.h"
-#include "TH1.h"
-#include "THStack.h"
 #include "TKey.h"
 #include "TObjString.h"
 #include "Riostream.h"
@@ -73,6 +70,8 @@
 #include "TSystem.h"
 #include <stdlib.h>
 
+#include "TFileMerger.h"
+
 TList *FileList;
 TFile *Target, *Source;
 Bool_t noTrees;
@@ -143,23 +142,30 @@ int main( int argc, char **argv )
 
    cout << "Target file: " << argv[ffirst-1] << endl;
 
-   Target = TFile::Open( argv[ffirst-1], (force?"RECREATE":"CREATE") );
-   if (!Target || Target->IsZombie()) {
+   TFileMerger merger(kFALSE,kFALSE);
+   merger.SetPrintLevel(99);
+   if (!merger.OutputFile(argv[ffirst-1],force,newcomp) ) {
       cerr << "Error opening target file (does " << argv[ffirst-1] << " exist?)." << endl;
       cerr << "Pass \"-f\" argument to force re-creation of output file." << endl;
       exit(1);
    }
-   Target->SetCompressionLevel(newcomp);
-
-   // by default hadd can merge Trees in a file that can go up to 100 Gbytes
-   // No need to set this, as 100Gb is now the TTree default
-   // Long64_t maxsize = 100000000; //100GB
-   // maxsize *= 1000;  //to bypass some compiler limitations with big constants
-   // TTree::SetMaxTreeSize(maxsize);
 
    fastMethod = kTRUE;
    for ( int i = ffirst; i < argc; i++ ) {
-      if( AddFile(FileList, argv[i], newcomp) !=0 ) {
+      if (argv[i] && argv[i][0]=='@') {
+         std::ifstream indirect_file(argv[i]+1);
+         if( ! indirect_file.is_open() ) {
+            std::cerr<< "Could not open indirect file " << (argv[i]+1) << std::endl;
+            return 1;
+         }
+         while( indirect_file ){
+            std::string line;
+            std::getline(indirect_file, line);
+            if( !merger.AddFile(line.c_str()) ) {
+               return 1;
+            }
+         }         
+      } else if( ! merger.AddFile(argv[i]) ) {
          if ( skip_errors ) {
             cerr << "Skipping file with error: " << argv[i] << endl;
          } else {
@@ -168,257 +174,20 @@ int main( int argc, char **argv )
          }
       }
    }
-   if (!fastMethod && !reoptimize) {
+   if (merger.HasCompressionChange() && !reoptimize) {
       // Don't warn if the user any request re-optimization.
       cout <<"Sources and Target have different compression levels"<<endl;
       cout <<"Merging will be slower"<<endl;
    }
 
-   int status = MergeRootfile( Target, FileList);
+   Bool_t status = merger.Merge();
 
    //must delete Target to avoid a problem with dictionaries in~ TROOT
    delete Target;
 
-   return status;
-}
-
-//___________________________________________________________________________
-int AddFile(TList* sourcelist, std::string entry, int newcomp)
-{
-   // add a new file to the list of files
-   static int count(0);
-   if( entry.empty() ) return 0;
-   size_t j =entry.find_first_not_of(' ');
-   if( j==std::string::npos ) return 0;
-   entry = entry.substr(j);
-   if( entry.substr(0,1)=="@"){
-      std::ifstream indirect_file(entry.substr(1).c_str() );
-      if( ! indirect_file.is_open() ) {
-         std::cerr<< "Could not open indirect file " << entry.substr(1) << std::endl;
-         return 1;
-      }
-      while( indirect_file ){
-         std::string line;
-         std::getline(indirect_file, line);
-         if( AddFile(sourcelist, line, newcomp)!=0 )return 1;;
-      }
+   if (status) {
       return 0;
-   }
-   cout << "Source file " << (++count) << ": " << entry << endl;
-
-   TFile* source = TFile::Open( entry.c_str());
-   if( source==0 ){
-      cerr << "Could not open file " << entry << endl;
-      return 1;
-   } else if ( source->IsZombie() ) {
-      cerr << "Could not properly read file " << entry << endl;
+   } else {
       return 1;
    }
-   sourcelist->Add(source);
-   if (newcomp != source->GetCompressionLevel()) fastMethod = kFALSE;
-   return 0;
-}
-
-
-//___________________________________________________________________________
-int MergeRootfile( TDirectory *target, TList *sourcelist)
-{
-   // Merge all objects in a directory
-   int status = 0;
-   cout << "Target path: " << target->GetPath() << endl;
-   TString path( (char*)strstr( target->GetPath(), ":" ) );
-   path.Remove( 0, 2 );
-
-   TDirectory *first_source = (TDirectory*)sourcelist->First();
-   Int_t nguess = sourcelist->GetSize()+1000;
-   THashList allNames(nguess);
-   ((THashList*)target->GetList())->Rehash(nguess);
-   ((THashList*)target->GetListOfKeys())->Rehash(nguess);
-   TList listH;
-   TString listHargs;
-   listHargs.Form("((TCollection*)0x%lx)", (ULong_t)&listH);
-   while(first_source) {
-      TDirectory *current_sourcedir = first_source->GetDirectory(path);
-      if (!current_sourcedir) {
-         first_source = (TDirectory*)sourcelist->After(first_source);
-         continue;
-      }
-
-      // loop over all keys in this directory
-      TChain *globChain = 0;
-      TIter nextkey( current_sourcedir->GetListOfKeys() );
-      TKey *key, *oldkey=0;
-      //gain time, do not add the objects in the list in memory
-      TH1::AddDirectory(kFALSE);
-
-      while ( (key = (TKey*)nextkey())) {
-         if (current_sourcedir == target) break;
-         //keep only the highest cycle number for each key
-         if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue;
-         if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;}
-         if (allNames.FindObject(key->GetName())) continue;
-         TClass *cl = TClass::GetClass(key->GetClassName());
-         if (!cl || !cl->InheritsFrom(TObject::Class())) {
-            cout << "Cannot merge object type, name: "
-                 << key->GetName() << " title: " << key->GetTitle() << endl;
-            continue;
-         }
-         allNames.Add(new TObjString(key->GetName()));
-         // read object from first source file
-         //current_sourcedir->cd();
-         TObject *obj = key->ReadObj();
-         //printf("keyname=%s, obj=%x\n",key->GetName(),obj);
-
-         if ( obj->IsA()->InheritsFrom( TTree::Class() ) ) {
-
-            // loop over all source files create a chain of Trees "globChain"
-            if (!noTrees) {
-               TString obj_name;
-               if (path.Length()) {
-                  obj_name = path + "/" + obj->GetName();
-               } else {
-                  obj_name = obj->GetName();
-               }
-               globChain = new TChain(obj_name);
-               globChain->Add(first_source->GetName());
-               TFile *nextsource = (TFile*)sourcelist->After( first_source );
-               while ( nextsource ) {
-                  //do not add to the list a file that does not contain this Tree
-                  TFile *curf = TFile::Open(nextsource->GetName());
-                  if (curf) {
-                     Bool_t mustAdd = kFALSE;
-                     if (curf->FindKey(obj_name)) {
-                        mustAdd = kTRUE;
-                     } else {
-                        //we could be more clever here. No need to import the object
-                        //we are missing a function in TDirectory
-                        TObject *aobj = curf->Get(obj_name);
-                        if (aobj) { mustAdd = kTRUE; delete aobj;}
-                     }
-                     if (mustAdd) {
-                        globChain->Add(nextsource->GetName());
-                     }
-                  }
-                  delete curf;
-                  nextsource = (TFile*)sourcelist->After( nextsource );
-               }
-            }
-         } else if ( obj->IsA()->InheritsFrom( TDirectory::Class() ) ) {
-            // it's a subdirectory
-
-            cout << "Found subdirectory " << obj->GetName() << endl;
-            // create a new subdir of same name and title in the target file
-            target->cd();
-            TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() );
-
-            // newdir is now the starting point of another round of merging
-            // newdir still knows its depth within the target file via
-            // GetPath(), so we can still figure out where we are in the recursion
-            status = MergeRootfile( newdir, sourcelist);
-            if (status) return status;
-
-         } else if ( obj->InheritsFrom(TObject::Class())
-              && obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
-            // object implements Merge(TCollection*)
-
-            // loop over all source files and merge same-name object
-            TFile *nextsource = (TFile*)sourcelist->After( first_source );
-            while ( nextsource ) {
-               // make sure we are at the correct directory level by cd'ing to path
-               TDirectory *ndir = nextsource->GetDirectory(path);
-               if (ndir) {
-                  ndir->cd();
-                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
-                  if (key2) {
-                     TObject *hobj = key2->ReadObj();
-                     hobj->ResetBit(kMustCleanup);
-                     listH.Add(hobj);
-                     Int_t error = 0;
-                     obj->Execute("Merge", listHargs.Data(), &error);
-                     if (error) {
-                        cerr << "Error calling Merge() on " << obj->GetName()
-                             << " with the corresponding object in " << nextsource->GetName() << endl;
-                     }
-                     listH.Delete();
-                  }
-               }
-               nextsource = (TFile*)sourcelist->After( nextsource );
-            }
-         } else if ( obj->IsA()->InheritsFrom( THStack::Class() ) ) {
-            THStack *hstack1 = (THStack*) obj;
-            TList* l = new TList();
-
-            // loop over all source files and merge the histos of the
-            // corresponding THStacks with the one pointed to by "hstack1"
-            TFile *nextsource = (TFile*)sourcelist->After( first_source );
-            while ( nextsource ) {
-               // make sure we are at the correct directory level by cd'ing to path
-               TDirectory *ndir = nextsource->GetDirectory(path);
-               if (ndir) {
-                  ndir->cd();
-                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(hstack1->GetName());
-                  if (key2) {
-                    THStack *hstack2 = (THStack*) key2->ReadObj();
-                    l->Add(hstack2->GetHists()->Clone());
-                    delete hstack2;
-                  }
-               }
-
-               nextsource = (TFile*)sourcelist->After( nextsource );
-            }
-            hstack1->GetHists()->Merge(l);
-            l->Delete();
-         } else {
-            // object is of no type that we can merge
-            cout << "Cannot merge object type, name: "
-                 << obj->GetName() << " title: " << obj->GetTitle() << endl;
-
-            // loop over all source files and write similar objects directly to the output file
-            TFile *nextsource = (TFile*)sourcelist->After( first_source );
-            while ( nextsource ) {
-               // make sure we are at the correct directory level by cd'ing to path
-               TDirectory *ndir = nextsource->GetDirectory(path);
-               if (ndir) {
-                  ndir->cd();
-                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
-                  if (key2) {
-                     TObject *nobj = key2->ReadObj();
-                     nobj->ResetBit(kMustCleanup);
-                     int nbytes1 = target->WriteTObject(nobj, key2->GetName(), "SingleKey" );
-                     if (nbytes1 <= 0) status = -1;
-                     delete nobj;
-                  }
-               }
-               nextsource = (TFile*)sourcelist->After( nextsource );
-            }
-         }
-
-         // now write the merged histogram (which is "in" obj) to the target file
-         // note that this will just store obj in the current directory level,
-         // which is not persistent until the complete directory itself is stored
-         // by "target->Write()" below
-         target->cd();
-
-         //!!if the object is a tree, it is stored in globChain...
-         if(obj->IsA()->InheritsFrom( TDirectory::Class() )) {
-            //printf("cas d'une directory\n");
-         } else if(obj->IsA()->InheritsFrom( TTree::Class() )) {
-            if (!noTrees) {
-               globChain->ls("noaddr");
-               if (fastMethod && !reoptimize) globChain->Merge(target->GetFile(),0,"keep fast");
-               else                           globChain->Merge(target->GetFile(),0,"keep");
-               delete globChain;
-            }
-         } else {
-            int nbytes2 = obj->Write( key->GetName(), TObject::kSingleKey );
-            if (nbytes2 <= 0) status = -1;
-         }
-         oldkey = key;
-         delete obj;
-      } // while ( ( TKey *key = (TKey*)nextkey() ) )
-      first_source = (TDirectory*)sourcelist->After(first_source);
-   }
-   // save modifications to target file
-   target->SaveSelf(kTRUE);
-   return status;
 }
diff --git a/proof/proofplayer/inc/LinkDef.h b/proof/proofplayer/inc/LinkDef.h
index 369e5f9d7d1d8328acaf3f68d9d6d812e023bb5b..03fae20f07f72a30f144a665d939b581ca7541c2 100644
--- a/proof/proofplayer/inc/LinkDef.h
+++ b/proof/proofplayer/inc/LinkDef.h
@@ -42,7 +42,6 @@
 #pragma link C++ class TProofLimitsFinder;
 #pragma link C++ class TDrawFeedback+;
 #pragma link C++ class TStatus+;
-#pragma link C++ class TFileMerger+;
 #pragma link C++ class TProofOutputFile+;
 
 #pragma link C++ class TOutputListSelectorDataMap+;